Cで文字列の配列を作成するにはどうすればよいですか?

2009年07月07日に質問されました。  ·  閲覧回数 1M回  ·  ソース

Charles picture
2009年07月07日

Cで文字列の配列を作成しようとしています。このコードを使用する場合:

char (*a[2])[14];
a[0]="blah";
a[1]="hmm";

gccは「警告:互換性のないポインタ型からの割り当て」を表示します。 これを行う正しい方法は何ですか?

編集: printf(a[1]); 、「うーん」と正しく出力されるため、なぜこれがコンパイラの警告を表示するのか興味があります。

回答

Mikael Auno picture
2009年07月07日
240

文字列を変更したくない場合は、簡単に行うことができます

const char *a[2];
a[0] = "blah";
a[1] = "hmm";

このようにすると、2つのポインタの配列がconst char割り当てられます。 これらのポインタは、静的文字列"blah"および"hmm"のアドレスに設定されます。

実際の文字列の内容を変更できるようにしたい場合は、次のようなことを行う必要があります。

char a[2][14];
strcpy(a[0], "blah");
strcpy(a[1], "hmm");

これにより、それぞれ14 charの2つの連続した配列が割り当てられ、その後、静的文字列のコンテンツがそれらにコピーされます。

John Bode picture
2009年07月08日
194

Cで文字列の配列を作成するには、いくつかの方法があります。すべての文字列が同じ長さになる(または少なくとも同じ最大長になる)場合は、charの2次元配列を宣言し、必要に応じて割り当てるだけです。

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1];
...
strcpy(strs[0], aString); // where aString is either an array or pointer to char
strcpy(strs[1], "foo");

初期化子のリストを追加することもできます。

char strs[NUMBER_OF_STRINGS][STRING_LENGTH+1] = {"foo", "bar", "bletch", ...};

これは、初期化子の文字列のサイズと数が配列の次元と一致していることを前提としています。 この場合、各文字列リテラル(それ自体はcharのゼロで終了する配列)の内容が、strsに割り当てられたメモリにコピーされます。 このアプローチの問題は、内部の断片化の可能性です。 5文字以下の99個の文字列があり、20文字の長さの1個の文字列がある場合、99個の文字列には少なくとも15個の未使用文字が含まれます。 それはスペースの無駄です。

charの2次元配列を使用する代わりに、charへのポインターの1次元配列を格納できます。

char *strs[NUMBER_OF_STRINGS];

この場合、文字列へのポインタを保持するためのメモリのみを割り当てていることに注意してください。 文字列自体のメモリは、他の場所に割り当てる必要があります(静的配列として、またはmalloc()またはcalloc() )。 前の例のように初期化子リストを使用できます。

char *strs[NUMBER_OF_STRINGS] = {"foo", "bar", "bletch", ...};

文字列定数の内容をコピーする代わりに、単にそれらへのポインタを格納しているだけです。 文字列定数は書き込み可能でない場合があることに注意してください。 次のように、ポインタを再割り当てできます。

strs[i] = "bar";
strs[i] = "foo"; 

ただし、文字列の内容を変更できない場合があります。 すなわち、

strs[i] = "bar";
strcpy(strs[i], "foo");

許可されない場合があります。

malloc()を使用して、各文字列に動的にバッファを割り当て、そのバッファにコピーできます。

strs[i] = malloc(strlen("foo") + 1);
strcpy(strs[i], "foo");

ところで、

char (*a[2])[14];

charの14要素配列へのポインタの2要素配列としてaを宣言します。

mpen picture
2009年07月07日
97

わかった! 定数文字列:

const char *strings[] = {"one","two","three"};

もし私が正確に覚えていれば。

ああ、そしてあなたは=演算子ではなくstrcpyを割り当てに使用したいと思っています。 strcpy_sの方が安全ですが、C89標準にもC99標準にもありません。

char arr[MAX_NUMBER_STRINGS][MAX_STRING_SIZE]; 
strcpy(arr[0], "blah");

更新:トーマスstrlcpyが行く方法だと言います。

Faisal Vali picture
2009年07月07日
15

オプションのいくつかを次に示します。

char a1[][14] = { "blah", "hmm" };
char* a2[] = { "blah", "hmm" };
char (*a3[])[] = { &"blah", &"hmm" };  // only since you brought up the syntax -

printf(a1[0]); // prints blah
printf(a2[0]); // prints blah
printf(*a3[0]); // prints blah

a2の利点は、文字列リテラルを使用して次のことを実行できることです。

a2[0] = "hmm";
a2[1] = "blah";

そして、 a3場合、次のことを行うことができます。

a3[0] = &"hmm";
a3[1] = &"blah";

a1場合、文字列リテラルを割り当てる場合でも、 strcpy() (さらに良いのはstrncpy() )を使用する必要があります。 その理由は、 a2a3はポインターの配列であり、それらの要素(つまりポインター)が任意のストレージを指すようにすることができるのに対し、 a1は '配列の配列であるためです。したがって、各要素は独自のストレージを「所有」する配列です(つまり、スコープ外になると破棄されます)。コピーできるのはストレージにのみです。

これは、 a2a3を使用することの不利な点ももたらします-静的ストレージ(文字列リテラルが格納される場所)を指しているため、内容を確実に変更することはできません(つまり、未定義の動作) 、文字列以外のリテラルをa2またはa3要素に割り当てたい場合は、最初に十分なメモリを動的に割り当ててから、それらの要素がこのメモリを指すようにしてから、コピーする必要があります。その中に文字があります-そして、完了したら必ずメモリの割り当てを解除する必要があります。

Bah-私はすでにC ++が恋しいです;)

ps例が必要な場合はお知らせください。

FutureSci picture
2013年02月01日
12

または、文字arry(1 string)を含む構造体タイプを宣言すると、構造体の配列が作成され、複数要素の配列が作成されます。

typedef struct name
{
   char name[100]; // 100 character array
}name;

main()
{
   name yourString[10]; // 10 strings
   printf("Enter something\n:);
   scanf("%s",yourString[0].name);
   scanf("%s",yourString[1].name);
   // maybe put a for loop and a few print ststements to simplify code
   // this is just for example 
 }

他の方法に対するこれの利点の1つは、 strcpyを使用せずに文字列に直接スキャンできることです。

Noldorin picture
2009年07月07日
10

ANSI Cの場合:

char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";
Bryce picture
2014年06月15日
10

文字列が静的である場合は、次の方法が最適です。

const char *my_array[] = {"eenie","meenie","miney"};

基本的なANSICの一部ではありませんが、環境が構文をサポートしている可能性があります。 これらの文字列は不変(読み取り専用)であるため、多くの環境では、文字列配列を動的に構築するよりもオーバーヘッドが少なくて済みます。

たとえば、小さなマイクロコントローラプロジェクトでは、この構文は(通常)より貴重なRAMメモリではなくプログラムメモリを使用します。 AVR-Cはこの構文をサポートする環境の例ですが、他のほとんどの環境も同様です。

Sergey picture
2017年04月06日
10

配列内の文字列の数を追跡したくない場合で、それらを繰り返し処理する場合は、最後にNULL文字列を追加するだけです。

char *strings[]={ "one", "two", "three", NULL };

int i=0;
while(strings[i]) {
  printf("%s\n", strings[i]);
  //do something
  i++;
};
dmckee --- ex-moderator kitten picture
2009年07月07日
9

文字列リテラルはconst char *です。

そして、括弧の使用は奇妙です。 あなたはおそらく意味します

const char *a[2] = {"blah", "hmm"};

これは、定数文字への2つのポインターの配列を宣言し、ハードコードされた2つの文字列定数を指すようにそれらを初期化します。

Dario picture
2009年07月07日
3

あなたのコードは関数ポインタの配列を作成しています。 試してみてください

char* a[size];

または

char a[size1][size2];

代わりに。

配列ポインタへのウィキブックスを参照してください