Cで変数にconstキーワードを使用するタイミングと目的は何ですか?


58

取得中に私のコードはここに見直さ使用しての問題constのキーワードを思い付きました。変数の読み取り専用動作を実装するために使用されることを理解しています。

私は、さまざまな状況が役に立つときに混乱する。

  • 関数のプロトタイプを明確にするために使用すべきですか?
  • コード開発中のセキュリティ対策として使用する必要がありますか?
  • 実行時定数を宣言するために、さまざまな関数のスコープで使用する必要がありますか?
  • それはまったく使用されるべきですか?

これらの質問は、私が直面している混乱のほんの一例です。一般的な混乱は

  • ときにする必要がありますconstCプログラミングで使用するキーワード?
  • Cでこのキーワードを使用することで得られるさまざまなタイプの利点は何ですか?
  • constキーワードを使用することの短所はありますか?


私の質問の詳細にあるこれらすべての質問のために、この質問は広すぎるかもしれないと指摘されています。これらの質問は、主な質問に関する混乱を明確にするためだけのものであることを明確にしたかっただけです。

Cで変数にconstキーワードを使用するタイミングと目的は何ですか?

言い換えることもできます

const同じ長所と短所を備えたC` でのキーワードの適切な使用。


投票を終了する者のために、なぜそれがカテゴリーに分類されないか説明してくださいSpecific issues with software development。私は非常に具体的です。
アシームBansal

同じ投稿で複数の質問をしたので、あなたの質問は
ロバートハーベイ

1
@RobertHarveyこれらの質問はすべて、私の混乱を説明するためのものです。私の唯一の質問は、この質問のタイトルのままです。それに対する良い答えは、これらの関連する質問のすべてをカバーするでしょう。だから、残りますspecific issueconst同じ長所と短所を備えたC` でのキーワードの適切な使用。
アシームBansal

@RobertHarveyそれは今より良いですか?
アシームBansal

回答:


64

コードを確認するとき、次のルールを適用します。

  • const関数が指すデータを変更(または解放)しない場合、参照によって渡される関数パラメーターに常に使用します。

    int find(const int *data, size_t size, int value);
  • constそれ以外の場合は#defineまたは列挙を使用して定義される定数に常に使用します。コンパイラーは、結果として読み取り専用メモリー(ROM)内のデータを見つけることができます(ただし、組み込みシステムでは、この目的にはリンカーがより良いツールであることがよくあります)。

    const double PI = 3.14;
  • valueによって渡されるパラメーターの関数プロトタイプで constを使用しないでください。それは意味を持たないため、単なる「ノイズ」です。

    // don't add const to 'value' or 'size'
    int find(const int *data, size_t size, const int value); 
    
  • 必要に応じて、const volatileプログラムでは変更できないが、変更される可能性がある場所で使用します。ハードウェアレジスタは、ここでの典型的な使用例です。たとえば、デバイスの状態を反映するステータスレジスタです。

    const volatile int32_t *DEVICE_STATUS =  (int32_t*) 0x100;

他の用途はオプションです。たとえば、関数実装内の関数へのパラメーターはconstとしてマークできます。

// 'value' and 'size can be marked as const here
int find(const int *data, const size_t size, const int value)  
{
     ... etc

または取得され、その後変更されない値または計算を返す関数:

char *repeat_str(const char *str, size_t n) 
{
    const size_t len = strlen(str);
    const size_t buf_size = 1 + (len * n);
    char *buf = malloc(buf_size);
    ...

これらの使用はconst、変数を変更しないことを示しています。変数の保存方法や場所は変更されません。もちろん、コンパイラーは変数が変更されていないことconstを確認できますが、追加することでそれを強制できます。これは読者を助け、ある程度の安全性を追加することができます(ただし、関数が大きくて複雑で、これが大きな違いを生む場合は、おそらく他の問題があります)。編集-例 ネストされたループと多くの長いまたは同様の変数名を持つ200行の密にコード化された関数で、特定の変数が決して変わらないことを知っていると、わかりやすくなります。このような機能は、設計が不十分であるか、メンテナンスされています。


の問題const。おそらく「中毒」という言葉を聞くでしょう。これはconst、関数パラメーターに追加すると「安定性」が伝播する場合に発生します。

編集-constポイズニング:たとえば関数内:

int function_a(char * str, int n)
{
    ...
    function_b(str);
    ...
}

に変更strする場合はconst、それfuction_bconst。そして、そうであればfunction_b合格strとにfunction_cあなたはそれが多くの別々のファイル/モジュールに伝播する場合、これは痛みを伴う可能性が想像できるようになど、。変更できない関数(システムライブラリなど)に伝播する場合、キャストが必要になります。その constため、既存のコードを振り回すことは、おそらく問題を求めています。ただし、新しいコードではconst、必要に応じて一貫して修飾することをお勧めします。

よりconstin な問題は、元の言語ではなかったことです。アドオンとしては完全に適合しません。開始には、2つの意味があります(上記のルールのように、「これを変更しません」と「これは変更できません」を意味します)。しかし、それ以上に危険な場合があります。たとえば、このコードをコンパイルして実行すると、(コンパイラ/オプションに応じて)実行するとクラッシュする可能性があります。

const char str[] = "hello world\n";
char *s = strchr(str, '\n');
*s = '\0';

strchrchar*not aを返しますconst char*。呼び出しパラメーターである constため、呼び出しパラメーターをにキャストする必要がありますchar*。そして、この場合、実際の読み取り専用ストレージプロパティを捨てます。編集:-これは一般的に読み取り専用メモリ内の変数に適用されます。「ROM」とは、物理的なROMだけでなく、一般的なOSで実行されるプログラムのコードセクションで発生する書き込み保護されたメモリを意味します。

多くの標準ライブラリ関数は同じように動作するため、注意してください。実際の定数(つまり、ROMに格納されている)がある場合、その定数を失わないように注意する必要があります。


実際には2つの意味はありません。あなたが言うように、それは単に「私はこれを変えない」という意味です。たとえば、const volatile変数を持つことは完全に有効です。
確実に

2
これは関数パラメーターにも当てはまりますが、たとえばファイルスコープの定数の場合、const変数は実際には読み取り専用です。const「これ変更できません」と表示されます。const volatile一方、A は「これ変更できませんが、変更される可能性があります」と言います。私は答えにvolatilesの言及を追加します。
ウィリアムモリス

私はあなたのコメントに異議を唱えません、ただそれをコンパイラーにROMに入れるという命令だという考えだけです。これは、未定義の動作に対する何らかのランタイムガード(つまり、何らかの方法でconstを変更する)を意味し、「なぜこのconst変数を非constポインターで変更できるのか」などの誤解につながる可能性があります!)。全体的に私はこの答えが好きです:)
確実に

あなたは正しいです、「ROMにこれを置く」は不正確です(ただし、簡潔に:-)-「これは変更できません」に変更します。これはより正確です。コメントしてくれてありがとう。
ウィリアムモリス

これは素晴らしい概要です。const読み取り専用データデータの読み取り専用ビューの両方を宣言するように言いたいのですが、違いは「これは変更できません」と「変更できません」です。
ジョンパーディ

8

一般に、プログラミング言語では使用が推奨されているconstか、同等の修飾子

  • 発信者に渡されたものが変更されないことを明確にすることができます
  • コンパイラーはパラメーターを変更できる場合にのみ関連する特定のものを省略することができることをコンパイラーが特定しているため、潜在的な速度の改善
  • 誤って値を変更する自分からの保護

1

はい、それは基本的にTheLQの答えです。

プログラマーのセキュリティ対策であるため、変数を変更せず、変数を変更する可能性のある関数を呼び出さないでください。配列または構造体では、const指定子はその内容の値が変更されないことを示し、コンパイラーでも変更することはできません。ただし、キャストするだけで変数の値を簡単に変更できます。

私が普段目にしているものでは、主にコードに定数値を追加し、特定の関数を呼び出しても配列または構造が変更されないことを示すために使用されます。この最後の部分は重要です。なぜなら、配列または構造を変更する関数を呼び出すとき、元のバージョンを保持したい場合があるため、変数のコピーを作成して、それを関数に渡すためです。そうでない場合は、コピーを必要としないのは明らかです。たとえば、変更することができます。

int foo(Structure s);

int foo(const Structure * s);

コピーのオーバーヘッドが発生しない。

付け加えると、Cにはconst指定子を持つ特定のルールがあることに注意してください。例えば、

int b = 1;
const int * a = &b;

とは異なります

int b = 1;
int * const a = &b;

最初のコードでは変更できません。2番目の場合、ポインターは一定ですが、その内容はそうではないため、コンパイラーは* a = 3;コンパイラー・エラーなしで言うことができますがa、別のものへの参照にすることはできません。


0

TheLQの声明に同意して:

プログラマーのチームと協力するとき、宣言することconstは、上記の変数を変更してはならないこと、または単に大規模なプロジェクトで自分自身に思い出させるためであることを示す良い方法です。その意味で有用であり、多くの頭痛の種を救うことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.