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

Опитвам се да напиша функция, която ще преобразува знаците от масив в int, така че да мога да създам сума или друг математически процес за всяка група. Знам, че първо трябва да използвам 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 */

не прави това, което си мислиш.

Всъщност има изтичане на памет, защото първо сте разпределили памет и текстовото копиране е получило адреса на първия байт от хранилището

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

и след това преназначихте текстово копие.

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 Check може би сте написали 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 стойности от предоставен низ / достатъчно дълъг буфер и запълване на останалата част с 0s. Ако това не е това, което искате (почти никога), то е или неефективно, или напълно погрешно. - 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