Strcat () новая строка, повторяющаяся строка

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

Все работает нормально, пока я не использую функцию 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()? И если да, то кто нибудь знает как исправить?

Спасибо

Это я или на SO много незарегистрированных Стивов?

Mike F 04.10.2008 07:05

В вашем коде отсутствует какой-то 'символы: char pathDeepCopy = (char) malloc (strlen (path) + 1); токен char [80];

bk1e 04.10.2008 07:41

@ bk1e: OP действительно ввел их, но SO интерпретировал их как форматирование курсивом.

Mike F 04.10.2008 08:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
8 086
7

Ответы 7

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, вам практически не нужно беспокоиться о буфере, содержащем строку - класс управляет всем этим за вас.

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

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

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);
}

Обратите внимание, что strdup () не является частью стандарта C, хотя он является частью POSIX.

Chris Young 04.10.2008 09:46

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

strcpy (pathDeepCopy, путь);

  //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
}

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

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

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

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

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

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

Я думаю, что «токен» на самом деле является символом * [80], но SO интерпретировал «*» как курсивное форматирование.

Mike F 04.10.2008 07:07

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

Другие вопросы по теме