Khi biên dịch, C loại bỏ [1] hầu hết thông tin loại, chỉ để lại bù trừ. Vì vậy, chức năng của bạn sẽ biên dịch một cái gì đó như thế này, trong giả:
changeCount:
assign *(*(stack_ptr + offset_element) + offset_Count) + 1
to *(stack_ptr + offset_element) + offset_Count;
assign 1 to return_value;
pop
Các stack_ptr là vị trí của stack frame được tạo ra khi bạn gọi changeCount, các offset_element là vị trí của đối số yếu tố, liên quan đến stack_ptr, nhưng offset_Count là gì? Hãy nhớ, tất cả trình biên dịch biết về mã của bạn chỉ là những gì bạn đã thể hiện trong ví dụ của mình; yếu tố là một con trỏ chung, không thực sự là một con trỏ đến bất cứ điều gì. Bạn sẽ phải nói với trình biên dịch những gì phần tử được trỏ đến, bằng cách đúc hoặc gán nó vào một biến [2]:
typedef struct { int Count; } counted_t;
int changeCount(void* element)
{
counted_t* counted = element;
counted.Count++;
return 1;
}
Chức năng này sẽ tạo cơ bản (giả) cùng một mã như trên, nhưng trình biên dịch hiện nay biết những gì bù đắp của Count nên được.
Bạn đề cập rằng có ba khả năng cho loại phần tử nào trỏ đến. Có một vài cách để xử lý điều đó: hoặc là một công đoàn phân biệt hoặc một cấu trúc "được kế thừa". Đối với một sử dụng công đoàn phân biệt, nói, một cấu trúc với một phần tử là một enum xác định cái nào của ba khả năng và một phần tử khác là một liên minh của ba cấu trúc có thể; đây là những gì ngôn ngữ ML (OCaml, Haskell, v.v.)) gọi một loại dữ liệu đại số hoặc những gì một công đoàn là trong Pascal. Đối với "thừa kế", bạn có thể sử dụng định nghĩa kiểu:
typedef struct { counted_t counted; int i; } counted_int_t;
typedef struct { counted_t counted; double d; } counted_double_t;
typedef struct { counted_t counted; char* s; } counted_charptr_t;
Trong trường hợp này, bạn có thể sử dụng chức năng changeCount ở trên và vượt qua trong một con trỏ đến một counted_int_t, counted_double_t, hoặc counted_charptr_t. Điều gì xảy ra là trình biên dịch sẽ bố trí ba cấu trúc với phần tử Đếm trong cấu trúc "hậu duệ" ở cùng một vị trí miễn là phần tử count_t là trước tiên. (Ít nhất, trong mọi trình biên dịch tôi đã từng sử dụng và trong mỗi đoạn mã tôi đã thấy. Tôi nghĩ điều này đã làm cho nó thành tiêu chuẩn C tại một số điểm, nhưng nó là một thành ngữ rất bình thường.)
[1] Ngoại trừ thông tin gỡ lỗi, nếu bạn đã yêu cầu trình biên dịch phát ra nó. Tuy nhiên, chương trình của bạn sẽ không có quyền truy cập vào thông tin đó, do đó, nó sẽ không giúp ích trong trường hợp này.
[2] Thao tác x ++ (postincrement) tăng biến (tốt, lvalue) mà nó được áp dụng; việc chuyển nhượng trong mã ban đầu là không cần thiết.
Tại sao không chỉ viết 'element.Count ++' thay vì gọi hàm? – LnxPrgr3
Không liên quan trực tiếp đến câu hỏi, nhưng hành vi của 'element.Count = element.Count ++;' là không xác định. Vì mục đích đơn giản, tôi sẽ tuyên bố quy tắc như thế này: bạn không thể sửa đổi một giá trị nhiều hơn một lần trong cùng một biểu thức. Để biết chi tiết đầy đủ, hãy xem "Biểu thức 6.5" trong tiêu chuẩn C. –