Liệu có dễ bay hơi không đảm bảo bất cứ điều gì trong mã C di động cho các hệ thống đa lõi?


12

Sau khi xem xét một loạt các khác câu hỏi họ trả lời , tôi nhận được ấn tượng rằng không có thoả thuận rộng rãi về những gì từ khóa "không ổn định" trong C có nghĩa là chính xác.

Ngay cả bản thân tiêu chuẩn dường như cũng không đủ rõ ràng để mọi người đồng ý về ý nghĩa của nó .

Trong số các vấn đề khác:

  1. Nó dường như cung cấp các đảm bảo khác nhau tùy thuộc vào phần cứng của bạn và tùy thuộc vào trình biên dịch của bạn.
  2. Nó ảnh hưởng đến tối ưu hóa trình biên dịch nhưng không tối ưu hóa phần cứng, do đó, trên bộ xử lý tiên tiến thực hiện tối ưu hóa thời gian chạy của chính nó, thậm chí không rõ liệu trình biên dịch có thể ngăn chặn bất kỳ tối ưu hóa nào bạn muốn ngăn chặn hay không. (Một số trình biên dịch tạo ra các hướng dẫn để ngăn chặn một số tối ưu hóa phần cứng trên một số hệ thống, nhưng điều này dường như không được tiêu chuẩn hóa theo bất kỳ cách nào.)

Để tóm tắt vấn đề, có vẻ như (sau khi đọc rất nhiều) rằng "dễ bay hơi" đảm bảo một cái gì đó như: Giá trị sẽ được đọc / ghi không chỉ từ / đến một thanh ghi, mà ít nhất là vào bộ đệm L1 của lõi, theo cùng thứ tự việc đọc / ghi xuất hiện trong mã. Nhưng điều này có vẻ vô dụng, vì đọc / ghi từ / đến một thanh ghi đã đủ trong cùng một luồng, trong khi phối hợp với bộ đệm L1 không đảm bảo bất cứ điều gì thêm về việc phối hợp với các luồng khác. Tôi không thể tưởng tượng khi nào việc đồng bộ hóa chỉ với bộ đệm L1 có thể rất quan trọng.

SỬ DỤNG 1
Việc sử dụng dễ bay hơi được chấp thuận rộng rãi dường như chỉ dành cho các hệ thống cũ hoặc nhúng trong đó các vị trí bộ nhớ nhất định được ánh xạ phần cứng tới các chức năng I / O, giống như một chút trong bộ nhớ điều khiển (trực tiếp, trong phần cứng) một ánh sáng hoặc một chút trong bộ nhớ cho bạn biết liệu phím bàn phím có bị hỏng hay không (bởi vì nó được kết nối trực tiếp bởi phần cứng với phím).

Có vẻ như "sử dụng 1" không xảy ra trong mã di động có mục tiêu bao gồm các hệ thống đa lõi.

SỬ DỤNG 2
Không quá khác biệt so với "sử dụng 1" là bộ nhớ có thể được đọc hoặc ghi bất cứ lúc nào bởi một trình xử lý ngắt (có thể điều khiển ánh sáng hoặc lưu trữ thông tin từ một phím). Nhưng đối với điều này, chúng ta có một vấn đề là tùy thuộc vào hệ thống, trình xử lý ngắt có thể chạy trên một lõi khác với bộ nhớ cache riêng và "không ổn định" không đảm bảo tính liên kết của bộ đệm trên tất cả các hệ thống.

Vì vậy, "sử dụng 2" dường như vượt quá những gì "dễ bay hơi" có thể mang lại.

SỬ DỤNG 3
Cách sử dụng không thể tranh cãi khác mà tôi thấy là để ngăn chặn tối ưu hóa truy cập sai thông qua các biến khác nhau chỉ vào cùng một bộ nhớ mà trình biên dịch không nhận ra là cùng một bộ nhớ. Nhưng điều này có lẽ chỉ là không thể tranh cãi bởi vì mọi người không nói về nó - tôi chỉ thấy một đề cập đến nó. Và tôi nghĩ rằng tiêu chuẩn C đã nhận ra rằng các con trỏ "khác nhau" (như các đối số khác nhau của một hàm) có thể trỏ đến cùng một mục hoặc các mục gần đó và đã chỉ định rằng trình biên dịch phải tạo mã hoạt động ngay cả trong các trường hợp như vậy. Tuy nhiên, tôi không thể nhanh chóng tìm thấy chủ đề này trong tiêu chuẩn mới nhất (500 trang!).

Vì vậy, "sử dụng 3" có thể không tồn tại?

Do đó câu hỏi của tôi:

"Biến động" có đảm bảo bất cứ điều gì trong mã C di động cho các hệ thống đa lõi không?


EDIT - cập nhật

Sau khi duyệt tiêu chuẩn mới nhất , có vẻ như câu trả lời ít nhất là rất hạn chế:
1. Tiêu chuẩn liên tục chỉ định điều trị đặc biệt cho loại cụ thể "dễ bay hơi sig_atomic_t". Tuy nhiên, tiêu chuẩn cũng nói rằng việc sử dụng chức năng tín hiệu trong chương trình đa luồng dẫn đến hành vi không xác định. Vì vậy, trường hợp sử dụng này dường như bị giới hạn trong giao tiếp giữa một chương trình đơn luồng và trình xử lý tín hiệu của nó.
2. Tiêu chuẩn cũng chỉ định một ý nghĩa rõ ràng cho "dễ bay hơi" liên quan đến setjmp / longjmp. (Mã ví dụ về vấn đề được đưa ra trong các câu hỏicâu trả lời khác .)

Vì vậy, câu hỏi chính xác hơn trở thành:
"Biến động" có đảm bảo bất cứ điều gì trong mã C di động cho các hệ thống đa lõi không, ngoài (1) cho phép một chương trình đơn luồng nhận thông tin từ trình xử lý tín hiệu của nó, hoặc (2) cho phép setjmp mã để xem các biến được sửa đổi giữa setjmp và longjmp?

Đây vẫn là một câu hỏi có / không.

Nếu "có", sẽ thật tuyệt nếu bạn có thể hiển thị một ví dụ về mã di động không có lỗi sẽ bị lỗi nếu "không ổn định" bị bỏ qua. Nếu "không", thì tôi cho rằng một trình biên dịch có thể bỏ qua "dễ bay hơi" bên ngoài hai trường hợp rất cụ thể này, cho các mục tiêu đa lõi.


3
Tín hiệu tồn tại trong C di động; Điều gì về một biến toàn cầu được cập nhật bởi một bộ xử lý tín hiệu? Điều này cần phải được volatilethông báo cho chương trình rằng nó có thể thay đổi không đồng bộ.
Nate Eldredge

2
@NateEldredge Toàn cầu, trong khi không ổn định, không đủ tốt. Nó cần phải là nguyên tử là tốt.
Eugene Sh.

@EugeneSh.: Vâng, tất nhiên. Nhưng câu hỏi trong tầm tay là về volatilecụ thể, mà tôi tin là cần thiết.
Nate Eldredge

" trong khi phối hợp với bộ đệm L1 không đảm bảo gì thêm về việc phối hợp với các luồng khác " Trường hợp "phối hợp với bộ đệm L1" không đủ để giao tiếp với các luồng khác?
tò mò

1
Có thể có liên quan, đề xuất C ++ không tán thành biến động , đề xuất giải quyết nhiều mối quan tâm bạn nêu ra ở đây và có lẽ kết quả của nó sẽ có ảnh hưởng đến ủy ban C
MM

Câu trả lời:


1

Để tóm tắt vấn đề, có vẻ như (sau khi đọc rất nhiều) rằng "dễ bay hơi" đảm bảo một cái gì đó như: Giá trị sẽ được đọc / ghi không chỉ từ / đến một thanh ghi, mà ít nhất là vào bộ đệm L1 của lõi, theo cùng thứ tự việc đọc / ghi xuất hiện trong mã .

Không, nó hoàn toàn không . Và điều đó làm cho biến động gần như vô dụng cho mục đích của mã an toàn MT.

Nếu đúng như vậy, thì biến động sẽ khá tốt cho các biến được chia sẻ bởi nhiều luồng vì việc sắp xếp các sự kiện trong bộ đệm L1 là tất cả những gì bạn cần làm trong CPU thông thường (đó là đa lõi hoặc đa CPU trên bo mạch chủ) có khả năng hợp tác theo cách làm cho việc triển khai bình thường của đa luồng C / C ++ hoặc Java có thể có với chi phí dự kiến ​​điển hình (nghĩa là, không phải là một chi phí lớn đối với hầu hết các hoạt động đột biến nguyên tử hoặc không có nội dung).

Nhưng dễ bay hơi không cung cấp bất kỳ thứ tự được đảm bảo (hoặc "khả năng hiển thị bộ nhớ") trong bộ đệm trong lý thuyết hoặc trong thực tế.

(Lưu ý: những điều sau đây dựa trên diễn giải âm thanh của các tài liệu tiêu chuẩn, ý định của tiêu chuẩn, thực tiễn lịch sử và hiểu sâu sắc về những kỳ vọng của các nhà văn biên dịch. Cách tiếp cận này dựa trên lịch sử, thực tiễn thực tế, và kỳ vọng và hiểu biết về người thực trong thế giới thực, mạnh mẽ và đáng tin cậy hơn nhiều so với phân tích các từ của một tài liệu không được biết đến là văn bản đặc tả kỹ thuật xuất sắc và đã được sửa đổi nhiều lần.)

Trong thực tế, volility đảm bảo khả năng ptrace là khả năng sử dụng thông tin gỡ lỗi cho chương trình đang chạy, ở bất kỳ mức độ tối ưu hóa nào và thực tế thông tin gỡ lỗi có ý nghĩa đối với các đối tượng dễ bay hơi này:

  • bạn có thể sử dụng ptrace (một cơ chế giống như ptrace) để đặt các điểm dừng có ý nghĩa tại các điểm chuỗi sau các hoạt động liên quan đến các đối tượng dễ bay hơi: bạn thực sự có thể phá vỡ chính xác các điểm này (lưu ý rằng điều này chỉ hoạt động nếu bạn sẵn sàng đặt nhiều điểm dừng như bất kỳ Câu lệnh C / C ++ có thể được biên dịch thành nhiều điểm bắt đầu và kết thúc lắp ráp khác nhau, như trong một vòng lặp không được kiểm soát ồ ạt);
  • trong khi một chuỗi thực thi bị dừng, bạn có thể đọc giá trị của tất cả các đối tượng dễ bay hơi, vì chúng có biểu diễn chính tắc của chúng (theo ABI cho loại tương ứng của chúng); một biến cục bộ không bay hơi có thể có một đại diện không điển hình, f.ex. một đại diện thay đổi: một biến được sử dụng để lập chỉ mục một mảng có thể được nhân với kích thước của các đối tượng riêng lẻ, để lập chỉ mục dễ dàng hơn; hoặc nó có thể được thay thế bằng một con trỏ tới một phần tử mảng (miễn là tất cả việc sử dụng biến như được chuyển đổi tương tự) (nghĩ rằng thay đổi dx thành du trong một tích phân);
  • bạn cũng có thể sửa đổi các đối tượng đó (miễn là ánh xạ bộ nhớ cho phép, vì đối tượng dễ bay hơi có tuổi thọ tĩnh có thể đủ điều kiện có thể chỉ trong phạm vi bộ nhớ được ánh xạ chỉ đọc).

Bảo đảm dễ bay hơi trong thực tế nhiều hơn một chút so với giải thích ptrace nghiêm ngặt: nó cũng đảm bảo rằng các biến tự động dễ bay hơi có địa chỉ trên ngăn xếp, vì chúng không được phân bổ cho một thanh ghi, phân bổ đăng ký sẽ làm cho thao tác ptrace trở nên tinh tế hơn (trình biên dịch có thể thông tin gỡ lỗi đầu ra để giải thích cách các biến được phân bổ cho các thanh ghi, nhưng đọc và thay đổi trạng thái thanh ghi có liên quan nhiều hơn một chút so với truy cập địa chỉ bộ nhớ).

Lưu ý rằng khả năng gỡ lỗi chương trình đầy đủ, đang xem xét tất cả các biến không ổn định ít nhất tại các điểm chuỗi, được cung cấp bởi chế độ "tối ưu hóa không" của trình biên dịch, một chế độ vẫn thực hiện tối ưu hóa tầm thường như đơn giản hóa số học (thường không được đảm bảo tối ưu hóa ở tất cả các chế độ). Nhưng dễ bay hơi mạnh hơn không tối ưu hóa: x-xcó thể được đơn giản hóa cho một số nguyên không bay hơi xnhưng không phải là một đối tượng dễ bay hơi.

Vì vậy, các phương tiện dễ bay hơi được đảm bảo sẽ được biên dịch , giống như việc dịch từ nguồn sang nhị phân / lắp ráp bởi trình biên dịch của một cuộc gọi hệ thống không phải là diễn giải lại, thay đổi hoặc tối ưu hóa theo bất kỳ cách nào bởi trình biên dịch. Lưu ý rằng các cuộc gọi thư viện có thể hoặc không thể là các cuộc gọi hệ thống. Nhiều chức năng hệ thống chính thức thực sự là chức năng thư viện cung cấp một lớp xen kẽ mỏng và nói chung là trì hoãn hạt nhân ở cuối. (Đặc biệt getpidkhông cần phải đi đến kernel và cũng có thể đọc vị trí bộ nhớ do HĐH cung cấp có chứa thông tin.)

Tương tác dễ bay hơi là tương tác với thế giới bên ngoài của máy thật , phải tuân theo "máy trừu tượng". Chúng không tương tác nội bộ của các phần chương trình với các phần chương trình khác. Trình biên dịch chỉ có thể lý do về những gì nó biết, đó là các phần chương trình nội bộ.

Việc tạo mã cho truy cập dễ bay hơi phải tuân theo sự tương tác tự nhiên nhất với vị trí bộ nhớ đó: điều đó không gây ngạc nhiên. Điều đó có nghĩa là một số truy cập dễ bay hơi được dự kiến ​​là nguyên tử : nếu cách tự nhiên để đọc hoặc viết biểu diễn của a longtrên kiến ​​trúc là nguyên tử, thì người ta hy vọng rằng việc đọc hoặc viết của một volatile longsẽ là nguyên tử, vì trình biên dịch sẽ không tạo ra mã không hiệu quả ngớ ngẩn để truy cập các đối tượng dễ bay hơi từng byte, ví dụ .

Bạn sẽ có thể xác định rằng bằng cách biết kiến ​​trúc. Bạn không cần phải biết bất cứ điều gì về trình biên dịch, vì dễ bay hơi có nghĩa là trình biên dịch phải trong suốt .

Nhưng dễ bay hơi không hơn gì buộc phát xạ của lắp ráp dự kiến ​​cho tối ưu hóa tối thiểu cho các trường hợp cụ thể để thực hiện một hoạt động bộ nhớ: ngữ nghĩa dễ bay hơi có nghĩa là ngữ nghĩa trường hợp chung.

Trường hợp chung là trình biên dịch làm gì khi nó không có bất kỳ thông tin nào về cấu trúc: f.ex. gọi một hàm ảo trên một giá trị thông qua công văn động là một trường hợp chung, thực hiện cuộc gọi trực tiếp đến bộ ghi đè sau khi xác định tại thời điểm biên dịch loại đối tượng được chỉ định bởi biểu thức là một trường hợp cụ thể. Trình biên dịch luôn có một trường hợp xử lý chung của tất cả các cấu trúc và nó tuân theo ABI.

Tính dễ bay hơi không có gì đặc biệt để đồng bộ hóa các luồng hoặc cung cấp "khả năng hiển thị bộ nhớ": volility chỉ cung cấp các đảm bảo ở mức trừu tượng nhìn thấy từ bên trong một luồng thực thi hoặc dừng, đó là bên trong lõi CPU :

  • volility không nói gì về hoạt động bộ nhớ nào đạt RAM chính (bạn có thể đặt các loại bộ nhớ đệm cụ thể với hướng dẫn lắp ráp hoặc gọi hệ thống để có được các đảm bảo này);
  • không ổn định không cung cấp bất kỳ đảm bảo nào về thời điểm các hoạt động bộ nhớ sẽ được cam kết với bất kỳ mức bộ nhớ cache nào (thậm chí không phải L1) .

Chỉ có điểm thứ hai có nghĩa là dễ bay hơi không hữu ích trong hầu hết các vấn đề giao tiếp giữa các luồng; điểm đầu tiên về cơ bản không liên quan trong bất kỳ vấn đề lập trình nào không liên quan đến giao tiếp với các thành phần phần cứng bên ngoài CPU mà vẫn nằm trên bus bộ nhớ.

Thuộc tính của tính dễ bay hơi cung cấp hành vi được bảo đảm theo quan điểm của lõi chạy luồng có nghĩa là tín hiệu không đồng bộ được gửi đến luồng đó, được chạy từ quan điểm của thứ tự thực hiện của luồng đó, xem các hoạt động theo thứ tự mã nguồn .

Trừ khi bạn có kế hoạch gửi tín hiệu đến các chủ đề của mình (một cách tiếp cận cực kỳ hữu ích để hợp nhất thông tin về các chủ đề hiện đang chạy mà không có điểm dừng đã thỏa thuận trước đó), không ổn định không dành cho bạn.


6

Tôi không phải là chuyên gia, nhưng cppreference.com có ​​những thông tinvolatile khá hay về tôi . Đây là ý chính của nó:

Mọi quyền truy cập (cả đọc và viết) được thực hiện thông qua biểu thức giá trị của loại đủ điều kiện dễ bay hơi được coi là tác dụng phụ có thể quan sát được cho mục đích tối ưu hóa và được đánh giá đúng theo các quy tắc của máy trừu tượng (nghĩa là tất cả các ghi được hoàn thành tại một thời gian trước điểm thứ tự tiếp theo). Điều này có nghĩa là trong một luồng thực thi, một truy cập dễ bay hơi không thể được tối ưu hóa hoặc sắp xếp lại liên quan đến một hiệu ứng phụ có thể nhìn thấy khác được phân tách bằng một điểm thứ tự từ truy cập dễ bay hơi.

Nó cũng cung cấp một số công dụng:

Công dụng của chất dễ bay hơi

1) các đối tượng biến động tĩnh mô hình các cổng I / O được ánh xạ bộ nhớ và các đối tượng biến động tĩnh const mô hình các cổng đầu vào được ánh xạ bộ nhớ, như đồng hồ thời gian thực

2) các đối tượng dễ bay hơi tĩnh loại sig_atomic_t được sử dụng để liên lạc với các bộ xử lý tín hiệu.

3) các biến dễ bay hơi cục bộ của một hàm có chứa một lệnh gọi macro setjmp là các biến cục bộ duy nhất được đảm bảo giữ lại các giá trị của chúng sau khi trả về longjmp.

4) Ngoài ra, các biến dễ bay hơi có thể được sử dụng để vô hiệu hóa các hình thức tối ưu hóa nhất định, ví dụ: để vô hiệu hóa loại bỏ cửa hàng chết hoặc gấp liên tục cho các điểm vi mô.

Và tất nhiên, nó đề cập đến việc volatilekhông đồng bộ hóa luồng:

Lưu ý rằng các biến dễ bay hơi không phù hợp để liên lạc giữa các luồng; họ không cung cấp tính nguyên tử, đồng bộ hóa hoặc thứ tự bộ nhớ. Việc đọc từ một biến dễ bay hơi được sửa đổi bởi một luồng khác mà không đồng bộ hóa hoặc sửa đổi đồng thời từ hai luồng không đồng bộ là hành vi không xác định do cuộc đua dữ liệu.


2
Cụ thể, (2) và (3) có liên quan đến mã di động.
Nate Eldredge

2
@TED ​​Mặc dù tên miền, liên kết là thông tin về C, không phải C ++
David Brown

@NateEldredge Bạn hiếm khi có thể sử dụng longjmpmã C ++.
tò mò

@DavidBrown C và C ++ có cùng định nghĩa về một SE có thể quan sát được, và về cơ bản là cùng một nguyên thủy luồng.
tò mò

4

Trước hết, trong lịch sử đã có nhiều trục trặc khác nhau liên quan đến những suy nghĩ khác nhau về ý nghĩa của việc volatiletruy cập và tương tự. Xem nghiên cứu này: Volatiles bị sảy thai, và phải làm gì với nó .

Ngoài các vấn đề khác nhau được đề cập trong nghiên cứu đó, hành vi của volatilelà di động, tiết kiệm cho một khía cạnh của chúng: khi chúng đóng vai trò là rào cản bộ nhớ . Một rào cản bộ nhớ là một số cơ chế có sẵn để ngăn chặn việc thực thi đồng thời mã của bạn. Sử dụng volatilenhư một rào cản bộ nhớ chắc chắn không thể di động.

Cho dù ngôn ngữ C có đảm bảo hành vi bộ nhớ hay không từ đó volatilerõ ràng là có thể tranh cãi, mặc dù cá nhân tôi nghĩ rằng ngôn ngữ là rõ ràng. Đầu tiên chúng ta có định nghĩa chính thức về tác dụng phụ, C17 5.1.2.3:

Truy cập một volatileđối tượng, sửa đổi một đối tượng, sửa đổi một tệp hoặc gọi một chức năng thực hiện bất kỳ hoạt động nào trong số đó là tất cả các tác dụng phụ , đó là những thay đổi trong trạng thái của môi trường thực thi.

Tiêu chuẩn xác định trình tự thuật ngữ, như một cách xác định thứ tự đánh giá (thực hiện). Định nghĩa là chính thức và cồng kềnh:

Trình tự trước là một mối quan hệ không đối xứng, bắc cầu, cặp đôi giữa các đánh giá được thực hiện bởi một luồng duy nhất, tạo ra một thứ tự một phần trong số các đánh giá đó. Cho hai đánh giá A và B, nếu A được giải trình tự trước B, thì việc thực hiện A sẽ xảy ra trước khi thực hiện B. (Ngược lại, nếu A được giải trình tự trước B, thì B được giải trình tự sau A.) Nếu A không được giải trình tự trước hoặc sau B, sau đó A và B không có kết quả . Các đánh giá A và B không được xác định theo trình tự khi A được giải trình tự trước hoặc sau B, nhưng không xác định được.13) Sự hiện diện của a điểm thứ tự giữa việc đánh giá biểu thức A và B ngụ ý rằng mọi tính toán giá trị và tác dụng phụ liên quan đến A được sắp xếp theo thứ tự trước khi tính toán giá trị và tác dụng phụ liên quan đến B. (Tóm tắt các điểm chuỗi được nêu trong phụ lục C.)

TL; DR ở trên về cơ bản là trong trường hợp chúng ta có một biểu thức Acó chứa các tác dụng phụ, thì nó phải được thực hiện trước một biểu thức khác B, trong trường hợp Bđược giải trình tự sau A.

Tối ưu hóa mã C được thực hiện thông qua phần này:

Trong máy trừu tượng, tất cả các biểu thức được đánh giá theo quy định của ngữ nghĩa. Việc triển khai thực tế không cần đánh giá một phần của biểu thức nếu nó có thể suy ra rằng giá trị của nó không được sử dụng và không có tác dụng phụ cần thiết nào được tạo ra (bao gồm mọi tác nhân gây ra bằng cách gọi hàm hoặc truy cập một đối tượng dễ bay hơi).

Điều này có nghĩa là chương trình có thể đánh giá (thực hiện) các biểu thức theo thứ tự mà tiêu chuẩn bắt buộc ở nơi khác (thứ tự đánh giá, v.v.). Nhưng nó không cần phải đánh giá (thực thi) một giá trị nếu nó có thể suy ra rằng nó không được sử dụng. Ví dụ, hoạt động 0 * xkhông cần đánh giá xvà chỉ cần thay thế biểu thức bằng 0.

Trừ khi truy cập vào một biến là một tác dụng phụ. Có nghĩa là trong trường hợp xvolatile, nó phải đánh giá (thực thi) 0 * xmặc dù kết quả sẽ luôn là 0. Tối ưu hóa không được phép.

Hơn nữa, tiêu chuẩn nói về hành vi quan sát được:

Các yêu cầu tối thiểu về việc thực hiện tuân thủ là:

  • Truy cập vào các đối tượng dễ bay hơi được đánh giá đúng theo các quy tắc của máy trừu tượng.
    / - / Đây là hành vi có thể quan sát được của chương trình.

Với tất cả những điều trên, việc triển khai tuân thủ (trình biên dịch + hệ thống cơ bản) có thể không thực thi quyền truy cập của volatilecác đối tượng theo thứ tự không có kết quả, trong trường hợp ngữ nghĩa của nguồn C được viết khác.

Điều này có nghĩa là trong ví dụ này

volatile int x;
volatile int y;
z = x;
z = y;

Cả hai biểu thức gán phải được đánh giá và z = x; phải được đánh giá trước z = y;. Một triển khai đa bộ xử lý thuê ngoài hai hoạt động này cho hai lõi không có kết quả khác nhau là không phù hợp!

Vấn đề nan giải là các trình biên dịch không thể làm được gì nhiều về những thứ như bộ đệm lưu trữ trước và tìm đường dẫn hướng dẫn, v.v., đặc biệt không phải khi chạy trên hệ điều hành. Và vì vậy, các trình biên dịch chuyển vấn đề đó cho các lập trình viên, nói với họ rằng các rào cản bộ nhớ bây giờ là trách nhiệm của lập trình viên. Trong khi tiêu chuẩn C nêu rõ rằng vấn đề cần được giải quyết bằng trình biên dịch.

Trình biên dịch không nhất thiết phải quan tâm để giải quyết vấn đề, và vì vậy, volatileđể hoạt động như một rào cản bộ nhớ là không khả dụng. Nó đã trở thành một chất lượng của vấn đề thực hiện.


@cquilguy Không quan trọng.
Lundin

@cquilguy Không quan trọng, miễn là nó là một loại loại số nguyên có hoặc không có vòng loại.
Lundin

Nếu đó là một số nguyên không dễ bay hơi đơn giản, tại sao việc ghi thừa sẽ zđược thực hiện? (như z = x; z = y;) Giá trị sẽ bị xóa trong câu lệnh tiếp theo.
tò mò

@cquilguy Bởi vì việc đọc các biến dễ bay hơi phải được thực hiện bất kể, theo trình tự được chỉ định.
Lundin

Sau đó có zthực sự được giao hai lần? Làm thế nào để bạn biết rằng "đọc được thực hiện"?
tò mò
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.