2

先日、私はこのコードを見つけた本を読んでいました:

char *words[][40] = {"Goat", "A herbivore",
                     "Dog", "An omnivore",
                     "Fox", "A carnivore",
                     "Bear", "An omnivore"
                     "", ""};

そして本は、「注意してください、リストは2つのヌルで終了する必要があります」と述べました. しかし、null なしでこのコードをコンパイルすると、コンパイルして目的どおりに動作しました。ヌルが役立つかどうかを説明してください。私は C(++) にまったく慣れていないので、詳しく説明してください。

4

3 に答える 3

0

投稿したコードはコンパイルされません。本から正確にコピーしたと確信していますか?

明らかな不足しているコンマを追加すると、最終的には次のようになります

char* words[1][40] = 
{
    "Goat"
    "A herbivore",
    "Dog"
    "An omnivore",
    "Fox"
    "A carnivore",
    "Bear"
    "An omnivore",
    "",
    "",
    NULL,
    //  this last line repeated 30 times...
};

それが意図されたものであるかどうかも疑わしい。(結果として、2 個だけでなく 30 個の null で終了するリストが生成されます。)

意味されていたのは、もなしだったと思い*ます:

char words[][2][40] =
{
    "Goat", "A herbivore",
    "Dog", "An omnivore",
    "Fox", "A carnivore",
    "Bear", "An omnivore",
    "", ""
};

これにより、 の配列 [40] の配列 [10] が生成されcharます。もちろん、これを明確に示す方がはるかに良いでしょう:

char words[][2][40] =
{
    { "Goat", "A herbivore" },
    { "Dog" , "An omnivore" },
    { "Fox" , "A carnivore" },
    { "Bear", "An omnivore" },
    { ""    , ""            },
};

「null」が何を意味するかについては、まだ問題があります。null ポインターの場合、ポインターがないため、このバージョンには存在しません (存在することもできません)。ヌル文字 ( '\0') の場合、文字列ごとに 1 つずつ、合計 10 あります。空の文字列の場合、作成者は非常に珍しく紛らわしい用語を使用しています。彼が空の文字列を意味する場合、彼は空の文字列と言うべきです。

必要なものは 2 つあります。その意図は、番兵を使用することだと思います。これを (C または C++ で) 記述する通常の方法は、 ポインター使用して、次のように記述します。

char const* const words[][2] =
{
    { "Goat", "A herbivore" },
    { "Dog" , "An omnivore" },
    { "Fox" , "A carnivore" },
    { "Bear", "An omnivore" },
    { NULL  , NULL          }
};

この場合、おそらく 2 つの null を使用しますが、配列を初期化する最後の行は{}{ NULL }、または{ NULL, NULL };にすることができます。それらはすべて、コンパイラにとって同じ結果になります。

非ポインター バージョンでも、1 つの空の文字列で十分です。コンパイラは、すべての追加値にも空の文字列を入力します。

最後に、ヌル ポインターまたは空の文字列が必要な理由について説明します。C では、これらはセンチメンタルとして頻繁に使用されていました。それらに遭遇するまでループします。

for ( char const* const (*p)[2] = words; (*p)[0] != NULL; ++ p ) {
    std::cout << (*p)[0] << " - " << (*p)[1] << std::endl;
}

C++ では、センティナルを使用することはほとんどありません。次のように記述できるからです。

for ( char const* const (*p)[2] = begin( words );
        p != end( words );
        ++ p ) {
    std::cout << (*p)[0] << " - " << (*p)[1] << std::endl;
}

C++11 では、それはstd::beginandstd::endになりますが、もちろん、C++11 より前は、誰もが個人用ツールキットにそれらを実装していました。C++11 では、以下も使用できますauto

for ( auto p = begin( words ); p != end( words ); ++ p ) {
    std::cout << (*p)[0] << " - " << (*p)[1] << std::endl;
}

ただし、これが私たちに多くの利益をもたらすかどうかはわかりません。使用するときは、複雑な型を認識する必要があります。(autoイテレータのように、基本的に使用するのは簡単であるが、綴りが難しい型がある場合は、多くのことを購入します。)

最後に、もちろん、C でも C++ でも、このために 2 次元配列を使用する人はいません。構造体を定義します:

struct Mapping
{
    char const* animalName;
    char const* eatingType;
};

Mapping const map[] = 
{
    { "Goat", "A herbivore" },
    { "Dog" , "An omnivore" },
    { "Fox" , "A carnivore" },
    { "Bear", "An omnivore" },
    { NULL  , NULL          }
};

for ( Mapping const* p = map; p->animalName != NULL; ++ p ) {
    //  ...
}

(もちろん、センティナルの代わりに、C++ を使用して。begin)end

ところで、この本をひどく誤って引用していない限り、捨ててください。著者は明らかに C も C++ も知りません。

于 2013-04-24T12:40:29.457 に答える