Передача массива строк как параметра функции в 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-коде?

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

Ответы

Ответ 1

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

Ожидаемое:

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

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

char* y = NULL;
*sep_foo = y;

Этот юридический код с sep_foo является char**, поэтому *sep_foo является char*, а также y; вы можете назначить их. Но с тем, что вы пытались сделать, *sep_foo на самом деле не был бы char*; он будет указывать на массив из 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);
    

Ответ 2

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

char sep_foo[][MAX_STRING_LENGTH]

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

Ответ 3

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

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