@GMan: ANSI C tidak mengizinkan untuk mengambil alamat dari objek register; pembatasan ini tidak berlaku untuk C ++
Brian R. Bondy
1
@ Brian: Hm, kamu benar. Ini hanya dalam catatan sekarang (yang mungkin akan diabaikan jika alamatnya diambil), tetapi tidak diamanatkan. Senang mendengarnya. (Yah, semacam.: P)
GManNickG
8
Pemberian suara untuk dibuka kembali registermemiliki semantik yang berbeda antara C dan C ++.
CB Bailey
3
sebagai konsekuensi dari ini, di C dimungkinkan untuk melarang konversi array-ke-pointer dengan membuat register array: register int a[1];dengan deklarasi itu, Anda tidak dapat mengindeks array itu. Jika Anda mencoba, Anda melakukan UB
Johannes Schaub - litb
2
Memang, saya memilih untuk membuka kembali. Saya memilih untuk menutup sebelum mengetahui ada perbedaan.
GManNickG
Jawaban:
24
Dalam C ++ seperti yang ada pada tahun 2010, program apa pun yang valid yang menggunakan kata kunci "auto" atau "register" akan identik secara semantik dengan kata kunci tersebut yang dihapus (kecuali jika program tersebut muncul dalam makro bersangkai atau konteks serupa lainnya). Dalam arti kata kunci tidak berguna untuk program kompilasi dengan benar. Di sisi lain, kata kunci mungkin berguna dalam konteks makro tertentu untuk memastikan bahwa penggunaan makro yang tidak tepat akan menyebabkan kesalahan waktu kompilasi daripada menghasilkan kode palsu.
Di C ++ 11 dan versi bahasa yang lebih baru, autokata kunci itu bertujuan ulang untuk bertindak sebagai pseudo-type untuk objek yang diinisialisasi, yang secara otomatis akan diganti oleh compiler dengan jenis ekspresi inisialisasi. Jadi, di C ++ 03, deklarasi: auto int i=(unsigned char)5;setara dengan int i=5;saat digunakan dalam konteks blok, dan auto i=(unsigned char)5;merupakan pelanggaran batasan. Di C ++ 11, auto int i=(unsigned char)5;menjadi pelanggaran kendala sementara auto i=(unsigned char)5;menjadi setara dengan auto unsigned char i=5;.
Jawaban ini tidak lagi benar, sejak tahun 2011, kata kunci autotidak dapat dihilangkan begitu saja ... Mungkin Anda dapat memperbarui jawaban Anda.
Walter
2
@Walter: Dapatkah Anda mengutip apa yang telah berubah? Saya belum mengikuti semua perubahan bahasa.
supercat
2
@supercat, ya, untuk saat ini, tetapi registertidak digunakan lagi dan akan ada proposal untuk menghapusnya untuk C ++ 17.
Jonathan Wakely
3
Menurut en.cppreference.com/w/cpp/language/auto , post C ++ 11, autosekarang digunakan untuk pengurangan tipe otomatis. Namun sebelumnya, ini digunakan untuk menentukan bahwa Anda ingin variabel Anda disimpan "secara otomatis" ( oleh karena itu pada tumpukan saya kira) sebagai lawan dari kata kunci register(artinya "register prosesor"):
Guillaume
96
register adalah petunjuk bagi kompilator, yang menyarankannya untuk menyimpan variabel itu dalam register prosesor dan bukan memori (misalnya, alih-alih tumpukan).
Kompilator mungkin atau mungkin tidak mengikuti petunjuk itu.
Sejak C ++ 17, ia tidak digunakan lagi, tidak digunakan dan dicadangkan.
ZachB
@ZachB, ini salah; register dicadangkan di C ++ 17 tetapi masih berfungsi dan berfungsi hampir sama dengan register C.
Lewis Kelsey
@LewisKelsey Ini tidak digunakan dan dicadangkan dalam spesifikasi C ++ 17; ini bukan salah satu dari storage-class-specifiertata bahasa dan tidak memiliki semantik yang ditentukan. Kompiler yang sesuai dapat menampilkan kesalahan seperti yang dilakukan Clang. Meskipun demikian, beberapa implementasi masih mengizinkannya dan mengabaikannya (MSVC, ICC) atau menggunakannya sebagai petunjuk pengoptimalan (GCC). Lihat open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Saya memang salah bicara pada satu hal: itu tidak digunakan lagi di C ++ 11.
Dengan penyusun hari ini, mungkin tidak ada. Pada awalnya adalah petunjuk untuk menempatkan variabel dalam register untuk akses yang lebih cepat, tetapi kebanyakan kompiler saat ini mengabaikan petunjuk itu dan memutuskan sendiri.
registeradalah petunjuk bagi kompiler bahwa Anda berencana untuk xbanyak menggunakannya , dan menurut Anda itu harus ditempatkan dalam register.
Namun, compiler sekarang jauh lebih baik dalam menentukan nilai apa yang harus ditempatkan di register daripada programmer rata-rata (atau bahkan ahli), jadi compiler mengabaikan kata kunci, dan melakukan apa yang mereka inginkan.
Dan saya akan menambahkan bahwa kata kunci 'register' hanya akan berguna pada mikrokontroler yang menjalankan program C ++ tunggal tanpa utas dan tanpa multi tasking. Program C ++ harus memiliki seluruh CPU untuk memastikan bahwa variabel 'register' tidak akan dipindahkan dari register CPU khusus.
Santiago Villafuerte
@SantiagoVillafuerte ingin menambahkannya mengedit jawaban?
ncomputers
Saya tidak yakin dengan jawaban saya ... meskipun kedengarannya masuk akal. Saya lebih suka meninggalkannya sebagai komentar sehingga orang lain menyetujuinya atau tidak.
Santiago Villafuerte
1
@SantiagoVillafuerte Ini sebenarnya tidak benar, dalam sistem multitasking ketika konteks beralih OS - bukan aplikasi - bertanggung jawab untuk menyimpan / memulihkan register. Karena Anda tidak beralih konteks setelah setiap instruksi CPU, memasukkan sesuatu ke dalam register sangatlah berarti. Jawaban lain di sini (bahwa penyusun sama sekali tidak peduli dengan pendapat Anda tentang alokasi register) lebih akurat.
Kubik
Contoh yang Anda tunjukkan sebenarnya menggunakan ekstensi Variabel Register Eksplisit GCC , yang berbeda dari registerpenentu kelas penyimpanan dan masih didukung oleh GCC.
ZachB
2
Mulai gcc 9.3, mengompilasi menggunakan -std=c++2a,register menghasilkan peringatan compiler, tetapi masih memiliki efek yang diinginkan dan berperilaku identik dengan C registerketika mengompilasi tanpa flag -O1 –- Optimasi Ofast sehubungan dengan jawaban ini . Namun, penggunaan clang ++ - 7 menyebabkan kesalahan kompiler. Jadi ya, registerpengoptimalan hanya membuat perbedaan pada kompilasi standar tanpa tanda pengoptimalan -O, tetapi pengoptimalan tersebut adalah pengoptimalan dasar yang akan diketahui oleh compiler bahkan dengan -O1.
Satu-satunya perbedaan adalah bahwa di C ++, Anda diizinkan untuk mengambil alamat dari variabel register yang berarti bahwa pengoptimalan hanya terjadi jika Anda tidak mengambil alamat variabel atau aliasnya (untuk membuat pointer) atau mengambil referensi di kode (hanya di - O0, karena referensi juga memiliki alamat, karena ini adalah pointer const di tumpukan , yang, seperti pointer dapat dioptimalkan dari tumpukan jika mengompilasi menggunakan -Ofast, kecuali mereka tidak akan pernah muncul pada stack menggunakan -Ofast, karena tidak seperti pointer, mereka tidak dapat dibuat volatiledan alamatnya tidak dapat diambil), jika tidak maka akan berperilaku seperti Anda tidak pernah menggunakannya register, dan nilainya akan disimpan di stack.
Pada -O0, perbedaan lainnya adalah const registerpada gcc C dan gcc C ++ tidak berperilaku sama. Di gcc C, const registerberperilaku seperti register, karena block-scope consts tidak dioptimalkan di gcc. Pada clang C, registertidak melakukan apa-apa dan hanya constpengoptimalan cakupan blok yang berlaku. Di gcc C, registerpengoptimalan diterapkan tetapi constdi block-scope tidak ada pengoptimalan. Di gcc C ++, pengoptimalan cakupan blok registerdan keduanya constdigabungkan.
#include<stdio.h> //yes it's C code on C++intmain(void){
constregisterint i = 3;
printf("%d", i);
return0;
}
.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
registermemberitahu compiler untuk 1) menyimpan variabel lokal dalam register yang disimpan callee, dalam hal ini rbx, dan 2) mengoptimalkan stack menulis jika alamat variabel tidak pernah diambil . constmemberi tahu compiler untuk segera mengganti nilai (alih-alih menetapkannya sebagai register atau memuatnya dari memori) dan menulis variabel lokal ke stack sebagai perilaku default. const registeradalah kombinasi dari pengoptimalan yang berani ini. Ini sangat tipis.
Selain itu, di gcc C dan C ++, registerdengan sendirinya tampaknya membuat celah 16 byte acak pada tumpukan untuk lokal pertama di tumpukan, yang tidak terjadi dengan const register.
Namun kompilasi menggunakan -Ofast; registermemiliki 0 efek optimasi karena jika bisa dimasukkan ke dalam register atau dibuat langsung, akan selalu begitu dan jika tidak bisa tidak akan; constmasih mengoptimalkan beban pada C dan C ++ tetapi hanya pada cakupan file ; volatilemasih memaksa nilai untuk disimpan dan dimuat dari tumpukan.
.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 printfxor eax, eax
add rsp, 8
ret
Pertimbangkan kasus ketika pengoptimal kompiler memiliki dua variabel dan dipaksa untuk menumpahkan satu variabel ke tumpukan. Kebetulan kedua variabel tersebut memiliki bobot yang sama untuk compiler. Mengingat tidak ada perbedaan, compiler akan secara sewenang-wenang menumpahkan salah satu variabel. Di sisi lain, registerkata kunci memberi kompiler petunjuk variabel mana yang akan lebih sering diakses. Ini mirip dengan instruksi prefetch x86, tetapi untuk pengoptimal compiler.
Jelas registerpetunjuk mirip dengan petunjuk probabilitas cabang yang diberikan pengguna, dan bisa disimpulkan dari petunjuk probabilitas ini. Jika kompilator mengetahui bahwa beberapa cabang sering diambil, itu akan menyimpan variabel terkait cabang dalam register. Jadi saya sarankan untuk lebih memperhatikan petunjuk cabang, dan melupakannya register. Idealnya profiler Anda harus berkomunikasi dengan kompiler dan menghindarkan Anda dari pemikiran tentang nuansa seperti itu.
register
memiliki semantik yang berbeda antara C dan C ++.register int a[1];
dengan deklarasi itu, Anda tidak dapat mengindeks array itu. Jika Anda mencoba, Anda melakukan UBJawaban:
Dalam C ++ seperti yang ada pada tahun 2010, program apa pun yang valid yang menggunakan kata kunci "auto" atau "register" akan identik secara semantik dengan kata kunci tersebut yang dihapus (kecuali jika program tersebut muncul dalam makro bersangkai atau konteks serupa lainnya). Dalam arti kata kunci tidak berguna untuk program kompilasi dengan benar. Di sisi lain, kata kunci mungkin berguna dalam konteks makro tertentu untuk memastikan bahwa penggunaan makro yang tidak tepat akan menyebabkan kesalahan waktu kompilasi daripada menghasilkan kode palsu.
Di C ++ 11 dan versi bahasa yang lebih baru,
auto
kata kunci itu bertujuan ulang untuk bertindak sebagai pseudo-type untuk objek yang diinisialisasi, yang secara otomatis akan diganti oleh compiler dengan jenis ekspresi inisialisasi. Jadi, di C ++ 03, deklarasi:auto int i=(unsigned char)5;
setara denganint i=5;
saat digunakan dalam konteks blok, danauto i=(unsigned char)5;
merupakan pelanggaran batasan. Di C ++ 11,auto int i=(unsigned char)5;
menjadi pelanggaran kendala sementaraauto i=(unsigned char)5;
menjadi setara denganauto unsigned char i=5;
.sumber
auto
tidak dapat dihilangkan begitu saja ... Mungkin Anda dapat memperbarui jawaban Anda.register
tidak digunakan lagi dan akan ada proposal untuk menghapusnya untuk C ++ 17.auto
sekarang digunakan untuk pengurangan tipe otomatis. Namun sebelumnya, ini digunakan untuk menentukan bahwa Anda ingin variabel Anda disimpan "secara otomatis" ( oleh karena itu pada tumpukan saya kira) sebagai lawan dari kata kunciregister
(artinya "register prosesor"):register
adalah petunjuk bagi kompilator, yang menyarankannya untuk menyimpan variabel itu dalam register prosesor dan bukan memori (misalnya, alih-alih tumpukan).Kompilator mungkin atau mungkin tidak mengikuti petunjuk itu.
Menurut Herb Sutter dalam "Kata Kunci yang Tidak (atau, Komentar oleh Nama Lain)" :
sumber
storage-class-specifier
tata bahasa dan tidak memiliki semantik yang ditentukan. Kompiler yang sesuai dapat menampilkan kesalahan seperti yang dilakukan Clang. Meskipun demikian, beberapa implementasi masih mengizinkannya dan mengabaikannya (MSVC, ICC) atau menggunakannya sebagai petunjuk pengoptimalan (GCC). Lihat open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0001r1.html . Saya memang salah bicara pada satu hal: itu tidak digunakan lagi di C ++ 11.Menurut Herb Sutter ,
register
" sama berartinya dengan spasi " dan tidak berpengaruh pada semantik program C ++.sumber
Dengan penyusun hari ini, mungkin tidak ada. Pada awalnya adalah petunjuk untuk menempatkan variabel dalam register untuk akses yang lebih cepat, tetapi kebanyakan kompiler saat ini mengabaikan petunjuk itu dan memutuskan sendiri.
sumber
Hampir pasti tidak ada.
register
adalah petunjuk bagi kompiler bahwa Anda berencana untukx
banyak menggunakannya , dan menurut Anda itu harus ditempatkan dalam register.Namun, compiler sekarang jauh lebih baik dalam menentukan nilai apa yang harus ditempatkan di register daripada programmer rata-rata (atau bahkan ahli), jadi compiler mengabaikan kata kunci, dan melakukan apa yang mereka inginkan.
sumber
register
tidak digunakan lagi di C ++ 11. Ini tidak digunakan dan dicadangkan dalam C ++ 17.Sumber: http://en.cppreference.com/w/cpp/keyword/register
sumber
Kata
register
kunci itu berguna untuk:Contoh sistem produktif, di mana
register
kata kunci diperlukan:typedef unsigned long long Out; volatile Out out,tmp; Out register rax asm("rax"); asm volatile("rdtsc":"=A"(rax)); out=out*tmp+rax;
Ini sudah tidak digunakan lagi sejak C ++ 11 dan tidak digunakan serta dicadangkan di C ++ 17 .
sumber
register
penentu kelas penyimpanan dan masih didukung oleh GCC.Mulai gcc 9.3, mengompilasi menggunakan
-std=c++2a
,register
menghasilkan peringatan compiler, tetapi masih memiliki efek yang diinginkan dan berperilaku identik dengan Cregister
ketika mengompilasi tanpa flag -O1 –- Optimasi Ofast sehubungan dengan jawaban ini . Namun, penggunaan clang ++ - 7 menyebabkan kesalahan kompiler. Jadi ya,register
pengoptimalan hanya membuat perbedaan pada kompilasi standar tanpa tanda pengoptimalan -O, tetapi pengoptimalan tersebut adalah pengoptimalan dasar yang akan diketahui oleh compiler bahkan dengan -O1.Satu-satunya perbedaan adalah bahwa di C ++, Anda diizinkan untuk mengambil alamat dari variabel register yang berarti bahwa pengoptimalan hanya terjadi jika Anda tidak mengambil alamat variabel atau aliasnya (untuk membuat pointer) atau mengambil referensi di kode (hanya di - O0, karena referensi juga memiliki alamat, karena ini adalah pointer const di tumpukan , yang, seperti pointer dapat dioptimalkan dari tumpukan jika mengompilasi menggunakan -Ofast, kecuali mereka tidak akan pernah muncul pada stack menggunakan -Ofast, karena tidak seperti pointer, mereka tidak dapat dibuat
volatile
dan alamatnya tidak dapat diambil), jika tidak maka akan berperilaku seperti Anda tidak pernah menggunakannyaregister
, dan nilainya akan disimpan di stack.Pada -O0, perbedaan lainnya adalah
const register
pada gcc C dan gcc C ++ tidak berperilaku sama. Di gcc C,const register
berperilaku sepertiregister
, karena block-scopeconst
s tidak dioptimalkan di gcc. Pada clang C,register
tidak melakukan apa-apa dan hanyaconst
pengoptimalan cakupan blok yang berlaku. Di gcc C,register
pengoptimalan diterapkan tetapiconst
di block-scope tidak ada pengoptimalan. Di gcc C ++, pengoptimalan cakupan blokregister
dan keduanyaconst
digabungkan.#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
memberitahu compiler untuk 1) menyimpan variabel lokal dalam register yang disimpan callee, dalam hal inirbx
, dan 2) mengoptimalkan stack menulis jika alamat variabel tidak pernah diambil .const
memberi tahu compiler untuk segera mengganti nilai (alih-alih menetapkannya sebagai register atau memuatnya dari memori) dan menulis variabel lokal ke stack sebagai perilaku default.const register
adalah kombinasi dari pengoptimalan yang berani ini. Ini sangat tipis.Selain itu, di gcc C dan C ++,
register
dengan sendirinya tampaknya membuat celah 16 byte acak pada tumpukan untuk lokal pertama di tumpukan, yang tidak terjadi denganconst register
.Namun kompilasi menggunakan -Ofast;
register
memiliki 0 efek optimasi karena jika bisa dimasukkan ke dalam register atau dibuat langsung, akan selalu begitu dan jika tidak bisa tidak akan;const
masih mengoptimalkan beban pada C dan C ++ tetapi hanya pada cakupan file ;volatile
masih memaksa nilai untuk disimpan dan dimuat dari tumpukan..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
sumber
Pertimbangkan kasus ketika pengoptimal kompiler memiliki dua variabel dan dipaksa untuk menumpahkan satu variabel ke tumpukan. Kebetulan kedua variabel tersebut memiliki bobot yang sama untuk compiler. Mengingat tidak ada perbedaan, compiler akan secara sewenang-wenang menumpahkan salah satu variabel. Di sisi lain,
register
kata kunci memberi kompiler petunjuk variabel mana yang akan lebih sering diakses. Ini mirip dengan instruksi prefetch x86, tetapi untuk pengoptimal compiler.Jelas
register
petunjuk mirip dengan petunjuk probabilitas cabang yang diberikan pengguna, dan bisa disimpulkan dari petunjuk probabilitas ini. Jika kompilator mengetahui bahwa beberapa cabang sering diambil, itu akan menyimpan variabel terkait cabang dalam register. Jadi saya sarankan untuk lebih memperhatikan petunjuk cabang, dan melupakannyaregister
. Idealnya profiler Anda harus berkomunikasi dengan kompiler dan menghindarkan Anda dari pemikiran tentang nuansa seperti itu.sumber