2008-10-04 5 views
1

Я пишу функцию, которая получает переменную окружения системы, разбивает каждый путь, а затем объединяет некоторые другие дополнительные символы в конец каждого пути.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 
    } 
} 

Мой выход оболочки, как это (я конкатенации «/, который» на все):

... 
/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()? И если да, то кто-нибудь знает, как это исправить?

Thanks

+0

Это я или есть много незарегистрированных Стивов на SO? – 2008-10-04 03:05:11

+0

Ваш код не содержит символов '*': char * pathDeepCopy = (char) malloc (strlen (path) + 1); символьный токен [80]; – bk1e 2008-10-04 03:41:54

+0

@ bk1e: OP напечатал их, но SO интерпретировал их как курсивное форматирование. – 2008-10-04 04:39:32

ответ

2

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

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

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

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

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

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

  • strlcat()

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

  • strncat_s()

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

2

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

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

6

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

Итак, ваше использование strcat (токен [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); 
} 
0

заменить, что с

зЬгсру (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 
} 

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

1

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

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

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

1

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

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

0

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