Sự khác biệt giữa
int x=7;
và
register int x=7;
?
Tôi đang sử dụng C ++.
Sự khác biệt giữa
int x=7;
và
register int x=7;
?
Tôi đang sử dụng C ++.
registercó ngữ nghĩa khác nhau giữa C và C ++.
register int a[1];với khai báo đó, bạn không thể lập chỉ mục mảng đó. Nếu bạn cố gắng, bạn sẽ làm được UB
Câu trả lời:
Trong C ++ như nó đã tồn tại vào năm 2010, bất kỳ chương trình nào hợp lệ sử dụng các từ khóa "auto" hoặc "register" sẽ giống về mặt ngữ nghĩa với một chương trình đã bị xóa các từ khóa đó (trừ khi chúng xuất hiện trong các macro được xâu chuỗi hoặc các ngữ cảnh tương tự khác). Theo nghĩa đó, các từ khóa vô dụng đối với các chương trình biên dịch đúng cách. Mặt khác, các từ khóa có thể hữu ích trong một số ngữ cảnh macro nhất định để đảm bảo rằng việc sử dụng macro không đúng cách sẽ gây ra lỗi thời gian biên dịch hơn là tạo ra mã không có thật.
Trong C ++ 11 và các phiên bản mới hơn của ngôn ngữ, autotừ khóa đã được tái sử dụng để hoạt động như một kiểu giả cho các đối tượng được khởi tạo, trình biên dịch sẽ tự động thay thế bằng kiểu của biểu thức khởi tạo. Do đó, trong C ++ 03, khai báo: auto int i=(unsigned char)5;tương đương với int i=5;khi được sử dụng trong ngữ cảnh khối và auto i=(unsigned char)5;là một vi phạm ràng buộc. Trong C ++ 11, auto int i=(unsigned char)5;đã trở thành một vi phạm ràng buộc trong khi auto i=(unsigned char)5;trở thành tương đương với auto unsigned char i=5;.
autokhông thể đơn giản bỏ qua từ khóa ... Có lẽ bạn có thể cập nhật câu trả lời của mình.
registerkhông được dùng nữa và sẽ có một đề xuất xóa nó cho C ++ 17.
autohiện được sử dụng để khấu trừ kiểu tự động Nhưng trước đó, nó được sử dụng để chỉ định rằng bạn muốn biến của mình được lưu trữ "tự động" ( do đó, tôi đoán trên ngăn xếp ) trái ngược với từ khóa register(có nghĩa là "đăng ký của bộ xử lý"):
register là một gợi ý cho trình biên dịch, khuyên nó lưu trữ biến đó trong thanh ghi bộ xử lý thay vì bộ nhớ (ví dụ: thay vì ngăn xếp).
Trình biên dịch có thể tuân theo gợi ý đó hoặc không.
Theo Herb Sutter trong "Từ khóa không phải (hoặc, nhận xét bằng tên khác)" :
Bộ chỉ định thanh ghi có cùng ngữ nghĩa với bộ chỉ định tự động ...
storage-class-specifiertrong những ngữ pháp và không có ngữ nghĩa xác định. Một trình biên dịch phù hợp có thể gây ra lỗi giống như Clang. Tuy nhiên, một số triển khai vẫn cho phép nó và bỏ qua nó (MSVC, ICC) hoặc sử dụng nó như một gợi ý tối ưu hóa (GCC). Xem open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Tuy nhiên, tôi đã sai ở một điểm: nó không được chấp nhận trong C ++ 11.
Theo Herb Sutter , register" chính xác có ý nghĩa như khoảng trắng " và không ảnh hưởng đến ngữ nghĩa của một chương trình C ++.
Gần như chắc chắn không có gì.
registerlà một gợi ý cho trình biên dịch mà bạn dự định sử dụng xnhiều và bạn nghĩ rằng nó nên được đặt trong một sổ đăng ký.
Tuy nhiên, các trình biên dịch hiện nay giỏi hơn nhiều trong việc xác định giá trị nào nên được đặt trong thanh ghi so với lập trình viên trung bình (hoặc thậm chí là chuyên gia), vì vậy các trình biên dịch chỉ cần bỏ qua từ khóa và làm những gì họ muốn.
registerkhông được chấp nhận trong C ++ 11. Nó không được sử dụng và được bảo lưu trong C ++ 17.
Các registertừ khóa là hữu ích cho:
Ví dụ về hệ thống hiệu quả, trong đó registertừ khóa được yêu cầu:
typedef unsigned long long Out;
volatile Out out,tmp;
Out register rax asm("rax");
asm volatile("rdtsc":"=A"(rax));
out=out*tmp+rax;
Nó đã không được chấp nhận kể từ C ++ 11 và không được sử dụng và được bảo lưu trong C ++ 17 .
registerlớp lưu trữ và vẫn được GCC hỗ trợ.
Hãy xem xét một trường hợp khi trình tối ưu hóa của trình biên dịch có hai biến và buộc phải đổ một biến vào ngăn xếp. Nó đã xảy ra rằng cả hai biến có cùng trọng số đối với trình biên dịch. Do không có sự khác biệt, trình biên dịch sẽ tùy ý đổ một trong các biến. Mặt khác, registertừ khóa cung cấp cho trình biên dịch một gợi ý biến nào sẽ được truy cập thường xuyên hơn. Nó tương tự như lệnh tìm nạp trước x86, nhưng dành cho trình tối ưu hóa trình biên dịch.
Rõ ràng registerlà các gợi ý tương tự như các gợi ý xác suất nhánh do người dùng cung cấp và có thể được suy ra từ các gợi ý xác suất này. Nếu trình biên dịch biết rằng một số nhánh được sử dụng thường xuyên, nó sẽ giữ các biến liên quan đến nhánh trong thanh ghi. Vì vậy, tôi khuyên bạn nên quan tâm nhiều hơn đến các gợi ý về nhánh, và hãy quên đi register. Lý tưởng nhất là trình biên dịch của bạn nên giao tiếp bằng cách nào đó với trình biên dịch và giúp bạn không phải nghĩ về những sắc thái như vậy.
Kể từ gcc 9.3, việc biên dịch bằng cách sử dụng -std=c++2a, register tạo ra một cảnh báo trình biên dịch, nhưng nó vẫn có tác dụng mong muốn và hoạt động giống như C registerkhi biên dịch mà không có cờ -O1 –- Ofast tối ưu hóa về câu trả lời này . Tuy nhiên, sử dụng clang ++ - 7 gây ra lỗi trình biên dịch. Vì vậy, đúng vậy, các registertối ưu hóa chỉ tạo ra sự khác biệt khi biên dịch tiêu chuẩn không có cờ -O tối ưu hóa, nhưng chúng là những tối ưu hóa cơ bản mà trình biên dịch sẽ tìm ra ngay cả với -O1.
Sự khác biệt duy nhất là trong C ++, bạn được phép lấy địa chỉ của biến đăng ký, điều đó có nghĩa là việc tối ưu hóa chỉ xảy ra nếu bạn không lấy địa chỉ của biến hoặc bí danh của nó (để tạo con trỏ) hoặc tham chiếu của nó trong mã (chỉ trên - O0, vì một tham chiếu cũng có địa chỉ, vì đó là một con trỏ const trên ngăn xếp , giống như một con trỏ có thể được tối ưu hóa khỏi ngăn xếp nếu biên dịch bằng -Ofast, ngoại trừ chúng sẽ không bao giờ xuất hiện trên ngăn xếp bằng cách sử dụng -Ofast, vì không giống như một con trỏ, chúng không thể được tạo volatilevà không thể lấy địa chỉ của chúng), nếu không nó sẽ hoạt động như bạn chưa sử dụng registervà giá trị sẽ được lưu trữ trên ngăn xếp.
Trên -O0, một sự khác biệt khác là const registertrên gcc C và gcc C ++ không hoạt động giống nhau. Trên gcc C, const registerhoạt động như thế nào register, vì phạm vi khối constkhông được tối ưu hóa trên gcc. Trên clang C, registerkhông có gì và chỉ constáp dụng tối ưu phạm vi khối. Trên gcc C, registertối ưu hóa được áp dụng nhưng constở phạm vi khối không có tối ưu hóa. Trên gcc C ++, cả tối ưu hóa phạm vi registervà constkhối kết hợp với nhau.
#include <stdio.h> //yes it's C code on C++
int main(void) {
const register int i = 3;
printf("%d", i);
return 0;
}
int i = 3;:
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 3
mov eax, DWORD PTR [rbp-4]
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
register int i = 3;:
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
push rbx
sub rsp, 8
mov ebx, 3
mov esi, ebx
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
mov rbx, QWORD PTR [rbp-8] //callee restoration
leave
ret
const int i = 3;
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 3 //still saves to stack
mov esi, 3 //immediate substitution
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
const register int i = 3;
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
mov esi, 3 //loads straight into esi saving rbx push/pop and extra indirection (because C++ block-scope const is always substituted immediately into the instruction)
mov edi, OFFSET FLAT:.LC0 // can't optimise away because printf only takes const char*
mov eax, 0 //zeroed: https://stackoverflow.com/a/6212755/7194773
call printf
mov eax, 0 //default return value of main is 0
pop rbp //nothing else pushed to stack -- more efficient than leave (rsp == rbp already)
ret
registeryêu cầu trình biên dịch biết 1) lưu trữ một biến cục bộ trong một thanh ghi đã lưu callee, trong trường hợp này rbx, và 2) tối ưu hóa việc ghi ngăn xếp nếu địa chỉ của biến không bao giờ được lấy . constyêu cầu trình biên dịch thay thế giá trị ngay lập tức (thay vì gán cho nó một thanh ghi hoặc tải nó từ bộ nhớ) và ghi biến cục bộ vào ngăn xếp như hành vi mặc định. const registerlà sự kết hợp của những tối ưu được khuyến khích này. Đây là đường nét mảnh mai như nó có được.
Ngoài ra, trên gcc C và C ++, registertự nó dường như tạo ra khoảng trống 16 byte ngẫu nhiên trên ngăn xếp cho cục bộ đầu tiên trên ngăn xếp, điều này không xảy ra với const register.
Biên dịch bằng -Ofast tuy nhiên; registercó 0 tác dụng tối ưu hóa bởi vì nếu nó có thể được đưa vào sổ đăng ký hoặc được thực hiện ngay lập tức, nó sẽ luôn như vậy và nếu không thể thì sẽ không; constvẫn tối ưu hóa tải trên C và C ++ nhưng chỉ ở phạm vi tệp ; volatilevẫn buộc các giá trị được lưu trữ và tải từ ngăn xếp.
.LC0:
.string "%d"
main:
//optimises out push and change of rbp
sub rsp, 8 //https://stackoverflow.com/a/40344912/7194773
mov esi, 3
mov edi, OFFSET FLAT:.LC0
xor eax, eax //xor 2 bytes vs 5 for mov eax, 0
call printf
xor eax, eax
add rsp, 8
ret