Thứ tự đánh giá các chỉ số mảng (so với biểu thức) trong C


47

Nhìn vào mã này:

static int global_var = 0;

int update_three(int val)
{
    global_var = val;
    return 3;
}

int main()
{
    int arr[5];
    arr[global_var] = update_three(2);
}

Những mảng nào được cập nhật? 0 hay 2?

Có một phần trong đặc tả của C chỉ ra mức độ ưu tiên của hoạt động trong trường hợp cụ thể này không?


21
Điều này có mùi của hành vi không xác định. Đó chắc chắn là thứ không bao giờ được mã hóa có chủ đích.
Fiddling Bits

1
Tôi đồng ý nó là một ví dụ về mã hóa xấu.
Jiminion

4
Một số kết quả giai thoại: godbolt.org/z/hM2Jo2
Bob__

15
Điều này không có gì để làm với các chỉ số mảng hoặc thứ tự các hoạt động. Nó phải làm với cái mà đặc tả C gọi là "điểm chuỗi" và đặc biệt, thực tế là các biểu thức gán không tạo ra một điểm thứ tự giữa biểu thức bên trái và bên phải, vì vậy trình biên dịch có thể tự do làm như vậy chọn.
Lee Daniel Crocker

4
Bạn nên báo cáo một yêu cầu tính năng để clangđoạn mã này kích hoạt cảnh báo IMHO.
malat

Câu trả lời:


51

Thứ tự các toán tử trái và phải

Để thực hiện gán trong arr[global_var] = update_three(2), việc thực hiện C phải đánh giá các toán hạng và, như một hiệu ứng phụ, cập nhật giá trị được lưu trữ của toán hạng bên trái. C 2018 6.5.16 (nói về bài tập) đoạn 3 cho chúng ta biết không có trình tự trong các toán hạng bên trái và bên phải:

Các đánh giá của toán hạng là không có kết quả.

Điều này có nghĩa là việc triển khai C được tự do tính toán giá trị arr[global_var] trước (bằng cách tính toán giá trị, chúng tôi có nghĩa là tìm ra biểu thức này đề cập đến điều gì), sau đó để đánh giá update_three(2)và cuối cùng là gán giá trị của giá trị sau cho giá trị trước; hoặc để đánh giá update_three(2)trước, sau đó tính toán giá trị, sau đó gán giá trị trước cho giá trị sau; hoặc để đánh giá giá trị và update_three(2)theo một số cách kết hợp và sau đó gán giá trị bên phải cho giá trị bên trái.

Trong mọi trường hợp, việc gán giá trị cho giá trị phải đến sau cùng, bởi vì 6.5.16 3 cũng cho biết:

Tác dụng phụ của việc cập nhật giá trị được lưu trữ của toán hạng bên trái được sắp xếp theo trình tự sau khi tính toán giá trị của toán hạng bên trái và bên phải

Vi phạm trình tự

Một số người có thể suy nghĩ về hành vi không xác định do cả việc sử dụng global_varvà cập nhật riêng nó vi phạm 6.5 2, cho biết:

Nếu một hiệu ứng phụ trên một đối tượng vô hướng không có kết quả tương ứng với một hiệu ứng phụ khác trên cùng một đối tượng vô hướng hoặc một tính toán giá trị sử dụng giá trị của cùng một đối tượng vô hướng, thì hành vi đó không được xác định

Một điều khá quen thuộc với nhiều học viên C là hành vi của các biểu thức như x + x++không được xác định bởi tiêu chuẩn C vì cả hai đều sử dụng giá trị xvà riêng biệt sửa đổi nó trong cùng một biểu thức mà không giải trình tự. Tuy nhiên, trong trường hợp này, chúng tôi có một lệnh gọi hàm, cung cấp một số trình tự. global_varđược sử dụng trong arr[global_var]và được cập nhật trong lệnh gọi hàm update_three(2).

6.5.2.2 10 cho chúng ta biết có một điểm thứ tự trước khi hàm được gọi:

Có một điểm thứ tự sau các đánh giá của người chỉ định hàm và các đối số thực tế nhưng trước lệnh gọi thực tế

Bên trong hàm, global_var = val;là một biểu thức đầy đủ , và 3trong return 3;6,8 4:

Một biểu hiện đầy đủ là một biểu hiện đó không phải là một phần của biểu thức khác, cũng không phải là một phần của một declarator hoặc declarator trừu tượng ...

Sau đó, có một điểm thứ tự giữa hai biểu thức này, một lần nữa trên 6.8 4:

Có một điểm thứ tự giữa việc đánh giá biểu thức đầy đủ và đánh giá biểu thức đầy đủ tiếp theo được đánh giá.

Do đó, việc thực hiện C có thể đánh giá arr[global_var]đầu tiên và sau đó thực hiện lệnh gọi hàm, trong trường hợp đó có một điểm thứ tự giữa chúng bởi vì có một điểm trước lệnh gọi hàm hoặc nó có thể đánh giá global_var = val;trong lệnh gọi hàm và sau đó arr[global_var], trong trường hợp đó có một điểm thứ tự giữa chúng bởi vì có một điểm sau biểu thức đầy đủ. Vì vậy, hành vi là không xác định được một trong hai điều đó có thể được đánh giá đầu tiên nhưng nó không được xác định.


24

Kết quả ở đây là không xác định .

Trong khi thứ tự của các hoạt động trong một biểu thức, quy định cách thức các biểu thức con được nhóm lại, được xác định rõ, thứ tự đánh giá không được chỉ định. Trong trường hợp này, điều đó có nghĩa là global_varcó thể được đọc trước hoặc cuộc gọi đến update_threecó thể xảy ra trước, nhưng không có cách nào để biết được.

không xác định hành vi ở đây vì một cuộc gọi chức năng giới thiệu một điểm chuỗi, cũng như tất cả các tuyên bố trong các chức năng trong đó có một mà sửa đổi global_var.

Để làm rõ, tiêu chuẩn C định nghĩa hành vi không xác định trong phần 3.4.3 là:

hành vi không xác định

hành vi, khi sử dụng một cấu trúc chương trình không thể truy cập hoặc có lỗi hoặc dữ liệu sai, mà Tiêu chuẩn quốc tế này áp đặt không có yêu cầu

và định nghĩa hành vi không xác định trong mục 3.4.4 là:

hành vi không xác định

sử dụng một giá trị không xác định hoặc hành vi khác trong đó Tiêu chuẩn quốc tế này cung cấp hai hoặc nhiều khả năng và không áp dụng thêm các yêu cầu nào được chọn trong bất kỳ trường hợp nào

Tiêu chuẩn quy định rằng thứ tự đánh giá của các đối số hàm là không xác định, trong trường hợp này có nghĩa là arr[0]được đặt thành 3 hoặc arr[2]được đặt thành 3.


Một cuộc gọi chức năng giới thiệu một điểm chuỗi là không đủ. Nếu toán hạng bên trái được ước tính trước, nó sẽ đủ, từ đó điểm chuỗi sẽ tách toán hạng bên trái khỏi các đánh giá trong hàm. Nhưng, nếu toán hạng bên trái được ước tính sau lệnh gọi hàm, điểm chuỗi do gọi hàm không nằm giữa các đánh giá trong hàm và đánh giá toán hạng bên trái. Bạn cũng cần điểm trình tự phân tách các biểu thức đầy đủ.
Eric Postpischil

2
@EricPostpischil Trong thuật ngữ trước C11 có một điểm thứ tự khi vào và ra của một hàm. Trong thuật ngữ C11, toàn bộ cơ thể chức năng được sắp xếp không xác định theo trình tự liên quan đến bối cảnh gọi. Cả hai đều chỉ định cùng một điều, chỉ sử dụng các thuật ngữ khác nhau
MM

Điều này là hoàn toàn sai. Thứ tự đánh giá các đối số của bài tập là không xác định. Đối với kết quả của nhiệm vụ cụ thể này, đó là việc tạo ra một mảng có nội dung không đáng tin cậy, cả không thể di chuyển và sai về bản chất (không phù hợp với ngữ nghĩa hoặc kết quả dự định). Một trường hợp hoàn hảo của hành vi không xác định.
Kuroi neko

1
@kuroineko Chỉ vì đầu ra có thể thay đổi không tự động làm cho hành vi không xác định. Tiêu chuẩn có các định nghĩa khác nhau cho hành vi không xác định so với hành vi không xác định và trong tình huống này, đó là định nghĩa sau.
dbush

@EricPostpischil Bạn có các điểm thứ tự ở đây (từ Phụ lục thông tin C11 C): "Giữa các đánh giá của người chỉ định hàm và các đối số thực tế trong một lệnh gọi hàm và cuộc gọi thực tế. (6.5.2.2)", "Giữa việc đánh giá biểu thức đầy đủ và biểu thức đầy đủ tiếp theo được ước tính ... / - / ... biểu thức (tùy chọn) trong câu lệnh return (6.8.6.4) ". Và tốt, ở mỗi dấu chấm phẩy cũng vậy, vì đó là một biểu thức đầy đủ.
Lundin

1

Tôi đã thử và tôi đã nhận được mục 0 cập nhật.

Tuy nhiên, theo câu hỏi này: phần bên phải của biểu thức luôn được đánh giá đầu tiên

Thứ tự đánh giá là không xác định và không có kết quả. Vì vậy, tôi nghĩ rằng một mã như thế này nên tránh.


Tôi đã nhận được bản cập nhật ở mục 0.
Jiminion

1
Hành vi không được xác định nhưng không xác định. Đương nhiên tùy thuộc vào một trong hai nên được tránh.
Antti Haapala

@AnttiHaapala Tôi đã chỉnh sửa
Mickael B.

1
Hmm ah và nó không phải là không có kết quả nhưng được giải trình tự không xác định ... 2 người đứng ngẫu nhiên trong một hàng đợi được sắp xếp không xác định. Neo bên trong Đặc vụ Smith là không có kết quả và hành vi không xác định sẽ xảy ra.
Antti Haapala

0

Vì việc phát mã cho một phép gán trước khi bạn có giá trị cần gán rất ít, hầu hết các trình biên dịch C trước tiên sẽ phát mã gọi hàm và lưu kết quả ở đâu đó (đăng ký, ngăn xếp, v.v.), sau đó chúng sẽ phát ra mã ghi giá trị này đến đích cuối cùng của nó và do đó họ sẽ đọc biến toàn cục sau khi nó được thay đổi. Chúng ta hãy gọi đây là "trật tự tự nhiên", không được xác định bởi bất kỳ tiêu chuẩn nào mà bằng logic thuần túy.

Tuy nhiên, trong quá trình tối ưu hóa, trình biên dịch sẽ cố gắng loại bỏ bước trung gian lưu tạm thời giá trị ở đâu đó và cố gắng viết kết quả hàm trực tiếp nhất có thể đến đích cuối cùng và trong trường hợp đó, chúng thường sẽ phải đọc chỉ mục trước , ví dụ như một thanh ghi, để có thể di chuyển trực tiếp kết quả hàm vào mảng. Điều này có thể khiến biến toàn cục được đọc trước khi nó được thay đổi.

Vì vậy, đây về cơ bản là hành vi không xác định với thuộc tính rất xấu mà rất có thể kết quả sẽ khác nhau, tùy thuộc vào việc tối ưu hóa được thực hiện và mức độ tối ưu hóa này mạnh mẽ như thế nào. Nhiệm vụ của bạn là nhà phát triển để giải quyết vấn đề đó bằng cách mã hóa:

int idx = global_var;
arr[idx] = update_three(2);

hoặc mã hóa:

int temp = update_three(2);
arr[global_var] = temp;

Như một quy tắc tốt: Trừ khi các biến toàn cục là const(hoặc chúng không nhưng bạn biết rằng sẽ không có mã nào thay đổi chúng như một tác dụng phụ), bạn không bao giờ nên sử dụng chúng trực tiếp trong mã, như trong môi trường đa luồng, như trong một môi trường đa luồng, thậm chí điều này có thể không được xác định:

int result = global_var + (2 * global_var);
// Is not guaranteed to be equal to `3 * global_var`!

Vì trình biên dịch có thể đọc nó hai lần và một luồng khác có thể thay đổi giá trị ở giữa hai lần đọc. Tuy nhiên, một lần nữa, tối ưu hóa chắc chắn sẽ khiến mã chỉ đọc một lần, do đó bạn có thể lại có kết quả khác mà giờ cũng phụ thuộc vào thời gian của một luồng khác. Do đó, bạn sẽ bớt đau đầu hơn rất nhiều nếu bạn lưu trữ các biến toàn cục vào một biến ngăn xếp tạm thời trước khi sử dụng. Hãy ghi nhớ nếu trình biên dịch nghĩ rằng điều này an toàn, rất có thể nó sẽ tối ưu hóa ngay cả khi đó và thay vào đó sử dụng biến toàn cục trực tiếp, vì vậy cuối cùng, nó có thể không tạo ra sự khác biệt trong hiệu suất hoặc sử dụng bộ nhớ.

(Chỉ trong trường hợp ai đó hỏi tại sao mọi người sẽ làm x + 2 * xthay vì 3 * x- trên một số bổ sung CPU là cực nhanh và nhân với một công suất hai vì trình biên dịch sẽ biến chúng thành bit shift ( 2 * x == x << 1), nhưng nhân với số tùy ý có thể rất chậm , do đó, thay vì nhân 3, bạn nhận được mã nhanh hơn nhiều bằng cách dịch chuyển x 1 và thêm x vào kết quả - và thậm chí mẹo đó được thực hiện bởi các trình biên dịch hiện đại nếu bạn nhân 3 và bật tối ưu hóa tích cực trừ khi đó là mục tiêu hiện đại CPU trong đó phép nhân cũng nhanh như nhau kể từ đó, thủ thuật sẽ làm chậm quá trình tính toán.)


2
Đó không phải là hành vi không xác định - các khả năng liệt kê tiêu chuẩn và một trong số đó được chọn trong mọi trường hợp
Antti Haapala

Trình biên dịch sẽ không biến 3 * xthành hai lần đọc của x. Nó có thể đọc x một lần và sau đó thực hiện phương thức x + 2 * x trên thanh ghi mà nó đọc x thành
MM

6
@Mecki "Nếu bạn không thể nói kết quả là gì khi chỉ nhìn vào mã, kết quả là không xác định" - hành vi không xác định có ý nghĩa rất cụ thể trong C / C ++, và đó không phải là nó. Những người trả lời khác đã giải thích tại sao trường hợp cụ thể này không được chỉ định , nhưng không được xác định .
marcelm

3
Tôi đánh giá cao ý định chiếu một chút ánh sáng vào bên trong máy tính, ngay cả khi điều đó vượt quá phạm vi của câu hỏi ban đầu. Tuy nhiên, UB là thuật ngữ C / C ++ rất chính xác và nên được sử dụng cẩn thận, đặc biệt khi câu hỏi liên quan đến kỹ thuật ngôn ngữ. Thay vào đó, bạn có thể cân nhắc sử dụng thuật ngữ "hành vi không xác định" thích hợp, điều đó sẽ cải thiện đáng kể câu trả lời.
Kuroi neko

2
@Mecki " Không xác định có một ý nghĩa rất đặc biệt bằng tiếng Anh " ... nhưng trong một câu hỏi được dán nhãn language-lawyer, nơi mà các ngôn ngữ trong câu hỏi của mình có riêng "ý nghĩa rất đặc biệt" cho undefined , bạn sẽ chỉ gây ra sự nhầm lẫn bằng cách không sử dụng định nghĩa của ngôn ngữ.
TripeHound

-1

Chỉnh sửa toàn cầu: xin lỗi các bạn, tôi đã bị sa thải và viết rất nhiều điều vô nghĩa. Chỉ là một lão già gặm nhấm.

Tôi muốn tin rằng C đã được tha, nhưng than ôi kể từ C11, nó đã được đưa lên ngang hàng với C ++. Rõ ràng, việc biết trình biên dịch sẽ làm gì với các hiệu ứng phụ trong các biểu thức đòi hỏi phải giải quyết một câu đố toán học nhỏ liên quan đến việc sắp xếp một phần các chuỗi mã dựa trên "được đặt trước điểm đồng bộ hóa".

Tôi tình cờ đã thiết kế và triển khai một vài hệ thống nhúng thời gian thực quan trọng trong những ngày K & R (bao gồm cả bộ điều khiển của một chiếc xe điện có thể khiến mọi người đâm vào bức tường gần nhất nếu động cơ không được kiểm tra, công nghiệp 10 tấn robot có thể đè bẹp con người vào bột giấy nếu không được chỉ huy đúng cách và một lớp hệ thống, mặc dù vô hại, sẽ có vài chục bộ xử lý hút bus dữ liệu của chúng khô với hệ thống dưới 1%).

Tôi có thể quá già hoặc ngu ngốc để có được sự khác biệt giữa không xác định và không xác định, nhưng tôi nghĩ rằng tôi vẫn có một ý tưởng khá tốt về việc thực thi đồng thời và truy cập dữ liệu có nghĩa là gì. Theo ý kiến ​​được cho là thông tin của tôi, nỗi ám ảnh về C ++ và bây giờ là những người C với ngôn ngữ thú cưng của họ tiếp quản các vấn đề đồng bộ hóa là một giấc mơ đắt giá. Hoặc bạn biết thực thi đồng thời là gì và bạn không cần bất kỳ gizmos nào, hoặc bạn không, và bạn sẽ làm cho thế giới rộng lớn không cố gắng gây rối với nó.

Tất cả các khối lượng trừu tượng của rào cản bộ nhớ tưới nước này chỉ đơn giản là do một số hạn chế tạm thời của các hệ thống bộ đệm đa CPU, tất cả đều có thể được gói gọn trong các đối tượng đồng bộ hóa hệ điều hành phổ biến như, ví dụ, các biến thể và biến điều kiện C ++ cung cấp.
Chi phí của việc đóng gói này là nhưng hiệu suất giảm một phút so với việc sử dụng các hướng dẫn CPU cụ thể có thể đạt được là một số trường hợp.
Các volatiletừ khóa (hoặc một#pragma dont-mess-with-that-variableđối với tất cả tôi, với tư cách là một lập trình viên hệ thống, quan tâm) sẽ là khá đủ để nói với trình biên dịch ngừng sắp xếp lại các truy cập bộ nhớ. Mã tối ưu có thể dễ dàng được tạo ra bằng các chỉ thị asm trực tiếp để rắc trình điều khiển và mã hệ điều hành cấp thấp với các hướng dẫn cụ thể của CPU ad hoc. Nếu không có kiến ​​thức sâu sắc về cách thức hoạt động của phần cứng cơ bản (hệ thống bộ đệm hoặc giao diện bus), bạn chắc chắn sẽ viết mã vô dụng, không hiệu quả hoặc bị lỗi.

Một phút điều chỉnh volatiletừ khóa và Bob sẽ là tất cả mọi người trừ chú của các lập trình viên cấp thấp khó tính nhất. Thay vào đó, các nhóm toán học C ++ thông thường đã có một ngày thực địa thiết kế một sự trừu tượng khó hiểu khác, mang lại xu hướng điển hình của họ là thiết kế các giải pháp tìm kiếm các vấn đề không tồn tại và nhầm lẫn định nghĩa của ngôn ngữ lập trình với thông số kỹ thuật của trình biên dịch.

Chỉ lần này, sự thay đổi cần thiết để làm mất đi một khía cạnh cơ bản của C, vì những "rào cản" này phải được tạo ra ngay cả trong mã C cấp thấp để hoạt động chính xác. Điều đó, trong số những thứ khác, rèn sự định nghĩa trong biểu thức, không có lời giải thích hay biện minh nào.

Kết luận, việc một trình biên dịch có thể tạo ra một mã máy nhất quán từ đoạn C vô lý này chỉ là hậu quả xa vời của cách những kẻ C ++ đối phó với sự không nhất quán tiềm tàng của các hệ thống bộ đệm vào cuối những năm 2000.
Nó đã tạo ra một mớ hỗn độn khủng khiếp về một khía cạnh cơ bản của C (định nghĩa biểu thức), do đó, phần lớn các lập trình viên C - những người không đưa ra một hệ thống bộ nhớ cache chết tiệt, và đúng như vậy - giờ đây buộc phải dựa vào các bậc thầy để giải thích sự khác biệt giữa a = b() + c()a = b + c.

Cố gắng đoán những gì sẽ trở thành của mảng không may này là mất thời gian và nỗ lực ròng. Bất kể trình biên dịch sẽ làm gì với nó, mã này là sai về mặt bệnh lý. Điều duy nhất có trách nhiệm phải làm với nó là gửi nó vào thùng.
Về mặt khái niệm, các tác dụng phụ luôn có thể được chuyển ra khỏi các biểu thức, với nỗ lực tầm thường là để cho phép sửa đổi xảy ra trước hoặc sau khi đánh giá, trong một tuyên bố riêng.
Loại mã shitty này có thể đã được chứng minh vào những năm 80, khi bạn không thể mong đợi một trình biên dịch để tối ưu hóa bất cứ điều gì. Nhưng bây giờ các trình biên dịch từ lâu đã trở nên thông minh hơn hầu hết các lập trình viên, tất cả những gì còn lại là một đoạn mã shitty.

Tôi cũng không hiểu tầm quan trọng của cuộc tranh luận không xác định / không xác định này. Hoặc bạn có thể dựa vào trình biên dịch để tạo mã với hành vi nhất quán hoặc bạn không thể. Cho dù bạn gọi đó là không xác định hoặc không xác định có vẻ như một điểm moot.

Theo ý kiến ​​được cho là của tôi, C đã đủ nguy hiểm trong trạng thái K & R của nó. Một sự tiến hóa hữu ích sẽ là thêm các biện pháp an toàn thông thường. Ví dụ, sử dụng công cụ phân tích mã nâng cao này, thông số kỹ thuật buộc trình biên dịch phải thực hiện ít nhất là tạo cảnh báo về mã bonkers, thay vì âm thầm tạo mã có khả năng không đáng tin cậy đến mức cực đoan.
Nhưng thay vào đó, các chàng trai đã quyết định, ví dụ, để xác định một thứ tự đánh giá cố định trong C ++ 17. Bây giờ mọi phần mềm imbecile đều được kích hoạt để đưa các tác dụng phụ vào mã của anh ấy / cô ấy một cách có chủ đích, dựa trên sự chắc chắn rằng các trình biên dịch mới sẽ háo hức xử lý obfuscation theo cách xác định.

K & R là một trong những tuyệt tác thực sự của thế giới điện toán. Trong hai mươi đô la, bạn có một đặc tả toàn diện về ngôn ngữ (Tôi đã thấy các cá nhân đơn lẻ viết trình biên dịch hoàn chỉnh chỉ bằng cách sử dụng cuốn sách này), một hướng dẫn tham khảo tuyệt vời (mục lục thường sẽ chỉ cho bạn trong một vài trang câu trả lời của bạn câu hỏi), và một cuốn sách giáo khoa sẽ dạy bạn sử dụng ngôn ngữ một cách hợp lý. Hoàn thành với những lý lẽ, ví dụ và những lời cảnh báo khôn ngoan về vô số cách bạn có thể lạm dụng ngôn ngữ để làm những việc rất, rất ngu ngốc.

Phá hủy di sản đó để kiếm được rất ít dường như là một sự lãng phí tàn nhẫn đối với tôi. Nhưng một lần nữa tôi rất có thể không nhìn thấy điểm hoàn toàn. Có lẽ một linh hồn tốt bụng nào đó có thể chỉ cho tôi theo hướng của một ví dụ về mã C mới có lợi thế đáng kể của các tác dụng phụ này?


Đó là hành vi không xác định nếu có tác dụng phụ trên cùng một đối tượng trong cùng một biểu thức, C17 6.5 / 2. Đây là những kết quả không theo C17 6.5,18 / 3. Nhưng văn bản từ 6.5 / 2 "Nếu một hiệu ứng phụ trên một đối tượng vô hướng không bị ảnh hưởng so với hiệu ứng phụ khác trên cùng một đối tượng vô hướng hoặc tính toán giá trị sử dụng giá trị của cùng một đối tượng vô hướng, thì hành vi không được xác định." không áp dụng, vì tính toán giá trị bên trong hàm được sắp xếp theo trình tự trước hoặc sau khi truy cập chỉ mục mảng, bất kể toán tử gán có bản thân toán hạng không được xử lý.
Lundin

Hàm gọi hành động giống như "một cuộc đột biến chống lại truy cập không có kết quả", nếu bạn muốn. Tương tự như crap điều hành dấu phẩy tối nghĩa như 0,expr,0.
Lundin

Tôi nghĩ rằng bạn tin rằng các tác giả của Tiêu chuẩn khi họ nói "Hành vi không xác định mang lại cho giấy phép người thực hiện không mắc phải một số lỗi chương trình khó chẩn đoán. Nó cũng xác định các khu vực có thể mở rộng ngôn ngữ phù hợp: người triển khai có thể tăng ngôn ngữ bằng cách cung cấp định nghĩa về hành vi không xác định chính thức . " và cho biết Tiêu chuẩn không được coi là hạ thấp các chương trình hữu ích không tuân thủ nghiêm ngặt. Tôi nghĩ rằng hầu hết các tác giả của Tiêu chuẩn sẽ nghĩ rằng rõ ràng là những người tìm cách viết trình biên dịch chất lượng ...
supercat

... Nên tìm cách sử dụng UB như một cơ hội để làm cho trình biên dịch của họ hữu ích nhất có thể cho khách hàng của họ. Tôi nghi ngờ bất kỳ ai có thể tưởng tượng rằng các nhà văn trình biên dịch sẽ sử dụng nó như một cái cớ để trả lời các khiếu nại của "Trình biên dịch của bạn xử lý mã này ít hữu ích hơn so với mọi người" với "Đó là vì Tiêu chuẩn không yêu cầu chúng tôi xử lý nó một cách hữu ích và triển khai rằng các chương trình hữu ích xử lý các hành vi mà Tiêu chuẩn không bắt buộc chỉ đơn thuần là thúc đẩy việc viết các chương trình bị hỏng ".
supercat

Tôi không nhìn thấy điểm trong nhận xét của bạn. Dựa vào hành vi dành riêng cho trình biên dịch là sự đảm bảo cho tính không di động. Nó cũng đòi hỏi niềm tin lớn vào nhà sản xuất trình biên dịch, người có thể ngừng bất kỳ "định nghĩa bổ sung" nào vào bất cứ lúc nào. Điều duy nhất một trình biên dịch có thể làm là tạo ra các cảnh báo, mà một lập trình viên thông thái và hiểu biết có thể quyết định xử lý như các lỗi. Vấn đề tôi thấy với con quái vật ISO này là nó tạo ra mã dữ dội như ví dụ của OP hợp pháp (vì những lý do cực kỳ không rõ ràng, so với định nghĩa của biểu thức K & R).
Kuroi neko
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.