Передача массива строк в качестве параметра функции в C - PullRequest
14 голосов
/ 28 января 2009

Мне нужна простая функция, которая получает строку и возвращает массив строк после некоторого анализа. Итак, это моя подпись функции:

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?

Заранее спасибо

Ответы [ 4 ]

28 голосов
/ 28 января 2009

Предупреждение совершенно верно. Ваша функция хочет массив указателей. Вы даете ему массив массивов.

Ожидаемое:

 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 не может измениться в этом преобразовании. Допускается только операция распада одна . Вам нужно, чтобы это произошло дважды. В вашем случае X - это массив из MAX_STRING_LENGTH символов. Функция хочет, чтобы X был указателем на символ. Поскольку это не одно и то же, компилятор предупреждает вас. Я немного удивлен, что это было просто предупреждение, так как ничего не может быть получено из того, что компилятор допустил.

В вашей функции вы можете написать этот код:

char* y = NULL;
*sep_foo = y;

Это юридический код, поскольку sep_foo - это char**, поэтому *sep_foo - это char*, а также y; Вы можете назначить их. Но с тем, что вы пытались сделать, *sep_foo не будет на самом деле будет char*; это будет указывать на массив символов. Ваш код, по сути, будет пытаться сделать это:

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

Вы не можете назначить указатель на массив, и поэтому компилятор предупреждает, что вызов не годится.

Есть два способа решить эту проблему:

  • Измените способ, которым вы объявляете и выделяете 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);
    
15 голосов
/ 28 января 2009

Параметр 2 должен быть

char sep_foo[][MAX_STRING_LENGTH]

Для пояснения, вы передаете указатель на parse () и рассматриваете его как указатель на указатель. Многомерный массив в C не является массивом указателей. Это отдельный блок памяти, на который указывает переменная массива. Вы не можете разыменовать его дважды.

4 голосов
/ 28 января 2009

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);
0 голосов
/ 28 января 2009

Если это ваш точный код, то я полагаю, что segfault вызван тем фактом, что вы не выделили память для char* token внутри вашей функции синтаксического анализа, а затем используете ее в своем strcpy.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...