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


263

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

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

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

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

c  arrays  string 

12
ちょうど記録のために、char (*a[2])[14]14文字の配列への2つのポインタの配列です。
avakar 2009

4
私はそれが2文字xDの配列への14のポインタであると思った
Fortran

74
私がCタイプを解読するために私が今まで読んだ最も役立つアドバイス:「名前から始めて、可能なときは右に読み、必要なときに左に読みなさい」:char (*a[2])[14]-から始めてa、右に移動:「2つの配列」、左に移動:「ポインター」、ブラケットが完成したので、右に読みます: "forteenの配列"、左に読みます: "char" ...まとめると、 "aは、14のcharの配列への2つのポインタの配列"になります
Mark K Cowan

4
@dotancohen:そのヒントは、ポインタをchar *strでなく書くように最終的に確信したものですchar* str。Delphi / Pascalの経歴を持っているので、より複雑な型に出会うまでは、後者の方法に慣れていました。前者の方法は依然として醜いように見えますが、型表記の一貫性を高めています(IMO)。
Mark K Cowan、

@MarkKCowan、素晴らしい!ありがとう!:)
エッセン博士2018年

回答:


232

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

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");

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


185

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を宣言します。


3
@Slater:はい、それがmalloc呼び出しの結果である場合。
John Bode 2012

この非常に詳細な回答をありがとうございます。これは本当に役に立ちました。
cokedude 2014年

1
2D配列として宣言されたString配列でのみstrcpyを使用できるのはなぜですか。標準割り当てが失敗するのはなぜですか?
Andrew S

4
@AndrewS:完全な回答はコメントに収まりませんが、基本的にはCが配列式を処理する方法のアーティファクトです。ほとんどの状況では、型の式は型の式T [N]に変換され、式のT *値は最初の要素のアドレスになります。したがって、を書いた場合str = "foo"、の最初の文字のアドレスを"foo"配列に割り当てようとしますがstr、これは機能しません。詳細については、この回答を参照してください。
John Bode

@JohnBodeに小さな調整を追加してください。char *strs[NUMBER_OF_STRINGS] = {0}; これは、初期化することにより、将来の問題を防ぐことができますstrsNULL。do googleがCの文字列の配列を検索すると、多くの人がこの投稿を読みます
cokedude

94

わかった!定数文字列:

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

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

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

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

更新: トーマスによるstrlcpyと、これが方法です。


それはC99ですか?ANSI Cではそれが可能だとは思いません
。–ノルドリン

6
C89とC99の両方で可能です。前者の方が好ましいですが、constの有無に関係なく、どちらでもかまいません。
avakar 2009

1
さて、constは新しいので、以前は外部配列のサイズ(この場合は3)を指定する必要がありましたが、それ以外の場合は、これは完全に許容できるK&R Cです。これを行う。彼らはそれを「不規則な配列」と呼んでいます。もちろん、「演算子」はありませんでした。strcpy_sは私にとって新しいものです。
TED

6
strcpy_sはMicrosoftの関数です。それは、標準のCではないので、それはおそらく避けるべきである
Cromulent

5
strcpy_sおよびその他の「安全な機能」はISO / IEC TR 24731として標準化されています(これはISOが公開した標準であるため、無料でオンラインで入手することはできません。最新のドラフトはopen-std.org/jtc1/sc22/wg14/wwwです。 /docs/n1225.pdf
パベルMinaev

14

ここにあなたのオプションのいくつかがあります:

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()a2a3a1

これはまたa2、and を使用することの欠点をもたらしa3ます-andは静的ストレージ(文字列リテラルが格納されている場所)を指しているため、非文字列リテラルをa2またはの要素a3-最初に十分なメモリを動的に割り当ててから、その要素をこのメモリにポイントさせてから、それに文字をコピーする必要があります。次に、完了時にメモリの割り当てを解除する必要があります。

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

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


Arduinoプロジェクトには文字列配列が必要でした。最後に私はa2スタイルを使用しました。最初に、文字列配列をchar a1 [] [2] = {"F3"、 "G3" ...などとして定義するa1スタイルを試しました。2文字の長い文字列を格納するためのものでした。nullターミネーターを忘れると、2文字を格納するために各文字列のサイズが少なくとも3である必要があるため、予期しない出力が発生しました。a2スタイルを使用すると、文字列の長さを指定する必要がなく、さまざまな文字列の長さにも対応できるため、それを
守る

char(* a3 [])[] = {& "blah"、& "hmm"}; => g ++ Apple LLVMバージョン9.1.0では機能しませんが、gccで機能します
1234

12

または、文字arry(1文字列)を含む構造体型を宣言できます。それらは、構造体の配列を作成し、したがって複数要素の配列を作成します

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


10

ANSI Cの場合:

char* strings[3];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "baz";

8
@Zifre:私は完全に同意しません。これはタイプの一部です。この場合は「charポインタ」です。とにかく何を言いますか?それは変数名の一部ですか?多くの有能なプログラマーがこのスタイルを使用するのを見てきました。
ノルドリン2009

14
これを読んでいる他の人のためだけに、Bjarne Stroustrupが*をタイプ別に付けていることを指摘したいと思います...
MirroredFate

1
@MirroredFate:正解です。実際、私が知っていることから、C ++での実践が推奨されています。意味的には、それが使用される方法のために、識別子によってそれを置くことは私には意味がありません。:/
Noldorin 2013年

16
@Noldorin char* foo, bar;のタイプはbar何ですか?
mASOUD 2015年

10
Cは1972年にDennis Ritchieによって開発され、1988年に彼とBrian KernighanがK&Rの第2版-Cプログラミング言語を発行しました。
マリウスリアン

10

文字列が静的な場合は、次のようにするのが最善です。

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

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

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


10

配列内の文字列の数を追跡したくない場合、それらを反復したい場合は、最後にNULL文字列を追加します。

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

int i=0;
while(strings[i]) {
  printf("%s\n", strings[i]);
  //do something
  i++;
};

これはC ++でのみ有効であると思います。Cでは、NULLがゼロであることは保証されていないため、ループが中断しても、中断しない場合があります。私が間違っていたら訂正してください。
Palec

2
わからない:)必要に応じて、whileステートメントでNULLと比較できます。
セルゲイ2017

9

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

そして、括弧の使用は奇妙です。もしかして

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

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


3

コードが関数ポインタの配列を作成しています。試す

char* a[size];

または

char a[size1][size2];

代わりに。

配列ポインタのウィキブックを見る


1
あなたの異なるアプローチが
嫌い

1

こんにちは、あなたはこのうなり声を試すことができます:

 char arr[nb_of_string][max_string_length]; 
 strcpy(arr[0], "word");

使用したい場合は、cの文字列の配列の良い例

#include <stdio.h>
#include <string.h>


int main(int argc, char *argv[]){

int i, j, k;

// to set you array
//const arr[nb_of_string][max_string_length]
char array[3][100];

char temp[100];
char word[100];

for (i = 0; i < 3; i++){
    printf("type word %d : ",i+1);
    scanf("%s", word);
    strcpy(array[i], word);
}

for (k=0; k<3-1; k++){
    for (i=0; i<3-1; i++)
    {
        for (j=0; j<strlen(array[i]); j++)
        {
            // if a letter ascii code is bigger we swap values
            if (array[i][j] > array[i+1][j])
            {
                strcpy(temp, array[i+1]);
                strcpy(array[i+1], array[i]);
                strcpy(array[i], temp);

                j = 999;
            }

            // if a letter ascii code is smaller we stop
            if (array[i][j] < array[i+1][j])
            {
                    j = 999;
            }

        }
    }
}

for (i=0; i<3; i++)
{
    printf("%s\n",array[i]);
}

return 0;
}

0
char name[10][10]
int i,j,n;//here "n" is number of enteries
printf("\nEnter size of array = ");
scanf("%d",&n);
for(i=0;i<n;i++)
{
    for(j=0;j<1;j++)
    {
        printf("\nEnter name = ");
        scanf("%s",&name[i]);
    }
}
//printing the data
for(i=0;i<n;i++)
{
    for(j=0;j<1;j++)
    {
        printf("%d\t|\t%s\t|\t%s",rollno[i][j],name[i],sex[i]);
    }
    printf("\n");
}

ここでこれを試してください!!!


1
変数jでforループが必要な理由、つまりfor(j = 0; j <1; j ++)が必要な理由を説明できますか?
SouvikMaji 2015

0

文字列の量が実行時の選択に応じて変化する可能性のある、より動的な文字列の配列が欠落していましたが、それ以外の場合は文字列を修正する必要があります。

私はこのようにコードスニペットをコーディングすることになりました:

#define INIT_STRING_ARRAY(...)          \
    {                                   \
        char* args[] = __VA_ARGS__;     \
        ev = args;                      \
        count = _countof(args);         \
    }

void InitEnumIfAny(String& key, CMFCPropertyGridProperty* item)
{
    USES_CONVERSION;
    char** ev = nullptr;
    int count = 0;

    if( key.Compare("horizontal_alignment") )
        INIT_STRING_ARRAY( { "top", "bottom" } )

    if (key.Compare("boolean"))
        INIT_STRING_ARRAY( { "yes", "no" } )

    if( ev == nullptr )
        return;

    for( int i = 0; i < count; i++)
        item->AddOption(A2T(ev[i]));

    item->AllowEdit(FALSE);
}

char** ev配列文字列へのポインタを取得し、countは_countof関数を使用して文字列の量を取得します。(と同様sizeof(arr) / sizeof(arr[0]))。

またA2T、マクロを使用してUnicodeに変換するための追加のAnsi がありますが、これはオプションの場合もあります。


-6

文字列を自分で定義するのが良い方法です。

#include <stdio.h>
typedef char string[]
int main() {
    string test = "string";
    return 0;
}

とても簡単です。


4
あなたはを欠いています;、そしてこれはどのように文字列の配列を作成しますか?
キーヤー2014年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.