อะไรคือความแตกต่างระหว่าง
int x=7;
และ
register int x=7;
เหรอ?
ฉันใช้ C ++
อะไรคือความแตกต่างระหว่าง
int x=7;
และ
register int x=7;
เหรอ?
ฉันใช้ C ++
registerมีความหมายที่แตกต่างกันระหว่าง C และ C ++
register int a[1];ด้วยการประกาศดังกล่าวคุณไม่สามารถจัดทำดัชนีอาร์เรย์นั้นได้ ถ้าคุณลองคุณทำ UB
คำตอบ:
ใน C ++ ที่มีอยู่ในปี 2010 โปรแกรมใด ๆ ที่ถูกต้องซึ่งใช้คีย์เวิร์ด "auto" หรือ "register" จะมีความหมายเหมือนกันกับคำหลักที่นำออก (เว้นแต่ว่าจะปรากฏในมาโครแบบสตริงหรือบริบทอื่นที่คล้ายคลึงกัน) ในแง่นั้นคำหลักไม่มีประโยชน์สำหรับการรวบรวมโปรแกรมอย่างถูกต้อง ในทางกลับกันคำหลักอาจมีประโยชน์ในบริบทมาโครบางอย่างเพื่อให้แน่ใจว่าการใช้มาโครอย่างไม่เหมาะสมจะทำให้เกิดข้อผิดพลาดเวลาคอมไพล์แทนที่จะสร้างโค้ดปลอม
ในภาษา C ++ 11 และเวอร์ชันที่ใหม่กว่าautoคีย์เวิร์ดถูกกำหนดให้ทำหน้าที่เป็นประเภทหลอกสำหรับอ็อบเจ็กต์ที่ถูกกำหนดค่าเริ่มต้นซึ่งคอมไพลเลอร์จะแทนที่โดยอัตโนมัติด้วยประเภทของนิพจน์การเริ่มต้น ดังนั้นใน C ++ 03 การประกาศ: auto int i=(unsigned char)5;เทียบเท่ากับint i=5;เมื่อใช้ภายในบริบทบล็อกและauto i=(unsigned char)5;เป็นการละเมิดข้อ จำกัด ใน C ++ 11 auto int i=(unsigned char)5;กลายเป็นการละเมิดข้อ จำกัด ในขณะที่auto i=(unsigned char)5;เทียบเท่ากับauto unsigned char i=5;.
autoไม่สามารถละคำหลักไปได้เลย ... บางทีคุณอาจอัปเดตคำตอบของคุณได้
registerเลิกใช้งานแล้วและจะมีข้อเสนอให้ลบออกสำหรับ C ++ 17
autoตอนนี้ใช้สำหรับการหักประเภทอัตโนมัติ แต่ก่อนหน้านั้นมีการใช้เพื่อระบุว่าคุณต้องการให้เก็บตัวแปรของคุณ "โดยอัตโนมัติ" ( ดังนั้นฉันเดาว่าบนสแต็ก ) ตรงข้ามกับคีย์เวิร์ดregister(หมายถึง "การลงทะเบียนของโปรเซสเซอร์"):
register เป็นคำใบ้ให้กับคอมไพลเลอร์โดยแนะนำให้เก็บตัวแปรนั้นไว้ในรีจิสเตอร์โปรเซสเซอร์แทนหน่วยความจำ (ตัวอย่างเช่นแทนที่จะเป็นสแต็ก)
คอมไพเลอร์อาจปฏิบัติตามคำใบ้นั้นหรือไม่ก็ได้
อ้างอิงจาก Herb Sutter ใน"คำหลักที่ไม่ใช่ (หรือแสดงความคิดเห็นโดยใช้ชื่ออื่น)" :
ตัวระบุรีจิสเตอร์มีความหมายเหมือนกับตัวระบุอัตโนมัติ ...
storage-class-specifierในไวยากรณ์และไม่มีความหมายที่กำหนดไว้ คอมไพเลอร์ที่เข้ากันได้อาจทำให้เกิดข้อผิดพลาดเช่น Clang ได้ อย่างไรก็ตามการใช้งานบางอย่างยังคงอนุญาตและไม่สนใจ (MSVC, ICC) หรือใช้เป็นคำแนะนำในการเพิ่มประสิทธิภาพ (GCC) ดูopen-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html ฉันพูดผิดในประเด็นหนึ่ง: มันเลิกใช้งานใน C ++ 11
ตามSutter สมุนไพร , registerคือ " ตรงตามที่มีความหมายเป็นช่องว่าง " และมีผลกระทบต่อความหมายของโปรแกรมภาษา C ++ ที่
ด้วยคอมไพเลอร์ในปัจจุบันอาจจะไม่มีอะไร เป็นคำแนะนำดั้งเดิมในการวางตัวแปรในรีจิสเตอร์เพื่อการเข้าถึงที่เร็วขึ้น แต่คอมไพเลอร์ส่วนใหญ่ในปัจจุบันไม่สนใจคำใบ้นั้นและตัดสินใจด้วยตัวเอง
แทบไม่มีอะไรแน่นอน
registerเป็นคำแนะนำสำหรับคอมไพเลอร์ที่คุณวางแผนจะใช้xเป็นจำนวนมากและคุณคิดว่ามันควรจะอยู่ในรีจิสเตอร์
อย่างไรก็ตามขณะนี้คอมไพเลอร์สามารถกำหนดค่าที่ควรวางไว้ในรีจิสเตอร์ได้ดีกว่าค่าเฉลี่ยของโปรแกรมเมอร์ (หรือแม้แต่ผู้เชี่ยวชาญ) ดังนั้นคอมไพเลอร์จึงเพิกเฉยต่อคีย์เวิร์ดและทำในสิ่งที่ต้องการ
registerเลิกใช้งานใน C ++ 11 ไม่ได้ใช้และสงวนไว้ใน C ++ 17
registerคำหลักที่เป็นประโยชน์สำหรับ:
ตัวอย่างของระบบการผลิตที่registerจำเป็นต้องใช้คีย์เวิร์ด:
typedef unsigned long long Out;
volatile Out out,tmp;
Out register rax asm("rax");
asm volatile("rdtsc":"=A"(rax));
out=out*tmp+rax;
เลิกใช้งานแล้วตั้งแต่ C ++ 11 และไม่มีการใช้งานและสงวนไว้ใน C ++ 17
registerระบุคลาสพื้นที่เก็บข้อมูลและ GCC ยังรองรับ
ในฐานะของ GCC 9.3 รวบรวมโดยใช้-std=c++2a, registerผลิตเตือนคอมไพเลอร์ แต่ก็ยังคงมีผลที่ต้องการและพฤติกรรมเหมือนกันกับซีregisterเมื่อรวบรวมได้โดยไม่ต้อง -O1 - ธงเพิ่มประสิทธิภาพ Ofast ในส่วนที่เกี่ยวกับเรื่องนี้คำตอบ อย่างไรก็ตามการใช้ clang ++ - 7 ทำให้เกิดข้อผิดพลาดของคอมไพเลอร์ ใช่แล้วการregisterเพิ่มประสิทธิภาพจะสร้างความแตกต่างในการคอมไพล์มาตรฐานโดยไม่มีแฟล็ก -O การปรับให้เหมาะสม แต่เป็นการเพิ่มประสิทธิภาพพื้นฐานที่คอมไพเลอร์จะคิดออกแม้จะมี -O1 ก็ตาม
ข้อแตกต่างเพียงอย่างเดียวคือใน C ++ คุณได้รับอนุญาตให้ใช้ที่อยู่ของตัวแปร register ซึ่งหมายความว่าการเพิ่มประสิทธิภาพจะเกิดขึ้นก็ต่อเมื่อคุณไม่ใช้ที่อยู่ของตัวแปรหรือนามแฝง (เพื่อสร้างตัวชี้) หรือใช้อ้างอิง ของมันในโค้ด (เฉพาะบน - O0 เนื่องจากการอ้างอิงมีแอดเดรสด้วยเนื่องจากเป็นตัวชี้ const บนสแต็กซึ่งเช่นเดียวกับตัวชี้สามารถปรับให้เหมาะสมจากสแต็กหากคอมไพล์โดยใช้ -Ofast ยกเว้นว่าจะไม่ปรากฏ บนสแต็กโดยใช้ -Ofast เนื่องจากไม่เหมือนกับตัวชี้คือไม่สามารถสร้างได้volatileและไม่สามารถนำที่อยู่ได้) มิฉะนั้นจะทำงานเหมือนที่คุณไม่ได้ใช้registerและค่าจะถูกเก็บไว้ในสแต็ก
บน -O0 ข้อแตกต่างอีกประการหนึ่งคือconst registerใน gcc C และ gcc C ++ จะไม่ทำงานเหมือนกัน ใน gcc C const registerมีลักษณะการทำงานregisterเนื่องจาก block-scope consts ไม่ได้รับการปรับให้เหมาะสมกับ gcc ในเสียงดัง C registerไม่ทำอะไรเลยและใช้เฉพาะconstการเพิ่มประสิทธิภาพขอบเขตบล็อกเท่านั้น ใน gcc C จะregisterใช้การเพิ่มประสิทธิภาพ แต่constที่ขอบเขตบล็อกไม่มีการปรับให้เหมาะสม ใน gcc C ++ การเพิ่มประสิทธิภาพทั้งสองregisterและconstขอบเขตบล็อกจะรวมกัน
#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
registerบอกรวบรวมเพื่อ 1) เก็บตัวแปรท้องถิ่นใน callee บันทึกการลงทะเบียนในกรณีนี้rbxและ 2) การเพิ่มประสิทธิภาพการเขียนสแต็คว่าที่อยู่ของตัวแปรจะไม่ดำเนินการ constบอกให้คอมไพเลอร์แทนที่ค่าทันที (แทนที่จะกำหนดรีจิสเตอร์หรือโหลดจากหน่วยความจำ) และเขียนตัวแปรโลคัลลงในสแต็กเป็นลักษณะการทำงานเริ่มต้น const registerคือการรวมกันของการเพิ่มประสิทธิภาพที่กล้าหาญเหล่านี้ นี่คือเส้นที่บางเฉียบที่สุดเท่าที่จะทำได้
นอกจากนี้ใน gcc C และ C ++ registerดูเหมือนว่าจะสร้างช่องว่าง 16 ไบต์แบบสุ่มบนสแต็กสำหรับโลคัลแรกบนสแต็กซึ่งไม่ได้เกิดขึ้นกับconst register.
การรวบรวมโดยใช้ -Ofast อย่างไรก็ตาม registerมีเอฟเฟกต์การปรับให้เหมาะสมเป็น 0 เพราะถ้าสามารถใส่ลงทะเบียนหรือทำการลงทะเบียนได้ทันทีมันจะเป็นเสมอและถ้าไม่สามารถทำได้ constยังคงเพิ่มประสิทธิภาพการโหลดบน C และ C ++ แต่ในขอบเขตไฟล์เท่านั้น ; volatileยังคงบังคับให้จัดเก็บและโหลดค่าจากสแต็ก
.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
พิจารณากรณีที่เครื่องมือเพิ่มประสิทธิภาพของคอมไพเลอร์มีตัวแปรสองตัวและถูกบังคับให้หกตัวลงบนสแตก มันเกิดขึ้นที่ตัวแปรทั้งสองมีน้ำหนักเท่ากันกับคอมไพเลอร์ เนื่องจากไม่มีความแตกต่างคอมไพเลอร์จะทำตัวแปรตัวใดตัวหนึ่งโดยพลการ ในทางกลับกันไฟล์registerคำหลักจะช่วยให้คอมไพเลอร์ทราบว่าตัวแปรใดจะถูกเข้าถึงบ่อยขึ้น คล้ายกับคำสั่ง x86 prefetch แต่สำหรับเครื่องมือเพิ่มประสิทธิภาพคอมไพเลอร์
เห็นได้ชัดว่าregisterคำใบ้คล้ายกับคำแนะนำความน่าจะเป็นสาขาที่ผู้ใช้ให้มาและสามารถอนุมานได้จากคำใบ้ความน่าจะเป็น ถ้าคอมไพเลอร์รู้ว่ามีการใช้ branch บ่อยครั้งมันจะเก็บตัวแปรที่เกี่ยวข้องกับ branch ไว้ในรีจิสเตอร์ registerดังนั้นผมจึงขอแนะนำให้ดูแลเพิ่มเติมเกี่ยวกับคำแนะนำสาขาและลืมเกี่ยวกับ ตามหลักการแล้วผู้สร้างโปรไฟล์ของคุณควรสื่อสารกับคอมไพเลอร์และไม่ให้คุณคิดถึงความแตกต่างดังกล่าว