Существенная проблема заключается в том, что, хотя хранилище когда-либо выделяется (с malloc()
) для результатов, которые вы пытаетесь вернуть как myfoo
и mybar
, указатели на эти распределения фактически не возвращаются в main()
. В результате, более поздний вызов printf()
вполне может вызвать дамп ядра.
Решение состоит в том, чтобы объявить аргументы как указатели на указатель на char
и передать адреса myfoo
и mybar
на fn
. Нечто подобное (непроверенное) должно сработать:
void
fn(char *baz, char **foo, char **bar)
{
char *pch;
/* this is the part I'm having trouble with */
pch = strtok (baz, ":");
*foo = malloc(strlen(pch)+1); /* include space for NUL termination */
strcpy(*foo, pch);
pch = strtok (NULL, ":");
*bar = malloc(strlen(pch)+1); /* include space for NUL termination */
strcpy(*bar, pch);
return;
}
int
main(void)
{
char mybaz[] = "hello:world";
char *myfoo, *mybar;
fn(mybaz, &myfoo, &mybar);
fprintf(stderr, "%s %s", myfoo, mybar);
free(myfoo);
free(mybar);
}
Не забудьте освободить каждую выделенную строку на более позднем этапе, иначе вы создадите утечки памяти.
Чтобы одновременно выполнять malloc () и strcpy () в одном вызове, было бы лучше использовать strdup()
, поскольку он также помнит, что нужно выделить место для завершающего NUL, который вы оставили в коде, как написано. *foo = strdup(pch)
гораздо понятнее и легче поддерживать эту альтернативу. Поскольку strdup()
- это POSIX, а не ANSI C, вам, возможно, потребуется реализовать его самостоятельно, но усилия хорошо окупаются полученной ясностью для этого вида использования.
Другим традиционным способом возврата строки из функции C является то, чтобы вызывающая сторона выделяла хранилище и предоставляла его адрес функции. Это техника, используемая, например, sprintf()
. Проблема состоит в том, что нет способа сделать такой сайт вызова полностью безопасным от ошибок переполнения буфера, вызванных вызываемой функцией, при условии, что было выделено больше места, чем фактически доступно. Традиционное исправление этой проблемы состоит в том, чтобы требовать, чтобы аргумент длины буфера также передавался, и тщательно проверять как фактическое выделение, так и длину, заявленную на сайте вызова при проверке кода.
Edit:
Фактический segfault, который вы получаете, скорее всего, находится внутри strtok()
, а не printf()
, потому что ваш записанный пример пытается передать строковую константу в strtok()
, которая должна иметь возможность изменять строку. Это официально неопределенное поведение.
Исправление этой проблемы - убедиться, что bybaz
объявлен как инициализированный массив, а не как указатель на char
. Инициализированный массив будет расположен в доступной для записи памяти, а строковая константа, скорее всего, будет находиться в постоянной памяти. Во многих случаях строковые константы хранятся в той же части памяти, которая используется для хранения самого исполняемого кода, и все современные системы пытаются затруднить программе изменение собственного исполняемого кода.
Во встроенных системах, над которыми я зарабатываю на жизнь, код, вероятно, будет храниться в каком-либо ПЗУ и не может быть физически изменен.