Có thể có nhiều lợi thế cho mã như vậy, nhưng thật không may, Tiêu chuẩn C không được viết để tạo điều kiện thuận lợi cho nó. Các trình biên dịch trong lịch sử đã đưa ra các đảm bảo hành vi hiệu quả vượt quá những gì Tiêu chuẩn yêu cầu để có thể viết mã đó sạch hơn nhiều so với Tiêu chuẩn C, nhưng các trình biên dịch gần đây đã bắt đầu thu hồi các bảo đảm đó dưới tên tối ưu hóa.
Đáng chú ý nhất, nhiều trình biên dịch C đã được bảo đảm về mặt lịch sử (theo thiết kế nếu không phải là tài liệu) rằng nếu hai loại cấu trúc có cùng trình tự ban đầu, một con trỏ tới một trong hai loại có thể được sử dụng để truy cập các thành viên của chuỗi chung đó, ngay cả khi các loại không liên quan, và hơn nữa với mục đích thiết lập một chuỗi ban đầu chung, tất cả các con trỏ tới các cấu trúc là tương đương. Mã sử dụng hành vi đó có thể sạch hơn và an toàn hơn nhiều so với mã không, nhưng thật không may, mặc dù Tiêu chuẩn yêu cầu các cấu trúc chia sẻ một chuỗi ban đầu chung phải được trình bày theo cùng một cách, nhưng nó cấm mã thực sự sử dụng một con trỏ của một loại để truy cập vào chuỗi ban đầu của loại khác.
Do đó, nếu bạn muốn viết mã hướng đối tượng bằng C, bạn sẽ phải quyết định (và nên đưa ra quyết định này sớm) để vượt qua rất nhiều vòng để tuân theo quy tắc loại con trỏ của C và được chuẩn bị để có trình biên dịch hiện đại tạo mã vô nghĩa nếu một trình duyệt bị trượt, ngay cả khi trình biên dịch cũ hơn sẽ tạo mã hoạt động như dự định, hoặc tài liệu khác yêu cầu mã sẽ chỉ có thể sử dụng được với trình biên dịch được cấu hình để hỗ trợ hành vi con trỏ kiểu cũ (ví dụ: sử dụng một "-fno -rict-aliasing") Một số người coi "-fno -rict-aliasing" là xấu xa, nhưng tôi sẽ đề nghị rằng sẽ hữu ích hơn khi nghĩ về "-fno -rict-aliasing" C là một ngôn ngữ cung cấp sức mạnh ngữ nghĩa lớn hơn cho một số mục đích so với "tiêu chuẩn" C,nhưng với chi phí tối ưu hóa có thể quan trọng cho một số mục đích khác.
Ví dụ, trên các trình biên dịch truyền thống, các trình biên dịch lịch sử sẽ diễn giải đoạn mã sau:
struct pair { int i1,i2; };
struct trio { int i1,i2,i3; };
void hey(struct pair *p, struct trio *t)
{
p->i1++;
t->i1^=1;
p->i1--;
t->i1^=1;
}
khi thực hiện các bước sau theo thứ tự: tăng thành viên đầu tiên của *p
, bổ sung bit thấp nhất của thành viên đầu tiên *t
, sau đó giảm thành viên đầu tiên *p
và bổ sung bit thấp nhất của thành viên đầu tiên *t
. Các trình biên dịch hiện đại sẽ sắp xếp lại chuỗi các hoạt động theo kiểu mã nào sẽ hiệu quả hơn nếu p
và t
xác định các đối tượng khác nhau, nhưng sẽ thay đổi hành vi nếu chúng không hoạt động.
Ví dụ này tất nhiên là cố tình chiếm đoạt và trong mã thực tế sử dụng một con trỏ của một loại để truy cập các thành viên là một phần của chuỗi ban đầu chung của loại khác thường sẽ hoạt động, nhưng thật không may vì không biết khi nào mã đó có thể thất bại hoàn toàn không thể sử dụng nó một cách an toàn trừ khi vô hiệu hóa phân tích răng cưa dựa trên loại.
Một ví dụ ít gây tranh cãi sẽ xảy ra nếu người ta muốn viết một hàm để làm một cái gì đó như hoán đổi hai con trỏ thành các kiểu tùy ý. Trong phần lớn các trình biên dịch "thập niên 1990 C", điều đó có thể được thực hiện thông qua:
void swap_pointers(void **p1, void **p2)
{
void *temp = *p1;
*p1 = *p2;
*p2 = temp;
}
Tuy nhiên, trong Tiêu chuẩn C, người ta sẽ phải sử dụng:
#include "string.h"
#include "stdlib.h"
void swap_pointers2(void **p1, void **p2)
{
void **temp = malloc(sizeof (void*));
memcpy(temp, p1, sizeof (void*));
memcpy(p1, p2, sizeof (void*));
memcpy(p2, temp, sizeof (void*));
free(temp);
}
Nếu *p2
được giữ trong bộ lưu trữ được phân bổ và con trỏ tạm thời không được lưu trữ trong bộ lưu trữ được phân bổ, loại hiệu quả *p2
sẽ trở thành loại con trỏ tạm thời và mã cố gắng sử dụng *p2
như bất kỳ loại nào không khớp với con trỏ tạm thời loại sẽ gọi Hành vi không xác định. Chắc chắn là rất khó có khả năng trình biên dịch sẽ nhận thấy điều đó, nhưng vì triết lý trình biên dịch hiện đại yêu cầu các lập trình viên tránh Hành vi không xác định bằng mọi giá, tôi không thể nghĩ ra bất kỳ phương tiện an toàn nào khác để viết mã ở trên mà không sử dụng lưu trữ được phân bổ .