私はconst
Cコードでどのように使用すべきかを理解しようとしています。最初はあまり気にしませんでしたが、const
全体でかなりの数の使用例を見ました。私は努力して戻って、宗教的に適切な変数を作成する必要がありますconst
か?それとも私は自分の時間を無駄にしているだけですか?
特に関数呼び出しで、人間とコンパイラの両方で、変更が予想される変数を読みやすくなると思います。他に重要な点がありませんか?
const
は入力されますが、#define
マクロは入力されません。
const
Cブロックによってスコープが設定#define
され、ファイル(またはより厳密にはコンパイル単位)に適用されます。
const
パラメータの受け渡しで最も役立ちます。ポインタをconst
使用したプロトタイプで使用されている場合は、関数が変更しないため、配列または構造体を渡しても安全であることがわかります。いいえconst
、できます。
などの定義をstrcpy()
見ると、私が何を意味するかがわかります。最初に関数プロトタイプに「const-ness」を適用します。レトロフィットconst
は「多くの作業」ほど難しくはありません(ただし、時間単位で支払いを受ければ問題ありません)。
また、考慮してください:
const char *s = "Hello World";
char *s = "Hello World";
どちらが正しいのか、そしてその理由は?
Cでconstキーワードを最もよく使用するにはどうすればよいですか?
「読み取り専用」const
にしたい場合に使用します。とても簡単です:)
使用const
することは良い習慣であるだけでなく、コードの読みやすさと理解しやすさを改善し、いくつかの一般的なエラーを防ぐのに役立ちます。必要に応じて、必ずconstを使用してください。
定数を変更しようとしたときにコンパイラエラーが発生し、定数を非定数パラメータとして渡すことでコンパイラガードとして機能するほか、コンパイラは値が変更されないことを認識して特定の最適化を実行できるため、次のことが可能になります。値は変更されないため、値をキャッシュし、メモリから新しく読み取る必要はありません。また、値をコードですぐに置き換えることができます。
C const
const
基本的にregister
は反対でvolatile
あり、使用volatile
すると、ファイルとブロックスコープでのconst最適化、およびブロックスコープでの最適化がオーバーライドされますregister
。constはgccC-O0のブロックスコープでCに対して何も行わず、-O1以降では冗長であるため、同じ出力を生成します。したがって、最適化のみがconst register
-O0で適用され、-O1以降では冗長になります。register
register
#include<stdio.h>
int main() {
const int i = 1;
printf("%d", i);
}
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 1
mov eax, DWORD PTR [rbp-4] //load from stack isn't eliminated for block-scope consts on gcc C unlike on gcc C++ and clang C, even though value will be the same
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
leave
ret
この例では、-O0 、、を使用すると、const
すべて同じコードが生成されますが、cfが異なるだけです。volatile
auto
register
#include<stdio.h>
const int i = 1;
int main() {
printf("%d", i);
}
i:
.long 1
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
mov eax, DWORD PTR i[rip] //load from memory
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
pop rbp
ret
const int i = 1;
代わりに:
i:
.long 1
.LC0:
.string "%d"
main:
push rbp
mov rbp, rsp
mov eax, 1 //saves load from memory, now immediate
mov esi, eax
mov edi, OFFSET FLAT:.LC0
mov eax, 0
call printf
mov eax, 0
pop rbp
ret
C ++ const
#include <iostream>
int main() {
int i = 1;
std::cout << i;
}
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 1 //stores on stack
mov eax, DWORD PTR [rbp-4] //loads the value stored on the stack
mov esi, eax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov eax, 0
leave
ret
#include <iostream>
int main() {
const int i = 1;
std::cout << i;
}
main:
push rbp
mov rbp, rsp
sub rsp, 16
mov DWORD PTR [rbp-4], 1 //stores it on the stack
mov esi, 1 //but saves a load from memory here, unlike on C
//'register' would skip this store on the stack altogether
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov eax, 0
leave
ret
#include <iostream>
int i = 1;
int main() {
std::cout << i;
}
i:
.long 1
main:
push rbp
mov rbp, rsp
mov eax, DWORD PTR i[rip] //load from memory
mov esi, eax
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov eax, 0
pop rbp
ret
#include <iostream>
const int i = 1;
int main() {
std::cout << i;
}
main:
push rbp
mov rbp, rsp
mov esi, 1 //eliminated load from memory, now immediate
mov edi, OFFSET FLAT:_ZSt4cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
mov eax, 0
pop rbp
ret
C ++には、aconst
が初期化されていない場合(ファイルスコープとブロックスコープの両方)にコンパイラエラーを生成するという追加の制限があります。const
また、C++のデフォルトとして内部リンケージがあります。volatile
それでもオーバーライドconst
しますregister
がconst register
、C++で両方の最適化を組み合わせます。
上記のすべてのコードはデフォルトの暗黙的な-O0を使用してコンパイルされますが、-Ofastを使用してコンパイルすると、const
驚くべきことに、ファイルスコープのclangまたはgccのCまたはC++では冗長ではありませんconsts
。const
ファイルスコープ変数がコードで変更されていない場合でも、使用されない限り、メモリからのロードは最適化されません。https://godbolt.org/z/PhDdxk。