web-dev-qa-db-ja.com

文字列の配列をパラメータとしてCの関数に渡す

文字列を受け取り、解析後に文字列の配列を返す単純な関数が必要です。だから、これは私の関数シグネチャです:

int parse(const char *foo, char **sep_foo, int *sep_foo_qty) {
    int i;
    char *token;
    ...
    strcpy(sep_foo[i], token); /* sf here */
    ...
}

それから私はそれをこのように呼びます:

char sep_foo[MAX_QTY][MAX_STRING_LENGTH];
char foo[MAX_STRING_LENGTH];
int sep_foo_qty, error;

...

error = parse(foo, sep_foo, &sep_foo_qyt);

...

このようにして、コンパイル中に警告が表示されます。

warning: passing argument 2 of 'parse' from incompatible pointer type

そして、ここで/ * sfとマークされた行での実行中のセグメンテーション違反* /

私のCコードの何が問題になっていますか?

前もって感謝します

14
mmutilva

警告は正確に正しいです。関数にはポインターの配列が必要です。あなたはそれに配列の配列を与えています。

期待:

 sep_foo:
 + ------ + + ----- + 
 | char ** |-> 0:| char * |-> " string1 "
 + ------ + + ----- + 
 1:| char * |->" string2 "
 + ----- + 
 * sep_foo_qty-1:| ... | 
 + ----- + 

あなたが提供したもの:

 sep_foo:
 + -------------------------------- + 
 0:| char [MAX_STRING_LENGTH] | 
 + -------------------------------- + 
 1 :| char [MAX_STRING_LENGTH] | 
 + -------------------------------- + 
 MAX_QTY -1:| ... | 
 + -------------------------------- + 

タイプXの要素を持つ配列は、-XまたはX*へのポインタに「減衰」する可能性があります。ただし、その変換ではXの値を変更することはできません。 one減衰操作のみが許可されます。あなたはそれが二度起こる必要があるでしょう。あなたの場合、XMAX_STRING_LENGTH- charsの配列です。この関数は、Xをcharへのポインターにする必要があります。それらは同じではないので、コンパイラは警告します。コンパイラーが許可したことからは何も良いことは得られないので、それが単なる警告であったことに少し驚いています。

関数では、次のコードを記述できます。

char* y = NULL;
*sep_foo = y;

sep_foochar**であり、*sep_foochar*であり、yでもあるため、これは正当なコードです。それらを割り当てることができます。しかし、あなたがやろうとしたことでは、*sep_foo本当にchar*にはなりません。それはcharの配列を指しているでしょう。あなたのコードは、事実上、これを行おうとしているでしょう:

char destination[MAX_STRING_LENGTH];
char* y = NULL;
destination = y;

配列にポインタを割り当てることはできないため、コンパイラは呼び出しが適切でないことを警告します。

これを解決するには2つの方法があります。

  • 関数が受け取ることを期待するものと一致するように、呼び出し側でsep_fooを宣言して割り当てる方法を変更します。

    char** sep_foo = calloc(MAX_QTY, sizeof(char*));
    for (int i = 0; i < MAX_QTY; ++i)
      sep_foo[i] = malloc(MAX_STRING_LENGTH);
    

    または、同等に

    char* sep_foo[MAX_QTY];
    for (int i = 0; i < MAX_QTY; ++i)
      sep_foo[i] = malloc(MAX_STRING_LENGTH);
    
  • 関数のプロトタイプを変更して、実際に与えているものを受け入れます。

    int parse(const char *foo, char sep_foo[MAX_QTY][MAX_STRING_LENGTH], int *sep_foo_qty);
    
28
Rob Kennedy

パラメータ2は

char sep_foo[][MAX_STRING_LENGTH]

明確にするために、parse()へのポインターを渡し、それをポインターへのポインターとして扱います。 Cの多次元配列は、ポインターの配列ではありません。これは、配列変数が指す単一のメモリブロックです。 2回逆参照することはできません。

15
codelogic

sep_fooは、配列の配列として定義されます。つまり、sep_fooを使用すると、シーケンシャルメモリの先頭を指します。モデルは次のとおりです。

(assume MAX_STRING_LENGTH = 16, MAX_QTY = 2)
sep_foo       = &&0000
sep_foo[0]    =  &0000
sep_foo[0][0] = *&0000 = 12
sep_foo[0][8] = *&0008 = 74
sep_foo[1]    =  &0010
sep_foo[1][0] = *&0010 = 12


0000  12 34 56 78  9A BC DE F0  74 10 25 89  63 AC DB FE
0010  12 34 56 78  9A BC DE F0  74 10 25 89  63 AC DB FE

ただし、関数はポインターの配列(実際にはポインターへのポインター)を想定しています。これは次のようにモデル化されています。

sep_foo_arg       =   &&0000
sep_foo_arg[0]    =  *&&0000 = &0010
sep_foo_arg[0][0] =  *&*&0000 = 12
sep_foo_arg[0][8] = *(&*&0000 + 8) = 74
sep_foo_arg[1]    =  *&&0002 = &0020
sep_foo_arg[1][0] = *&*&0000 = 12

0000  0010 0020  xxxx xxxx  xxxx xxxx  xxxx xxxx

0010  12 34 56 78  9A BC DE F0  74 10 25 89  63 AC DB FE
0020  12 34 56 78  9A BC DE F0  74 10 25 89  63 AC DB FE

ええ...構文は私の説明では少し混乱するかもしれません...

とにかく、この問題は、ポイントされたポインターの処理方法を関数に指示することで解決できます。特に、それを配列(メモリのシーケンス)として扱いたいと思うでしょう:

int parse(const char *foo, char (*sep_foo)[MAX_STRING_LENGTH], int *sep_foo_qty);
4
strager