6

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

プログラムの起動時に呼び出される関数は main と呼ばれます。[...]
int の戻り型でパラメータなしで定義する必要があります。
int main(void) { /* ... */ }

または 2 つのパラメーターを使用[...] :
int main(int argc, char *argv[]) { /* ... */ }

そして後にこう言います。

argc の値は非負でなければなりません。

  • おそらく「引数カウント」を意味するとして定義されるべきではない のはなぜですか?argcunsigned intargc
  • argcのインデックスとして使用する必要がありargvますか?

それで、C 標準が配列のインデックスの型について何か言っているかどうか疑問に思い始めました。署名されていますか?

6.5.2.1 配列の添え字:

式の 1 つは「<em>オブジェクト型へのポインタ」型を持ち、もう 1 つの式は整数型を持ち、結果は型「<em>type」を持ちます。

その署名については何も述べていません (または、見つけられませんでした)。負の配列インデックス ( array[-1]) を使用するコードをよく見かけますが、未定義の動作ではありませんか?

  • 配列のインデックスは符号なしにする必要がありますか?
4

4 に答える 4

6

main() の int の理由は歴史的なものです。言語が標準化されるずっと前から、常にそうでした。配列インデックスの要件は、それが配列の境界内 (または場合によっては、末尾の 1 つ後) にあることです。それ以外は定義されていないため、符号の有無は重要ではありません。

于 2010-03-14T14:13:18.673 に答える
3

1) main() argc 型について: 私見では、標準は非常に古い伝統 (30 年以上!) を続けています。 、「argc」が「符号なし」と定義されていてもCPUは文句を言いませんが、標準から外れています!)

2) 実装の大部分では、argv[argc] は正当であり、NULL に評価されます。実際、引数リストの最後を見つける別の方法は、argv[i] が NULL の場合に終了する 0 から argv を反復することです。

3) (pn) から p までのアドレス範囲が同じメモリ オブジェクトに属している限り、負の数を使用した配列/ポインター演算は有効です。あなたが持つことができるIE

char array[100];
char *p;

p = &array[50];
p += -30; /* Now p points to array[20]. */

結果のポインタは元のメモリ オブジェクト (「配列」) 内にとどまるため、このポインタ演算の使用は正当です。ほとんどのシステムでは、この規則に違反してポインター演算を使用してメモリ内を移動できますが、これは完全にシステムに依存するため、移植性がありません。

于 2010-03-14T14:31:20.463 に答える
3

一般に C では、「最小の驚きの原則」は、変数を符号なしにする正当な理由がない限り、変数を符号付きにすることが望ましいことを意味します。これは、符号付きと符号なしの値を混在させると、型昇格規則が予期しない結果につながる可能性があるためです。たとえば、argc符号なしの場合、この単純な比較は驚くべき結果につながります。

if (argc > -1)

(-1は に昇格されるunsigned intため、その値は に変換されますUINT_MAX。これはほぼ確実に より大きくなりますargc)。

于 2010-03-14T23:13:31.290 に答える
-2

1) Argc は引数の数ですが、正直なところ、どのようにプログラム名の前に引数を追加できますかargv[0]。というプログラムを想像してみてください。の符号付きの型であるにもかかわらず、つまり、「args1」を取得するようなものではないにもかかわらず、それは無意味であるとfoo単純に言うことはできません...args1 foo args2argcintargv[-1]

2)ランタイムが実行可能プログラム名を 0 番目のオフセットに詰め込むため、 argc が実際には引数ベクトル (したがって ' argvargv[0] ') へのインデックスではない理由。つまり、argcは 1 ずれます。

3) ポインター操作に関して、配列インデックスは、ポインターがあるメモリ ブロックの境界内にいる場合、配列添字を負として使用することは正当であり、配列添字はポインターのショートカットであり、それだけではありません。 、それらは交換可能です。

char v[100];
char *p = &v[0];

あなたはこれを行うことができます:

p[55] = 'a';

と同じです

*(p + 55) = 'a';

これを行うこともできます:

p = &v[55];

p[-10] = 'b' /* これは 'b' を 45 番目のオフセットに詰め込みます! */

と同じです

*(p - 10) = 'b';

また、境界の外にあるような方法で配列を使用および操作する場合 - これは未定義の動作であり、それを処理する方法についてランタイムの実装に依存します。おそらく、セグメンテーション違反またはプログラムのクラッシュ.. ..

4) *nix 環境では、 mainchar **endvpに 3 番目のパラメータが指定されているものもありますが、これも Microsoft の DOS/Windows の世界ではめったに使用されません。一部の *nix ランタイム実装では、有史以前の理由から、ランタイム経由で環境変数を渡すことができました。

于 2010-03-14T15:04:04.720 に答える