Ошибка сегментации? Токенизация строк

Я пытаюсь написать функцию, которая будет преобразовывать символы из массива в целые числа, чтобы я мог произвести сумму или другой математический процесс для каждой группы. Я знаю, что сначала мне нужно использовать strtok, чтобы избавиться от пробелов и «+». Я пытался сначала хотя бы начать с того, чтобы заставить strtok работать, но он продолжает говорить об ошибке сегментации, когда я пытаюсь его запустить.

Любая помощь?

#include <string.h>
#include <stdio.h>
int main(void)
{
    char* string[] = { "10 + 20", "1 + 3 + 6 + 8" };
    sum(string[0]);
}

int sum(char* s)
{
    char *stringcopy = malloc( strlen(s) + 1 );     
    char* del = " +";                                  
    char* token;
    int i;

    stringcopy = s;   /* 'copy' problem here */             

    token = strtok(stringcopy, del);
    while( token ) {
       printf("%s\n", token);
       token = strtok(NULL, del);
    }
    return 11;   /* placeholder until I get the sum */
}

c c89
person Crisis    schedule 29.06.2014    source источник
comment
Совет: используйте правильный отступ и добавьте тег C. Во всяком случае, вы уверены, что это должен быть C89?   -  person Deduplicator    schedule 30.06.2014
comment
textcopy = s; - это не то, что вы думаете.   -  person Oliver Charlesworth    schedule 30.06.2014
comment
@Deduplicator да, он должен быть в формате C89.   -  person Crisis    schedule 30.06.2014
comment
@Crisis: Хорошо, в таком случае лучше упомянуть, что вам нужна эта версия, но все же пометьте тегом общего языка для более широкой аудитории.   -  person Deduplicator    schedule 30.06.2014
comment
@OliCharlesworth Я подумал, что это слишком хорошо, чтобы быть правдой. Что он на самом деле делает? Что мне делать вместо этого?   -  person Crisis    schedule 30.06.2014


Ответы (4)


Есть простая причина, по которой strtok выдает ошибку сегментации:

Вы запускаете его со строковым литералом, который, несмотря на тип char[n], является неизменяемым объектом.
strtok изменяет любую строку, на которой вы его запускаете!

Обходной путь прост: запустите копию. Вот функция для дублирования строк (большинство библиотек C предоставляют эту нестандартную функцию как char* strdup(const char*):

char* my_strdup(const char* s) {
    size_t size = strlen(s)+1;
    char* ret = malloc(size);
    if(ret)
        memcpy(ret, s, size);
    return ret;
}

Не забудьте free() копию позже.

Вы пытались сделать это, но после хорошего старта и резервирования места для строки с malloc вы просто отбросили ее (утечка памяти), назначив указатель на литерал тому же самому указателю.

person Deduplicator    schedule 29.06.2014
comment
Вы не сделали копию. Смотрите мой ответ. - person David K; 30.06.2014
comment
@Crisis: Да, это так. - person Deduplicator; 30.06.2014
comment
Что мне нравится в этом ответе: о функции strdup очень полезно знать. (Я думаю, что большинство библиотек времени выполнения C предоставляют его для вас?) - person David K; 30.06.2014

Это утверждение

textcopy = s;   /* making copy for strtok */

не делает то, что вы думаете.

На самом деле произошла утечка памяти, потому что сначала вы выделили память, а textcopy получил адрес первого байта хранилища.

char *textcopy = malloc( strlen(s) + 1 );

а затем вы переназначили textcopy.

textcopy = s;   /* making copy for strtok */

Вместо этого вы должны использовать стандартную функцию strcpy

strcpy( textcopy, s );   /* making copy for strtok */

Также было бы лучше, если бы функция была объявлена ​​как

int stringSum(const char* s);
person Vlad from Moscow    schedule 29.06.2014
comment
Использование strcpy(textcopy, s); дает мне сообщение об ошибке "слишком мало аргументов для функции" - person Crisis; 30.06.2014
comment
@Crisis Прежде всего вам нужно включить заголовок ‹string.h›. Функция имеет ровно два параметра, поэтому если включен заголовок, компилятор не может выдать такую ​​ошибку. - person Vlad from Moscow; 30.06.2014
comment
Это странно, потому что у меня есть это в моем заголовке. - person Crisis; 30.06.2014
comment
@Crisis Проверьте, возможно, вы написали strncpy( textcopy, s ); вместо strcpy(textcopy, s); - person Vlad from Moscow; 30.06.2014
comment
Должно быть, я перепутал другую функцию, размещенную здесь, с этой. Я больше не получаю ошибку, спасибо. - person Crisis; 30.06.2014

Эта строка не делает то, что вы думали:

textcopy = s;   /* making copy for strtok */             

Здесь не копируется строка. Все, что происходит, это то, что вместо указания на первый байт блока памяти, который вы только что выделили для него (с помощью malloc( strlen(s) + 1 )), textcopy теперь указывает непосредственно на первый байт литеральной строки "10 + 20", которую вы передали stringSum как s.

Вероятно, вы хотели что-то вроде strcpy(textcopy, s). Я предложил strncpy ранее; strncpy(textcopy, s, strlen(s)) + 1 может работать, но (как объясняют комментарии) кажется довольно бессмысленным для использования в этом контексте.

person David K    schedule 29.06.2014
comment
Вероятно, он хотел strcpy(textcopy, s); . Ваше предложение не создает строку. - person M.M; 30.06.2014
comment
Не используйте strncpy, это воплощение зла (если вы действительно не знаете, что делаете). В любом случае, вы получаете неправильную длину буфера. - person Deduplicator; 30.06.2014
comment
Ах да, если вы используете strncpy, то лучше либо добавить единицу к длине строки, либо потом записать ноль в textcopy(strlen(s)). И поскольку он уже убедился, что буфер достаточно большой, чтобы вместить strlen(s) + 1, обычные оправдания для strncpy не применимы. - person David K; 30.06.2014
comment
@Deduplicator был в курсе длины буфера; Я попытался исправить это сейчас. Я не удалил упоминание strncpy (поскольку комментарии ссылаются на него), но отметил strcpy. И я, вероятно, предложил бы strcpy сам, прежде чем Microsoft стала параноиком по этому поводу. Может быть, я все еще должен. - person David K; 30.06.2014
comment
@DavidK: Проблема с strncpy() заключается в том, что он предназначен для действительно специализированной ниши: заполнение части начала буфера ненулевыми значениями из предоставленной строки / достаточно длинного буфера и заполнение остальной части 0. Если это не то, чего вы хотите (например, почти никогда), это либо неэффективно, либо совершенно неправильно. - person Deduplicator; 30.06.2014
comment
Честно говоря, мне интересно, почему я вообще упомянул strncpy в этом конкретном случае. Во всем коде, который я когда-либо писал, strncpy практически всегда означало, что я хочу извлечь подстроку чего-то, поэтому я был уверен, что на самом деле получу n ненулевых значений, а strcpy скопирует слишком много, потому что s[n] не равно нулю) . - person David K; 30.06.2014
comment
На самом деле я думаю, что знаю, почему я подумал о strncpy - это были все те предупреждения strcpy is unsafe, которые Visual Studio начала извергать на меня. Я должен задаться вопросом, почему я позволяю им влиять на меня здесь. - person David K; 30.06.2014

Линия

textcopy = s;   /* making copy for strtok */             

не делает копию s и не помещает ее в textcopy. Вместо этого вам нужно использовать:

strcpy(textcopy, s);
person R Sahu    schedule 29.06.2014