Khi nào Hành vi không xác định trong C nhảy rào cản nhân quả


8

Một số trình biên dịch C siêu hiện đại sẽ suy luận rằng nếu một chương trình sẽ gọi Hành vi không xác định khi được cung cấp một số đầu vào nhất định, thì các đầu vào đó sẽ không bao giờ được nhận. Do đó, bất kỳ mã nào không liên quan trừ khi nhận được đầu vào như vậy có thể bị loại bỏ.

Một ví dụ đơn giản, được đưa ra:

void foo(uint32_t);

uint32_t rotateleft(uint_t value, uint32_t amount)
{
  return (value << amount) | (value >> (32-amount));
}

uint32_t blah(uint32_t x, uint32_t y)
{
  if (y != 0) foo(y);
  return rotateleft(x,y);
}

một trình biên dịch có thể suy ra rằng vì việc đánh giá value >> (32-amount)sẽ mang lại Hành vi không xác định khi amountbằng 0, nên hàm blahsẽ không bao giờ được gọi ybằng 0; foodo đó, cuộc gọi đến có thể được thực hiện vô điều kiện.

Từ những gì tôi có thể nói, triết lý này dường như đã bị bắt giữ vào khoảng năm 2010. Bằng chứng sớm nhất tôi thấy về nguồn gốc của nó bắt đầu từ năm 2009, và nó đã được ghi nhận trong tiêu chuẩn C11, trong đó nêu rõ rằng nếu Hành vi không xác định xảy ra ở bất kỳ điểm trong quá trình thực thi chương trình, hành vi của toàn bộ chương trình trở nên không xác định.

Được quan điểm cho rằng trình biên dịch nên cố gắng sử dụng hành vi undefined để biện minh cho việc tối ưu ngược nhân quả (tức là hành vi undefined trong rotateleftchức năng nên gây ra trình biên dịch cho rằng blahphải được gọi với một tổ chức phi-zero y, hay không bất cứ điều gì sẽ không bao giờ gây ra yđể giữ một giá trị khác không) được ủng hộ nghiêm túc trước năm 2009? Khi nào một điều như vậy lần đầu tiên được đề xuất nghiêm túc như là một kỹ thuật tối ưu hóa?

[Phụ lục]

Một số trình biên dịch, ngay cả trong Thế kỷ 20, bao gồm các tùy chọn để cho phép các loại suy luận nhất định về các vòng lặp và các giá trị được tính toán trong đó. Ví dụ, được đưa ra

int i; int total=0;
for (i=n; i>=0; i--)
{
  doSomething();
  total += i*1000;
}

một trình biên dịch, ngay cả khi không có các suy luận tùy chọn, có thể viết lại thành:

int i; int total=0; int x1000;
for (i=n, x1000=n*1000; i>0; i--, x1000-=1000)
{
  doSomething();
  total += x1000;
}

vì hành vi của mã đó sẽ khớp chính xác với bản gốc, ngay cả khi trình biên dịch đã chỉ định rằng intcác giá trị luôn bao bọc theo kiểu bổ sung của hai mod-65536 . Tùy chọn suy luận bổ sung sẽ cho phép trình biên dịch nhận ra rằng vì ix1000nên vượt qua 0 cùng một lúc, biến trước đây có thể được loại bỏ:

int total=0; int x1000;
for (x1000=n*1000; x1000 > 0; x1000-=1000)
{
  doSomething();
  total += x1000;
}

Trên một hệ thống có intcác giá trị được bao bọc mod 65536, một nỗ lực để chạy một trong hai vòng đầu tiên nbằng 33 sẽ dẫn đến doSomething()việc được gọi 33 lần. Ngược lại, vòng lặp cuối cùng sẽ không gọi được doSomething(), mặc dù lần gọi đầu tiên doSomething()sẽ có trước bất kỳ tràn số học nào. Một hành vi như vậy có thể được coi là "phi nhân quả", nhưng các tác động bị hạn chế một cách hợp lý và có nhiều trường hợp hành vi đó sẽ vô hại (trong trường hợp một hàm được yêu cầu để mang lại một số giá trị khi được đưa vào bất kỳ đầu vào nào , nhưng giá trị có thể tùy ý nếu đầu vào không hợp lệ, có vòng lặp kết thúc nhanh hơn khi được cung cấp giá trị không hợp lệ lànsẽ thực sự có lợi). Hơn nữa, tài liệu trình biên dịch có xu hướng xin lỗi vì thực tế là nó sẽ thay đổi hành vi của bất kỳ chương trình nào - ngay cả những chương trình tham gia vào UB.

Tôi quan tâm đến khi thái độ của các nhà biên dịch thay đổi khỏi ý tưởng rằng các nền tảng nên khi tài liệu thực tế có một số hạn chế về hành vi có thể sử dụng được ngay cả trong trường hợp không được Tiêu chuẩn bắt buộc, với ý tưởng rằng mọi cấu trúc sẽ dựa trên bất kỳ hành vi nào không được ủy quyền bởi Tiêu chuẩn phải được gắn nhãn bất hợp pháp ngay cả khi trên hầu hết các trình biên dịch hiện có, nó sẽ hoạt động tốt hoặc tốt hơn bất kỳ mã tuân thủ nghiêm ngặt nào đáp ứng các yêu cầu tương tự (thường cho phép tối ưu hóa không thể thực hiện được trong mã tuân thủ nghiêm ngặt).


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
maple_shaft

1
@rwong: Tôi không thấy điều đó thậm chí vi phạm quy luật thời gian, ít nhân quả hơn nhiều. Nếu các phôi tĩnh bất hợp pháp chấm dứt thực thi chương trình (hầu như không phải là mô hình thực thi tối nghĩa) và nếu mô hình xây dựng sao cho không có lớp bổ sung nào có thể được xác định sau khi liên kết, thì không có đầu vào nào của chương trình có thể shape->Is2D()được gọi trên một đối tượng không xuất phát từ Shape2D. Có một sự khác biệt rất lớn giữa việc tối ưu hóa mã chỉ có liên quan nếu Hành vi không xác định quan trọng đã xảy ra so với mã chỉ có liên quan trong trường hợp ...
supercat

... không có đường dẫn thực thi xuôi dòng nào không gọi một số dạng Hành vi không xác định. Mặc dù định nghĩa về "hành vi không xác định quan trọng" được nêu trong (IIRC) Phụ lục L hơi mơ hồ, việc triển khai có thể thực hiện hợp lý việc truyền tĩnh và gửi ảo để mã được chỉ định sẽ nhảy đến một địa chỉ tùy ý (đó là UB quan trọng). Đối với nó để luôn luôn nhảy đến Shape2D::Is­2Dlà thực sự tốt hơn so với chương trình xứng đáng.
supercat

1
@rwong: Đối với bản sao, ý định của câu hỏi của tôi là hỏi khi nào trong lịch sử loài người, việc giải thích quy phạm về Hành vi không xác định liên quan đến ví dụ như dịch chuyển tràn từ "Nếu hành vi tự nhiên của nền tảng sẽ cho phép ứng dụng đáp ứng các yêu cầu của nó mà không cần kiểm tra lỗi , có yêu cầu tiêu chuẩn bất kỳ hành vi trái ngược nào cũng có thể làm tăng độ phức tạp của mã nguồn, mã thực thi và mã trình biên dịch cần thiết để có một chương trình đáp ứng các yêu cầu, vì vậy, thay vào đó, hãy để cho nền tảng làm những gì tốt nhất "để" Trình biên dịch nên giả sử các lập trình viên đó ...
supercat

1
... sẽ luôn bao gồm mã để ngăn tràn trong mọi trường hợp họ không muốn phóng tên lửa hạt nhân. "Trong lịch sử, đưa ra thông số kỹ thuật" viết một hàm, đưa ra hai đối số loại 'int', trả về sản phẩm nếu nó có thể đại diện là 'int', hoặc nếu không thì chấm dứt chương trình hoặc trả về một số tùy ý ", int prod(int x, int y) {return x*y;}sẽ có hiệu lực. Việc tuân thủ" không khởi động nukes "theo cách tuân thủ nghiêm ngặt, tuy nhiên, sẽ yêu cầu mã khó đọc hơn và hầu như sẽ đọc chắc chắn chạy chậm hơn nhiều trên nhiều nền tảng.
supercat

Câu trả lời:


4

Hành vi không xác định được sử dụng trong các tình huống không thể xác định cụ thể để chỉ định hành vi và nó luôn được viết để cho phép hoàn toàn mọi hành vi có thể.

Các quy tắc cực kỳ lỏng lẻo cho UB rất hữu ích khi bạn nghĩ về những gì một trình biên dịch tuân thủ thông số kỹ thuật phải trải qua. Bạn có thể có đủ mã hóa biên dịch để phát ra lỗi khi bạn thực hiện một số UB xấu trong một trường hợp, nhưng thêm một vài lớp đệ quy và bây giờ điều tốt nhất bạn có thể làm là một cảnh báo. Thông số kỹ thuật không có khái niệm về "cảnh báo", vì vậy nếu thông số kỹ thuật đã đưa ra một hành vi, nó sẽ phải là "một lỗi".

Lý do chúng ta thấy ngày càng nhiều tác dụng phụ của việc này là sự thúc đẩy để tối ưu hóa. Viết một spec tối ưu hóa phù hợp là khó. Viết một trình tối ưu hóa phù hợp với thông số kỹ thuật cũng có thể thực hiện một công việc tốt đáng kể để đoán những gì bạn dự định khi bạn đi ra ngoài thông số kỹ thuật là tàn bạo. Trình biên dịch sẽ dễ dàng hơn nhiều nếu họ giả sử UB có nghĩa là UB.

Điều này đặc biệt đúng với gcc, cố gắng hỗ trợ nhiều tập lệnh với cùng một trình biên dịch. Việc để UB mang lại các hành vi UB dễ dàng hơn nhiều so với việc cố gắng vật lộn với tất cả các cách mà mọi mã UB đơn lẻ có thể sai trên mọi nền tảng và đưa nó vào các cụm từ đầu của trình tối ưu hóa.


1
Lý do C nổi tiếng về tốc độ là vì các lập trình viên biết rằng ngay cả trong một số trường hợp Tiêu chuẩn áp đặt không có yêu cầu nào, hành vi của nền tảng của họ sẽ thỏa mãn yêu cầu của họ có thể sử dụng hành vi đó. Nếu một nền tảng đảm bảo x-y > zsẽ tùy ý mang lại 0 hoặc 1 khi x-ykhông thể biểu thị là "int", thì một nền tảng như vậy sẽ có nhiều cơ hội tối ưu hóa hơn một nền tảng yêu cầu biểu thức được viết dưới dạng UINT_MAX/2+1+x+y > UINT_MAX/2+1+zhoặc (long long)x+y > z.
supercat

1
Trước khoảng năm 2009, một cách giải thích điển hình của nhiều dạng UB sẽ là ví dụ: "Ngay cả khi thực hiện lệnh shift-right-by-N trên CPU mục tiêu có thể làm tắc nghẽn xe buýt trong vài phút khi bị gián đoạn khi N là -1 [I 'Đã nghe nói về CPU như vậy], trình biên dịch có thể chuyển bất kỳ N nào sang lệnh shift-right-by-N mà không cần xác thực hoặc che dấu N với 31 hoặc tùy ý chọn trong số các tùy chọn đó ". Trong trường hợp bất kỳ hành vi nào ở trên sẽ đáp ứng yêu cầu, lập trình viên không cần lãng phí thời gian (của họ hoặc của máy) để ngăn chặn chúng xảy ra. Điều gì đã thúc đẩy sự thay đổi?
supercat

@supercat Tôi sẽ nói rằng, ngay cả trong năm 2015, một cách giải thích "điển hình" của UB cũng sẽ tương tự lành tính. Vấn đề không phải là trường hợp điển hình của UB, đó là những trường hợp bất thường. Tôi muốn nói 95-98% UB tôi đã vô tình viết "làm việc như dự định." Đó là 2 - 5% còn lại mà bạn không thể hiểu tại sao UB lại vượt quá tầm tay cho đến khi bạn mất 3 năm để đào sâu trong ruột của trình tối ưu hóa gcc để nhận ra rằng nó thực sự giống như một trường hợp kỳ quặc như họ nghĩ ... ban đầu nó trông có vẻ dễ dàng.
Cort Ammon

Một ví dụ tuyệt vời mà tôi gặp phải là vi phạm Quy tắc Một Định nghĩa (ODR) mà tôi đã viết một cách tình cờ, đặt tên hai lớp giống nhau và bị lỗi nghiêm trọng khi tôi gọi một hàm và nó chạy hàm kia. Có vẻ hợp lý để nói "tại sao bạn không sử dụng tên lớp gần nhất hoặc ít nhất là cảnh báo tôi" cho đến khi bạn hiểu cách trình liên kết giải quyết các vấn đề đặt tên. Đơn giản là không có tùy chọn hành vi "tốt" nào có sẵn trừ khi họ đưa một lượng lớn mã bổ sung vào trình biên dịch để bắt nó.
Cort Ammon

Công nghệ liên kết nói chung là khủng khiếp, nhưng có những rào cản đáng kể về gà và trứng để cải thiện nó. Mặt khác, điều tôi nghĩ là cần thiết cho C để chuẩn hóa một tập hợp các typedefs và macro tối ưu hóa tính di động để các trình biên dịch hiện có hỗ trợ các hành vi ngụ ý có thể hỗ trợ tiêu chuẩn mới chỉ bằng cách thêm tệp tiêu đề và mã muốn sử dụng mới các tính năng có thể được sử dụng với các trình biên dịch cũ hơn chỉ hỗ trợ các hành vi bằng cách bao gồm tệp tiêu đề "tương thích", nhưng trình biên dịch sau đó có thể đạt được tối ưu hóa tốt hơn nhiều ...
supercat

4

"Hành vi không xác định có thể khiến trình biên dịch viết lại mã" đã xảy ra trong một thời gian dài, trong tối ưu hóa vòng lặp.

Lấy một vòng lặp (ví dụ a và b là con trỏ để nhân đôi)

for (i = 0; i < n; ++i) a [i] = b [i];

Chúng tôi tăng một int, chúng tôi sao chép một phần tử mảng, chúng tôi so sánh với một giới hạn. Trình biên dịch tối ưu hóa trước tiên loại bỏ việc lập chỉ mục:

double* tmp1 = a;
double* tmp2 = b;
for (i = 0; i < n; ++i) *tmp1++ = *tmp2++;

Chúng tôi xóa trường hợp n <= 0:

i = 0;
if (n > 0) {
    double* tmp1 = a;
    double* tmp2 = b;
    for (; i < n; ++i) *tmp1++ = *tmp2++;
}

Bây giờ chúng ta loại bỏ biến i:

i = 0;
if (n > 0) {
    double* tmp1 = a;
    double* tmp2 = b;
    double* limit = tmp1 + n;
    for (; tmp1 != limit; tmp1++, tmp2++) *tmp1 = *tmp2;
    i = n;
}

Bây giờ nếu n = 2 ^ 29 trên hệ thống 32 bit hoặc 2 ^ 61 trên hệ thống 64 bit, trên các triển khai điển hình, chúng tôi sẽ có giới hạn tmp1 == và không có mã nào được thực thi. Bây giờ thay thế bài tập bằng một cái gì đó mất nhiều thời gian để mã gốc sẽ không bao giờ gặp sự cố không thể tránh khỏi vì mất quá nhiều thời gian và trình biên dịch đã thay đổi mã.


Nếu không con trỏ nào không ổn định, tôi sẽ không xem xét việc viết lại mã. Các trình biên dịch trong một thời gian dài đã được phép ghi lại các phần tử volatilecon trỏ, do đó, hành vi trong trường hợp nlớn đến mức các con trỏ sẽ bao bọc sẽ tương đương với việc lưu trữ ngoài giới hạn một vị trí lưu trữ tạm thời itrước bất kỳ thứ gì khác xảy ra. Nếu ahoặc bkhông ổn định, nền tảng đã ghi lại rằng các truy cập dễ bay hơi tạo ra các hoạt động tải / lưu trữ vật lý theo trình tự được yêu cầu và nền tảng xác định bất kỳ phương tiện nào thông qua đó các yêu cầu đó ...
supercat

... chắc chắn có thể ảnh hưởng đến việc thực hiện chương trình, sau đó tôi sẽ lập luận rằng một trình biên dịch nên kiềm chế các tối ưu hóa được chỉ định (mặc dù tôi sẽ dễ dàng thừa nhận rằng một số có thể không được lập trình để ngăn chặn các tối ưu hóa đó trừ khi icũng bị biến động). Đó sẽ là một trường hợp góc hành vi khá hiếm, mặc dù. Nếu abkhông dễ bay hơi, tôi đề nghị rằng sẽ không có ý nghĩa dự định hợp lý cho những gì mã nên làm nếu nlớn đến mức ghi đè lên tất cả bộ nhớ. Ngược lại, nhiều hình thức khác của UB có ý nghĩa dự định hợp lý.
supercat

Khi xem xét thêm, tôi có thể nghĩ đến các tình huống trong đó các giả định về quy luật số học số nguyên có thể khiến các vòng lặp chấm dứt sớm trước khi bất kỳ UB nào xảy ra và không có vòng lặp thực hiện bất kỳ tính toán địa chỉ bất hợp pháp nào. Trong FORTRAN, có sự phân biệt rõ ràng giữa các biến điều khiển vòng lặp và các biến khác, nhưng C thiếu sự khác biệt đó, điều không may vì yêu cầu các lập trình viên luôn ngăn chặn tràn các biến điều khiển vòng lặp sẽ ít gây trở ngại cho mã hóa hiệu quả hơn là phải ngăn chặn tràn bất cứ nơi nào
supercat

Tôi tự hỏi làm thế nào các quy tắc ngôn ngữ có thể được viết tốt nhất để mã có thể hài lòng với một loạt các hành vi trong trường hợp tràn có thể nói rằng, mà không cản trở tối ưu hóa thực sự hữu ích? Có nhiều trường hợp mã phải tạo đầu ra được xác định nghiêm ngặt cho đầu vào hợp lệ, nhưng đầu ra được xác định lỏng lẻo khi đưa ra đầu vào không hợp lệ. Ví dụ, giả sử một lập trình viên có khuynh hướng viết if (x-y>z) do_something(); 'không quan tâm liệu do_somethingthực thi trong trường hợp tràn, với điều kiện tràn không có tác dụng nào khác. Có cách nào để viết lại phần trên không ...
supercat

... Tạo mã không cần thiết không hiệu quả trên ít nhất một số nền tảng (so với những nền tảng đó có thể tạo ra nếu được lựa chọn miễn phí về việc có nên gọi hay không do_something)? Ngay cả khi tối ưu hóa vòng lặp bị cấm mang lại hành vi không phù hợp với mô hình tràn lỏng, các lập trình viên có thể viết mã theo cách để cho phép trình biên dịch tạo mã tối ưu. Có cách nào để khắc phục sự thiếu hiệu quả bắt buộc theo mô hình "tránh tràn bằng mọi giá" không?
supercat

3

Nó luôn luôn là trường hợp trong C và C ++ là kết quả của hành vi không xác định, bất cứ điều gì có thể xảy ra. Do đó, luôn có trường hợp trình biên dịch có thể đưa ra giả định rằng mã của bạn không gọi hành vi không xác định: Hoặc không có hành vi không xác định trong mã của bạn, thì giả định đó là chính xác. Hoặc có hành vi không xác định trong mã của bạn, sau đó bất cứ điều gì xảy ra do giả định không chính xác được bao phủ bởi " bất cứ điều gì cũng có thể xảy ra".

Nếu bạn nhìn vào tính năng "hạn chế" trong C, toàn bộ điểm của tính năng là trình biên dịch có thể cho rằng không có hành vi không xác định, vì vậy chúng tôi đã đạt đến điểm mà trình biên dịch không chỉ có thể mà thực sự nên cho rằng không có không xác định hành vi.

Trong ví dụ mà bạn đưa ra, các hướng dẫn trình biên dịch thường được sử dụng trên các máy tính dựa trên x86 để thực hiện dịch chuyển trái hoặc phải sẽ thay đổi 0 bit nếu số lượng dịch chuyển là 32 cho mã 32 bit hoặc 64 cho mã 64 bit. Điều này trong hầu hết các trường hợp thực tế sẽ dẫn đến kết quả không mong muốn (ví dụ, và kết quả không giống như trên ARM hoặc PowerPC), vì vậy trình biên dịch hoàn toàn hợp lý khi cho rằng loại hành vi không xác định này không xảy ra. Bạn có thể thay đổi mã của bạn thành

uint32_t rotateleft(uint_t value, uint32_t amount)
{
   return amount == 0 ? value : (value << amount) | (value >> (32-amount));
}

và đề xuất với các nhà phát triển gcc hoặc Clang rằng trên hầu hết các bộ xử lý, mã "số tiền == 0" sẽ bị xóa bởi trình biên dịch, bởi vì mã trình biên dịch được tạo cho mã dịch chuyển sẽ tạo ra kết quả tương tự như giá trị khi số tiền == 0.


1
Tôi có thể nghĩ về rất ít nền tảng trong đó việc triển khai x>>y[không dấu x] sẽ hoạt động khi biến ygiữ bất kỳ giá trị nào từ 0 đến 31 và đã làm một cái gì đó ngoài năng suất 0 hoặc x>>(y & 31)cho các giá trị khác, có thể hiệu quả như một cách làm khác ; Tôi biết không có nền tảng nào đảm bảo rằng không có hành động nào ngoài một trong những điều trên xảy ra sẽ làm tăng thêm chi phí đáng kể. Ý tưởng rằng các lập trình viên nên sử dụng một số công thức phức tạp hơn trong mã mà sẽ không bao giờ phải chạy trên các máy tối nghĩa sẽ được xem là vô lý.
supercat

3
Câu hỏi của tôi là khi nào mọi thứ chuyển từ "x >> 32` có thể tùy ý mang lại xhoặc 0, hoặc có thể bẫy trên một số nền tảng tối nghĩa" thành " x>>32có thể khiến trình biên dịch viết lại ý nghĩa của mã khác"? Bằng chứng sớm nhất tôi có thể tìm thấy là từ năm 2009, nhưng tôi tò mò liệu có bằng chứng sớm hơn không.
supercat

@Ded repeatator: Sự thay thế của tôi hoạt động hoàn toàn tốt đối với các giá trị có ý nghĩa, đó là 0 <= lượng <32. Và liệu (giá trị >> 32 - số tiền) của bạn có chính xác hay không, tôi không quan tâm, nhưng bỏ dấu ngoặc ra là xấu như một lỗi.
gnasher729

Không thấy hạn chế 0<=amount && amount<32. Liệu giá trị lớn hơn / nhỏ hơn có ý nghĩa? Tôi nghĩ liệu họ làm là một phần của câu hỏi. Và không sử dụng dấu ngoặc đơn khi đối mặt với bit-op có lẽ là một ý tưởng tồi, chắc chắn, nhưng chắc chắn không phải là một lỗi.
Ded repeatator

@Ded repeatator điều này là do một số CPU thực hiện toán tử shift như (y mod 32)đối với 32 bit x(y mod 64)64 bit x. Lưu ý rằng việc phát mã tương đối dễ dàng sẽ đạt được hành vi thống nhất trên tất cả các kiến ​​trúc CPU - bằng cách che giấu lượng dịch chuyển. Điều này thường mất một hướng dẫn thêm. Nhưng than ôi ...
rwong

-1

Điều này là do có một lỗi trong mã của bạn:

uint32_t blah(uint32_t x, uint32_t y)
{
    if (y != 0) 
    {
        foo(y);
        return x; ////// missing return here //////
    }
    return rotateleft(x, y);
}

Nói cách khác, nó chỉ nhảy rào cản nhân quả nếu trình biên dịch thấy rằng, với một số đầu vào nhất định, bạn đang gọi hành vi không xác định vượt quá nghi ngờ .

Bằng cách quay lại ngay trước khi gọi hành vi không xác định, bạn nói với trình biên dịch rằng bạn có ý thức ngăn chặn hành vi không xác định đó thực thi và trình biên dịch thừa nhận điều đó.

Nói cách khác, khi bạn có một trình biên dịch cố gắng thực thi đặc tả theo một cách rất nghiêm ngặt, bạn phải thực hiện mọi xác thực đối số có thể có trong mã của mình. Hơn nữa, các xác nhận này phải xảy ra trước khi gọi hành vi không xác định nói trên.

Chờ đợi! Và còn nhiều hơn thế!

Bây giờ, với các trình biên dịch thực hiện những điều siêu điên rồ nhưng siêu logic này, bạn bắt buộc phải nói với trình biên dịch rằng một hàm không được phép tiếp tục thực thi. Vì vậy, noreturntừ khóa trên foo()chức năng bây giờ trở thành bắt buộc .


4
Xin lỗi, nhưng đó không phải là câu hỏi.
Ded repeatator

@Ded repeatator Nếu đó không phải là câu hỏi, câu hỏi này đáng lẽ phải được đóng lại vì dựa trên quan điểm (tranh luận mở). Thực tế, đó là phỏng đoán thứ hai động lực của các ủy ban C và C ++ và của nhà cung cấp / nhà văn biên dịch.
rwong

1
Đó là một câu hỏi về giải thích lịch sử, sử dụng và lý do của UB trong ngôn ngữ C, không giới hạn trong tiêu chuẩn và ủy ban. Đoán thứ hai các ủy ban? Không phải vậy.
Ded repeatator

1
... Nhận ra rằng sau này chỉ nên thực hiện một hướng dẫn. Tôi không biết những người theo chủ nghĩa siêu hiện đại đang cố gắng làm cho C cạnh tranh trong các lĩnh vực mà nó bị thay thế, nhưng họ đang làm suy yếu tiện ích của nó trong lĩnh vực duy nhất nơi nó là vua, và từ những gì tôi có thể nói làm cho việc tối ưu hóa hữu ích trở nên khó khăn hơn thay vì dễ dàng hơn.
supercat

1
@ago: Trên thực tế, trên một hệ thống có 24 bit int, lần đầu tiên sẽ thành công và lần thứ hai sẽ thất bại. Trong các hệ thống nhúng, bất cứ điều gì đi. Hoặc rất nhiều đi; tôi có thể nhớ một trong đó int = 32 bit, và long = 40 bit, đó là hệ thống duy nhất tôi từng thấy ở đó dài hơn rất ít hiệu quả hơn int32_t.
gnasher729
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.