Daftarkan kata kunci di C ++

89

Apa perbedaan antara

int x=7;

dan

register int x=7;

?

Saya menggunakan C ++.

dato datuashvili
sumber
8
@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;.

supercat
sumber
22
Contoh bit terakhir semoga bermanfaat.
Dennis Zickefoose
14
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.

Menurut Herb Sutter dalam "Kata Kunci yang Tidak (atau, Komentar oleh Nama Lain)" :

Penentu register memiliki semantik yang sama dengan penentu otomatis ...

Tom
sumber
2
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.
ZachB
25

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.

KeithB
sumber
9

Hampir pasti tidak ada.

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.

James Curran
sumber
7

Kata registerkunci itu berguna untuk:

  • Perakitan inline.
  • Pemrograman C / C ++ ahli.
  • Deklarasi variabel yang dapat di-cache.

Contoh sistem produktif, di mana registerkata 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 .

nkomputer
sumber
2
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++
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

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 printf
  xor eax, eax
  add rsp, 8
  ret
Lewis Kelsey
sumber
1

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.

SmugLispWeenie
sumber