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