19
int main()
{
    char a[7] = "Network";
    return 0;
}

Cの文字列リテラルは、内部的にヌル文字で終了します。したがって、文字列リテラルの実際の長さは 8 であり、配列Networkに収まらないため、上記のコードはコンパイル エラーになるはずです。char[7]

ただし、 Ubuntu のgcc (を使用しても-Wall) は、エラーや警告なしでこのコードをコンパイルします。gcc がこれを許可し、コンパイル エラーとしてフラグを立てないのはなぜですか?

gcc は、char 配列のサイズが文字列リテラルより小さい場合にのみ警告を発します (まだエラーは発生しません!)。たとえば、次のように警告します。

char a[6] = "Network";

[関連] Visual C++ 2012でコンパイル エラーが発生するchar a[7]:

1>d:\main.cpp(3): error C2117: 'a' : array bounds overflow
1> d:\main.cpp(3) : see declaration of 'a'
4

4 に答える 4

31

それより大きい文字列リテラルで char 配列を初期化することは、C では問題ありませんが、C++ では正しくありません。これは、gcc と VC++ の動作の違いを説明しています。

VC++ で C ファイルと同じものをコンパイルした場合、エラーは発生しません。また、g++ で C++ ファイルとしてコンパイルすると、エラーが発生します。

C標準は次のように述べています。

文字型の配列は、文字列リテラルまたは UTF-8 文字列リテラルで初期化することができ、オプションで中括弧で囲みます。文字列リテラルの連続するバイト (空きがある場合、または配列のサイズが不明な場合は、終端の null 文字を含む) は、配列の要素を初期化します。

[...]

例 8

宣言

char s[] = "abc", t[3] = "abc";

''plain'' char 配列オブジェクトsを定義し、tその要素は文字列リテラルで初期化されます。この宣言は、

char s[] = { 'a', 'b', 'c', '\0' },
     t[] = { 'a', 'b', 'c' };

( C11 ドラフト標準のセクション 6.7.9 、最終標準の実際の文言は異なる場合があります。)

これは、配列にスペースがない場合に終了文字を削除するのが完全に正しいことを意味します。意外かもしれませんが、これは言語が本来あるべき機能であり、(少なくとも私にとっては) よく知られている機能です。

それどころか、C++ 標準は次のように述べています。

配列要素よりも多くの初期化子があってはなりません。

例:

 char cv[4] = "asdf"; // error

暗黙の末尾 '\0' のためのスペースがないため、形式が正しくありません。

(C++ 2011 ドラフトn3242の 8.5.2 。)

于 2012-11-21T10:12:42.210 に答える
3

巻き戻しの答えは、これについて警告しない理由を説明gccしていますが、それについて何ができるかは述べていません。

gcc-Wc++-compat警告オプションは、この特定の問題を次のメッセージで検出します。

foo.c: In function ‘main’:
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat]

gccこれが、この問題について警告する唯一のオプションです。の man ページから警告オプションをすばやく grep する短いスクリプトをgcc作成し、それぞれを使用してコンパイルを試み、エラーが発生するかどうかを確認できます。

$ time for F in $(man gcc | grep -o -- '-W[^= ]*')
    do if gcc -c "${F}" foo.c |& grep :3 >& /dev/null; then
         echo "${F}"; gcc -c "${F}" foo.c
    fi
  done
man gcc | grep -o -- '-W[^= ]*')
man gcc | grep -o -- '-W[^= ]*'
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wc++-compat
foo.c: In function ‘main’:
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused-variable
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused-variable
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused-variable
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wunused
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]
-Wc++-compat
foo.c: In function ‘main’:
foo.c:3:17: warning: initializer-string for array chars is too long for C++ [-Wc++-compat]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wall
foo.c: In function ‘main’:
foo.c:3:10: warning: unused variable ‘a’ [-Wunused-variable]
-Wtraditional
foo.c: In function ‘main’:
foo.c:3:5: warning: traditional C rejects automatic aggregate initialization [-Wtraditional]

real    0m26.399s
user    0m5.128s
sys 0m15.329s

一般に、 のようなlintのようなツールsplint は、あらゆる種類の潜在的な問題について警告します。この場合、次のように表示されます。

foo.c:3:17: String literal with 8 characters is assigned to char [7] (no room
               for null terminator): "Network"
  A string literal is assigned to a char array that is not big enough to hold
  the null terminator. (Use -stringliteralnoroom to inhibit warning)
foo.c:3:10: Variable a declared but not used
于 2012-11-22T16:58:11.937 に答える
3

C と Unix の初期には、メモリとディスクが小さかったため、文字列の末尾に NUL バイトを格納しないという手法が実際に使用されていました。文字列変数の長さが 7 文字の場合、7 文字の文字列を格納できます。最大長は 7 であるため、ターミネータ文字がなくても文字列がそこで終わることがわかります。これが、strncpyがそのように機能する理由です。

于 2012-11-21T10:20:59.957 に答える
2

通常、文字列リテラルを宣言する好ましい方法は次のとおりです。

   char a[] = "Network";
   printf("size of a: %d\n", sizeof a); // The compiler 'knows' the size of a.
   // this prints '8'

コンパイラにそれを理解させます。配列のサイズを手動で指定して、文字列リテラルの実際の長さと同期させるのは面倒です...

したがって、GCC は警告以外のことは何も気にしないと思います。

于 2012-11-21T10:15:53.747 に答える