Con trỏ trỏ tới một phần tử?

có nha, nó là undefined behavior =] C style cast (A*) kiểu đó ở đây tương đương với reinterpret_cast, mà reinterpret_cast chỉ cast được object thành (unsigned) char* thôi, chiều ngược lại chỉ đúng khi (u)char* đó được cast từ A nào đó :V

vd ở cái video này nói thẳng UB nè :V https://www.youtube.com/watch?v=o3j6hfXDCVc

https://en.cppreference.com/w/cpp/language/reinterpret_cast nói muốn cast từ A* thành B*

  1. Any object pointer type T1* can be converted to another object pointer type cv T2* . This is exactly equivalent to static_cast<cv T2*>(static_cast<cv void*>(expression)) (which implies that if T2 's alignment requirement is not stricter than T1 's, the value of the pointer does not change and conversion of the resulting pointer back to its original type yields the original value). In any case, the resulting pointer may only be dereferenced safely if allowed by the type aliasing rules (see below)

thì phải tuân theo type aliasing :V

Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType , the behavior is undefined unless one of the following is true:

  • AliasedType and DynamicType are similar .
  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.
  • AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.

nghĩa là chỉ đúng từ chiều T* sang (u)char* thôi :V chiều ngược lại chỉ đúng khi (u)char* đó là con trỏ được cast từ T* trước đó:

  1. A pointer to member object of some class T1 can be converted to a pointer to another member object of another class T2 . If T2 's alignment is not stricter than T1 's, conversion back to the original type T1 yields the original value, otherwise the resulting pointer cannot be used safely.

cách làm đúng phải là memcpy đống bytes đó vào A a; :joy:

hoặc khi read mấy bytes đó read 4/8 byte length trước, lặp vòng for length-- rồi tạo A a; và read thẳng vào (byte*)&a (cũng là 1 kiểu của memcpy :V)

hoặc bảo đảm alignment của receivedData đúng với alignment của A (hơi khó vì có thêm 4 bytes length ở đằng trước, nhưng nếu alignof(A) < 4 thì dễ), rồi gọi placement new cho từng object a trong mảng length object A đó :V

7 Likes

Cái này mình đã giải thích là dùng cho binary serialization, Khi đó binary data được cast từ A có trước.
Phần code này là deserialize. (Data format được quy định chặt chẽ).

Mình chỉ phản bác lại ý kiến này thôi.

Chỉ có thành phần thích thể hiện, hoặc là không biết đang viết gì mới dùng tới cách *(a+i) thôi bạn

Chủ quan duy ý chí.

2 Likes

cast hay là read từ 1 file hay 1 nguồn nào đó ai biết được :V Nhưng mà bỏ vào cái mảng uint_8 kia rồi reinterpret_cast thành A* trên nó là sai rồi :V

ý của 11) là

A a;
uint8_t* p = reinterpret_cast<uint8_t*>(&a); //ok
A* pa = reinterpret_cast<A*>(p); //hợp lệ vì p đang trỏ tới a hợp lệ

thì p đó trỏ tới a chứ ko phải là 1 cái mảng uint8_t buffer[255] như trên :V Nếu đọc từ nguồn nào đó ko đọc từng byte được vì nó chậm mà đọc 1 lúc 255 byte chẳng hạn thì coi như đó là buffer, phải memcpy vào A a; sau:

uint8_t buffer[255];
//read buffer...

int length;
memcpy(&length, buffer, sizeof(length));
std::cout << "length = " << length << "\n";

for (uint8_t* pbuf = buffer + sizeof(length); length--; pbuf += sizeof(A)) {
    A a;
    memcpy(&a, pbuf, sizeof(a));
    std::cout << a.w << " " << a.h << "\n";
}
3 Likes

Vì để tránh misalignment :slight_smile:

5 Likes

Thanks @tntxtnt, tới nay mới biết cái random iter kia có support cả kiểu viết it[n] đấy.

Có điều, trong thư viện STL thì không bàn được, vì nó generic, nên có khi là iterator, có khi là pointer, nên dùng một kiểu viết *(it+n) cho tất cả. Ở chỗ này chỉ có nói tới plain array với raw pointer mà thôi.

Còn bạn @LocNguyenXuan99 ấy, đưa ra chứng cứ mà bị phản bác, thì lại không đưa ra được chứng cứ tiếp, lại cứ đi công kích cá nhân như vầy thì không thích hợp để tranh luận tiếp đâu bạn ơi :yum:

5 Likes

Chuẩn. *(p+i) không được recommend bằng p[i] vì Misra-C đề cao tính đơn giản, dễ hiểu.

Đọc nhiều “ngôn tềnh” quá à bác :joy: Đây em có 1 ví dụ.

typedef struct
{
    unsigned char no0:1;
...
    unsigned char no15:1;
} __bitf_T2;
#define MDBL         ( * (volatile __near unsigned short * )0xFFF6)
#define MULOL        ( * (volatile __near unsigned short * )0xFFF6)
#define PMC          ( * (volatile __near unsigned char  * )0xFFFE)
#define PMC_bit      ( * (volatile __near __bitf_T *)0xFFFE)
2 Likes

À, là mình lười nên viết tắt á, casting ở đây là casting trực tiếp từ serializedData/transferedData á bạn. Chứ không phải là casting chung chung. Lý do tại sao thì bạn coi lại mấy post bên trên, có alignment và endian là 2 issue dễ thấy nhất.

Nhưng mà nhìn kỹ chút, chỗ này tại sao bạn không dùng type punning mà lại dùng macro?

3 Likes

Chưa hiểu ý bác lắm :thinking:

1 Like

Mấy bạn đưa chủ đề đi hơi xa
Mình không muốn nói về việc cast như thế nào cho đúng hay cách dùng *(p+i) chuẩn.
Mình thấy thái độ coi thường style code của người khác nên góp ý thôi

Chỉ có thành phần thích thể hiện, hoặc là không biết đang viết gì mới dùng tới cách *(a+i) thôi bạn

Rất nhiều người dùng style *(a+i), chẳng lẽ họ đều thể hiện hoặc là không biết đang viết gì?
Mình không biết nhiều như mấy bạn, chỉ đủ để không coi code người khác toàn là siêu lởm, viết như tâm thần

4 Likes

Coding style là quy tắc thống nhất giữa các thành viên trong team mà.
Team không thích dùng *(p+i) thì không dùng. Cơ mà số không thích dùng lại chiếm đa số. :V

4 Likes

Mình là mình vẫn thường dùng như vậy nhé, vì có 2 lý do chính sau. :stuck_out_tongue:

  1. Để nhòe thầy giáo
  2. Để cho bọn xin code cop y nguyên

Với lại mình code C, nên hay xài thế này:

int n = 0;
scanf("%d", &n);

int *a = malloc(n * sizeof(int));

for (int i = 0; i < n; ++i) scanf("%d", a + i);

Rõ là gõ a + i nhanh hơn &a[i]. :slightly_smiling_face: :slightly_smiling_face:

3 Likes

nó tâm thần thiệt mà, ai khùng mà đi viết a[i][j] theo kiểu *(*(a+i)+j) bao giờ. Còn siêu lởm chứ ko phải đơn giản là lởm là vì nó xài VLA trong code C++, rất ư là trigger.

bản thân nó cũng tự nhận là code lởm, ko valid đây: https://www.geeksforgeeks.org/variable-length-arrays-in-c-and-c/

But C++ standard (till C++11) doesn’t support variable sized arrays. The C++11 standard mentions array size as a constant-expression See (See 8.3.4 on page 179 of N3337). So the above program may not be a valid C++ program. The program may work in GCC compiler, because GCC compiler provides an extension to support them.

VLA chưa bao giờ được C++ chấp nhận, vậy mà câu đầu tiên nó ghi “till C++11” rất passive agressive. Nó cũng cực kì tởm lợm khi ko quên bồi thêm câu ra vẻ có nghiên cứu này nọ (mà ko tới nơi tới chốn)

As a side note, the latest C++14 (See 8.3.4 on page 184 of N3690) mentions array size as a simple expression (not constant-expression).

quote cái N3690 kia hình như là draft thôi, ko được đưa vào C++ chính thức. Vậy mà ko sửa ko update gì hết, làm cho nhiều người tưởng C++11/14 hỗ trợ VLA: http://www.cplusplus.com/forum/general/204173/#msg969246, người khác phải mất công đọc lại doc:

I thought the claim that VLAs are legal was dubious, so I checked the standard myself. Perhaps that was a typo, or the proposed feature was dropped.
[…]Consistent with my citation, the array bound must be a converted constant expression.

https://en.cppreference.com/w/cpp/language/array

expr - an integral constant expression (until C++14)a converted constant expression of type std::size_t (since C++14), which evaluates to a value greater than zero

tất cả đều phải là constant expression, ko có vụ int n rồi int a[n] thế kia được. Thế mới nói code nó siêu tởm lợm. Code viết cho newbie đọc lại càng phải cẩn thận hơn chứ ko cẩu thả thế kia được. Newbie đem code này qua MSVC 2015+ chạy nó báo lỗi ko biết lỗi gì rồi hỏi đi hỏi lại. Thế khác gì bắt người khác dọn rác của mình viết ra, mình chửi code siêu lởm chắc còn nhẹ, chắc phải chửi là code rác rưởi.

có 1 cách viết mảng động rất đơn giản cho C++ là vector<int> a(n); nó lại ko viết mà thích viết int a[n] thế kia. Nó thích code dài *(*(a + i) + j) vậy mà lại ko đi xài vector<int> a(n), rất ngoan cố. Web nó phổ biến, code bài nào cũng có, rất đáng khen. Nó có thể tập cho newbie thói quen tốt bằng cách xài vector nhưng ko, nó thích xài vla ko valid cơ.

trường hợp mảng 2 chiều thì vector<vector<int>> a(m, vector<int>(n)) rất xấu, rất tởm lợm, nhưng ít ra nó valid. int a[m][n] tuy đẹp nhưng ko phải là code C++.

5 Likes

Mình thầy bình luận của bạn sherly1001 rất chuẩn, và có đọc qua hết các bình luận thì mọi người cũng giải thích cũng khá chi tiết đấy.

mình chỉ giải thích thêm tại sao lại có thể như viết bạn @sherly1001 là vì bản chất i[a] = i[p] nó là bước nhảy của phần tử mảng nó tương đương với *(i+a) = *(a+i), cái kia tương tự, bạn nên tích solution cho người xứng đáng nhé

4 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?