strcat () новая строка, повторяющаяся строка - PullRequest
1 голос
/ 04 октября 2008

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

Все работает нормально, пока я не использую функцию strcat() (см. Код ниже).

char* prependPath( char* exeName )
{
    char* path = getenv("PATH");  
    char* pathDeepCopy = (char *)malloc(strlen(path) + 1);
    char* token[80];
    int j, i=0; // used to iterate through array

    strcpy(pathDeepCopy, path);

    //parse and split

    token[0] = strtok(pathDeepCopy, ":"); //get pointer to first token found and store in 0
    //place in array
    while(token[i]!= NULL) { //ensure a pointer was found
        i++;
        token[i] = strtok(NULL, ":"); //continue to tokenize the string
    }

    for(j = 0; j <= i-1; j++) {
        strcat(token[j], "/");
        //strcat(token[j], exeName);

        printf("%s\n", token[j]); //print out all of the tokens
    }
}

Мой вывод оболочки такой (я объединяю "/ which" на все):

...
/usr/local/applic/Maple/bin/which
which/which
/usr/local/applic/opnet/8.1.A.wdmguru/sys/unix/bin/which
which/which
Bus error (core dumped)

Мне интересно, почему strcat отображает новую строку, а затем повторяет which/which. Мне также интересно узнать о Bus error (core dumped) в конце.

Кто-нибудь видел это раньше при использовании strcat()? И если да, кто-нибудь знает, как это исправить?

Спасибо

Ответы [ 7 ]

6 голосов
/ 04 октября 2008

strtok () не дает вам новую строку.
Он искажает входную строку, вставляя символ '\ 0' там, где был символ разделения.

Таким образом, использование strcat (token [j], "/") поместит символ '/' там, где был '\ 0'.
Также последний маркер начнет добавлять «который» после окончания выделенной памяти в неизведанную память.

Вы можете использовать strtok (), чтобы разбить строку на куски. Но если вы хотите что-то добавить к токену, вам нужно сделать копию токена, в противном случае то, что вы добавите, будет перетекать на следующий токен.

Кроме того, вам нужно больше заботиться о распределении памяти, вы теряете память повсюду: -)

PS. Если вы должны использовать C-Strings. используйте strdup () для копирования строки.

char* prependPath( char* exeName )
{
    char* path         = getenv("PATH");
    char* pathDeepCopy = strdup(path);
    char* token[80];
    int j, i; // used to iterate through array

    token[0] = strtok(pathDeepCopy, ":");
    for(i = 0;(token[i] != NULL) && (i < 80);++i)
    {
        token[i] = strtok(NULL, ":");
    }

    for(j = 0; j <= i; ++j)
    {
        char*  tmp = (char*)malloc(strlen(token[j]) + 1 + strlen(exeName) + 1);
        strcpy(tmp,token[j]);
        strcat(tmp,"/");
        strcat(tmp,exeName);
        printf("%s\n",tmp); //print out all of the tokens
        free(tmp);
    }
    free(pathDeepCopy);
}
2 голосов
/ 04 октября 2008

strtok не дублирует токен, а просто указывает на него в строке. Поэтому, когда вы помещаете символ '/' в конец токена, вы пишете '\ 0' либо в начале токена next , либо после конца буфера.

Также обратите внимание, что даже если бы strtok сделал , возвращая копии токенов вместо оригиналов (чего не происходит), он не выделил бы дополнительное пространство для добавления символов, поэтому все еще будет ошибкой переполнения буфера.

2 голосов
/ 04 октября 2008

strtok () токены на месте. Когда вы начинаете добавлять символы в токены, вы перезаписываете данные следующего токена.

Кроме того, в общем случае небезопасно просто объединять существующую строку, если вы не знаете, что размер буфера, в котором находится строка, достаточно велик для хранения результирующей строки. Это является основной причиной ошибок в программах на C (включая ужасные ошибки безопасности переполнения буфера).

Таким образом, даже если strtok () вернул совершенно новые строки, не связанные с вашей исходной строкой (а это не так), вы все равно будете переполнять строковые буферы, когда будете объединять их.

Некоторые более безопасные альтернативы strcpy () / strcat (), на которые вы, возможно, захотите взглянуть (вам может понадобиться отследить реализации некоторых из них - они не все стандартные):

  • strncpy () - включает целевой размер буфера, чтобы избежать переполнения. Недостатком является не всегда завершение строки результата
  • strncat ()

  • strlcpy () - аналогично strncpy (), но предназначен для упрощения использования и повышения надежности (http://en.wikipedia.org/wiki/Strlcat)

  • strlcat ()

  • strcpy_s () - Microsoft варианты этих функций

  • strncat_s ()

И API, который вы должны стремиться использовать, если можете использовать C ++: класс std :: string . Если вы используете класс C ++ std :: string, вам не нужно беспокоиться о буфере, содержащем строку - класс управляет всем этим за вас.

1 голос
/ 04 октября 2008

ОК, прежде всего, будьте осторожны. Вы теряете память. Strtok () возвращает указатель на следующий токен, и вы сохраняете его в массиве символов. Вместо char token [80] это должен быть char * token. Будьте осторожны также при использовании strtok. strtok практически уничтожает массив char с именем pathDeepCopy, потому что он будет заменять каждое вхождение ":" на '\ 0'. Как Майк Ф сказал вам выше. Обязательно инициализируйте pathDeppCopy, используя memset из calloc. Поэтому, когда вы кодируете токен [i], нет никакого способа узнать, на что указывает точка. А поскольку в токене нет данных, допустимых, он, скорее всего, сгенерирует дамп ядра, потому что вы пытаетесь выполнить конкататацию. Строка другому, у которого нет валидных данных (токен). Возможно, вы ищете и массив указателей на char, в котором будут храниться все указатели на токен, возвращаемый strtok, и в этом случае токен будет похож на char * token [];

Надеюсь, это немного поможет.

1 голос
/ 04 октября 2008

Если вы используете C ++, рассмотрите boost :: tokenizer , как обсуждалось в здесь .

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

Да, и используйте strdup для создания дублирующейся строки за один шаг.

0 голосов
/ 05 октября 2008

и не забудьте проверить, возвращает ли malloc значение NULL!

0 голосов
/ 04 октября 2008

замените это на

strcpy (pathDeepCopy, path);

  //parse and split
    token[0] = strtok(pathDeepCopy, ":");//get pointer to first token found and store in 0
    //place in array
    while(token[i]!= NULL) { //ensure a pointer was found
    i++;
    token[i] = strtok(NULL, ":"); //continue to tokenize the string
    }

// use new array for storing the new tokens 
// pardon my C lang skills. IT's been a "while" since I wrote device drivers in C.
const int I = i;
const int MAX_SIZE = MAX_PATH;
char ** newTokens = new char [MAX_PATH][I];
for (int k = 0; k < i; ++k) {
   sprintf(newTokens[k], "%s%c", token[j], '/');
   printf("%s\n", newtoken[j]); //print out all of the tokens
}

это заменит перезапись содержимого и предотвратит дамп ядра.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...