2010-02-21 20 views
14

Tôi đoán rằng sau đây sẽ cung cấp cho tôi 10 ints dễ bay hơiLàm thế nào để khai báo một mảng tạo bằng malloc là dễ bay hơi trong C++

volatile int foo[10]; 

Tuy nhiên, tôi không nghĩ rằng sau đây sẽ làm điều tương tự.

volatile int* foo; 
foo = malloc(sizeof(int)*10); 

Hãy sửa tôi nếu tôi sai về điều này và cách tôi có thể có một mảng dễ bay hơi bằng malloc.

Cảm ơn.

+1

Tôi đã tìm thấy giải thích tốt ở đây: http://www.embedded.com/story/OEG20010615S0107 – Mark

+1

Tôi đã bắt đầu một câu hỏi về usenet: http://groups.google.com/group/comp.std .C++/browse_thread/thread/4af91d60c2a1af8a? pli = 1 –

Trả lời

11
int volatile * foo; 

đọc từ phải sang trái "foo là một con trỏ đến một int dễ bay hơi"

vì vậy bất cứ int bạn truy cập thông qua foo, int sẽ dễ bay hơi.

P.S.

int * volatile foo; // "foo is a volatile pointer to an int" 

==

volatile int * foo; // foo is a pointer to an int, volatile 

Ý nghĩa foo là dễ bay hơi. Trường hợp thứ hai thực sự chỉ là một phần còn lại của quy tắc từ phải sang trái chung. Bài học được rút ra là nhận được trong thói quen sử dụng

char const * foo; 

thay vì phổ biến hơn

const char * foo; 

Nếu bạn muốn mọi thứ phức tạp hơn như "con trỏ đến hoạt động trở lại con trỏ đến int" để làm cho bất kỳ ý nghĩa nào.

T.B., và đây là một biggy (và lý do chính tôi thêm một câu trả lời):

tôi lưu ý rằng bạn bao gồm "đa luồng" như một thẻ. Bạn có nhận thấy rằng biến động không ít/không có gì tốt đối với đa luồng?

+3

dễ bay hơi phải được sử dụng khi chia sẻ các biến trạng thái giữa các luồng; đặc biệt cho các thuật toán khóa đồng thời miễn phí, nơi bạn sử dụng chờ đợi bận rộn. Ví dụ. thread 1 sẽ quay về hướng dẫn này: trong khi (rào cản); cho đến khi chuỗi 2 đặt rào cản = false. Nếu volatile không được sử dụng, sau đó code có thể bị bế tắc nếu trình biên dịch quyết định đọc giá trị rào cản từ thanh ghi cục bộ của nó thay vì bộ nhớ. – Mark

+1

Không - dễ bay hơi không chèn hàng rào bộ nhớ cho tất cả các trình biên dịch. Xem http://software.intel.com/en-us/blogs/2007/11/30/volatile-almost-useless-for-multi-threaded-programming/ –

+0

vâng, có trường hợp hiếm hoi có dễ bay hơi . Nhưng một khóa quay đòi hỏi một rào cản bộ nhớ, hoặc ít nhất dữ liệu được đọc sau khi 'rào cản == đúng' cần một rào cản bộ nhớ. Trên MSVC dễ bay hơi ngụ ý các rào cản, nhưng đó không phải là tiêu chuẩn. – tony

3

Có, điều đó sẽ hiệu quả. Không có gì khác biệt về bộ nhớ thực tế là volatile. Nó chỉ là một cách để nói cho trình biên dịch biết cách tương tác với bộ nhớ đó.

+0

Tôi không chắc chắn. Tôi đã bắt đầu một câu hỏi về usenet về nó: http://groups.google.com/group/comp.std.c++/browse_thread/thread/4af91d60c2a1af8a?pli=1 –

2

Tôi nghĩ rằng lần thứ hai tuyên bố con trỏ là dễ bay hơi, không phải là con trỏ trỏ đến. Để có được điều đó, tôi nghĩ rằng nó phải được

int * volatile foo; 

Cú pháp này là có thể chấp nhận để gcc, nhưng tôi đang gặp rắc rối convincing myself rằng nó bất cứ điều gì khác nhau.

Tôi đã tìm thấy sự khác biệt với gcc -O3 (tối ưu hóa đầy đủ). Đối với điều này (ngớ ngẩn) mã kiểm tra:

volatile int v [10]; 
int * volatile p; 

int main (void) 
{ 
     v [3] = p [2]; 
     p [3] = v [2]; 
     return 0; 
} 

Với volatile, và bỏ qua (x86) hướng dẫn mà không thay đổi:

movl p, %eax 
    movl 8(%eax), %eax 
    movl %eax, v+12 
    movl p, %edx 
    movl v+8, %eax 
    movl %eax, 12(%edx) 

Nếu không có biến động, nó bỏ qua tải lại p:

movl p, %eax 
    movl 8(%eax), %edx ; different since p being preserved 
    movl %edx, v+12 
    ; 'p' not reloaded here 
    movl v+8, %edx 
    movl %edx, 12(%eax) ; p reused 

Sau nhiều thử nghiệm khoa học khác cố gắng tìm sự khác biệt, tôi kết luận rằng không có sự khác biệt. volatile tắt tất cả các tối ưu hóa liên quan đến biến sẽ sử dụng lại giá trị được đặt sau đó. Ít nhất với x86 gcc (GCC) 4.1.2 20070925 (Mũ Đỏ 4.1.2-33). :-)

+1

không nên là một cách khác? với biến động, nó sẽ buộc tải lại p. – Mark

+0

Điều này trông giống như một lỗi trình biên dịch, thực sự. Với biến động, nó phải thực hiện hai lần đọc từ 'p', bởi vì chuẩn cho biết có hai lần đọc (giá trị để chuyển đổi rvalue). –

+0

Đúng. Tôi đã có nó ngược. Bây giờ nó đã được sửa. – wallyk

6
volatile int* foo; 

là cách để thực hiện. Bộ định dạng loại dễ bay hơi hoạt động giống như loại vòng loại loại const. Nếu bạn muốn có một con trỏ đến một mảng liên tục của nguyên bạn sẽ viết:

const int* foo; 

trong khi

int* const foo; 

là một con trỏ hằng thành một số nguyên mà chính nó có thể được thay đổi. dễ bay hơi hoạt động theo cùng một cách.

1

Cảm ơn rất nhiều để wallyk, tôi đã có thể đưa ra một số mã sử dụng phương pháp của mình để tạo ra một số lắp ráp để chứng minh cho bản thân mình sự khác biệt giữa các phương pháp con trỏ khác nhau.

sử dụng mã: và biên soạn với -03

int main (void) 
{ 
     while(p[2]); 
     return 0; 
} 

khi p chỉ đơn giản là khai báo là con trỏ, chúng tôi gặp khó khăn trong một vòng lặp mà không thể thoát ra được. Lưu ý rằng nếu đây là một chương trình đa luồng và một luồng khác đã viết p [2] = 0, thì chương trình sẽ thoát khỏi vòng lặp while và kết thúc bình thường.

int * p; 
============ 
LCFI1: 
     movq _p(%rip), %rax 
     movl 8(%rax), %eax 
     testl %eax, %eax 
     jne  L6    
     xorl %eax, %eax 
     leave 
     ret 
L6: 
     jmp  L6 

lưu ý rằng lệnh duy nhất cho L6 là đi tới L6.

==

khi p là con trỏ dễ bay hơi

int * volatile p; 
============== 
L3: 
     movq _p(%rip), %rax 
     movl 8(%rax), %eax 
     testl %eax, %eax 
     jne  L3 
     xorl %eax, %eax 
     leave 
     ret 

đây, con trỏ p được nạp lại mỗi vòng lặp và kết quả là mục mảng cũng được nạp lại. Tuy nhiên, điều này sẽ không chính xác nếu chúng tôi muốn một mảng các số nguyên không ổn định vì điều này sẽ có thể:

int* volatile p; 
.. 
.. 
int* j; 
j = &p[2]; 
while(j); 

và sẽ cho kết quả trong vòng lặp đó sẽ không thể chấm dứt trong một chương trình đa luồng.

==

cuối cùng, đây là giải pháp đúng như tony độc đáo giải thích.

int volatile * p; 
LCFI1: 
     movq _p(%rip), %rdx 
     addq $8, %rdx 
     .align 4,0x90 
L3: 
     movl (%rdx), %eax 
     testl %eax, %eax 
     jne  L3 
     leave 
     ret 

Trong trường hợp này địa chỉ của p [2] được giữ ở giá trị đăng ký và không được nạp từ bộ nhớ, nhưng giá trị của p [2] được nạp lại từ bộ nhớ trên mỗi chu kỳ vòng lặp.

cũng lưu ý rằng

int volatile * p; 
.. 
.. 
int* j; 
j = &p[2]; 
while(j); 

sẽ tạo ra một lỗi biên dịch.

Các vấn đề liên quan