Функцията, която връща низ, не работи – грешка при сегментиране

Имам много лесна функция, която ми създава някои проблеми. Всичко, което искам да направя, е да върна подниз, след като го промених от низ. Но когато стартирам кода, имам грешка при сегментиране. Какво се случва с моя код:

#include <stdio.h>
#include <string.h>

const char *new_name(char str[])
{

  char * pch;
  char * last="";
  char * pch2;
  static char name[80];
  printf ("Splitting string \"%s\" into tokens:\n",str);

  pch = strtok (str,"/");
  while (pch != NULL)
  {
     last=pch;
     pch = strtok (NULL, "/");

  }

  pch2 = strtok (last,".");
  strcpy(name, pch2);
  strcat(name, ".ppm");

  return name; 
}

int main()
{
  printf("New name: %s",new_name("/home/test/segmentation/test.pgm"));
  return 0;

}

РЕДАКТИРАНЕ: Имам друг въпрос: искам да използвам връщането на тази функция като вход за друга функция, но тя приема char и върнатата стойност е const char. Как да направя преобразуването?


person mad    schedule 04.11.2013    source източник
comment
изглежда като излишно използване на strtok тук, използвайте прост цикъл, за да намерите последния / чрез цикъл от края char* ch; for (ch = str + strlen(str) - 1; ch != '/'; --ch); сега ch сочи към '/', сега можете също да подадете всичко към функцията, тъй като не променяте аргумента.   -  person AndersK    schedule 04.11.2013


Отговори (6)


strtok ще промени първия си параметър, така че не можете да подадете указател към низов литерал към него, което би предизвикало недефинирано поведение.

Променете първите два реда на main на това:

char str[] = "/home/test/segmentation/test.pgm";
printf("New name: %s\n",new_name(str));
person Yu Hao    schedule 04.11.2013

Сривът е в strtok. Проблемът е, че предавате указател към низов литерал като негов първи параметър, а низовите литерали обикновено са в памет само за четене, така че strtok се срива при опит за промяна на низа.

char *strtok(char *str, const char *delim);

Един начин да го поправите:

char fileName[] = "/home/test/segmentation/test.pgm";
printf("New name: %s",new_name(fileName));

РЕДАКТИРАНЕ: Отговор на въпроса ви под „РЕДАКТИРАНЕ“.

Просто променете подписа на вашата функция, за да върнете char *:

char *new_name(char str[])

Въпреки това би било хубаво да го промените малко повече. Вместо да използвате локалния статичен буфер (name), би било по-добре да подадете буфера като втори параметър и да го разпределите, преди да извикате функцията, в клиентския код. Проблемът с този подход е, че обикновено повикващият не знае точната дължина или дори максималната дължина на низа, който трябва да бъде произведен.

Друга, може би по-добра опция е да разпределите паметта на буфера, която да бъде върната динамично с помощта на malloc и да върнете указател към него на повикващия. В този случай отговорността за освобождаването на буфера ще бъде на потребителския код, така че трябва да го документирате правилно.

person piokuc    schedule 04.11.2013
comment
@mad не наистина. какво е? - person piokuc; 04.11.2013
comment
Искам да използвам връщането на функцията като char, така че да може да бъде вход на друга функция. Как да го направя? - person mad; 04.11.2013

Трябва да запомните, че функцията strtok модифицира въведения низ. И му предавате указател към литерал низ, който е константен (т.е. само за четене). Опитът да се промени литерален низ води до недефинирано поведение.

Решението е съвсем просто:

char str[] = "...";
printf("New name: %s\n", new_name(str));
person Some programmer dude    schedule 04.11.2013

strtok работи чрез мутиране на низа, който е предаден. Не можете да подадете постоянен низ към strtok.

Можете да опитате

char str[] = "/home/test/segmentation/test.pgm";
printf("New name: %s",new_name(str));

вместо.

person gsg    schedule 04.11.2013

new_name("/home/test/segmentation/test.pgm")

Вие предавате литерал на низ (само за четене) и strtok променя този низ

person David Ranieri    schedule 04.11.2013

Първият параметър на strtok не може да бъде постоянен низ ( което е низов литерал):

pch = strtok (str,"/");
              ^^^

тъй като променя първия си параметър, свързаният документ казва:

char *strtok( char *restrict str, const char *restrict delim);

Тази функция е разрушителна: тя записва символите '\0' в елементите на низа str. По-специално, низов литерал не може да се използва като първи аргумент на strtok.

използването на неконстантен масив ще реши този проблем:

char arr[]  = "/home/test/segmentation/test.pgm" ;
printf("New name: %s",newname(arr));

За пълнота, модифицирането на низов литерал е недефинирано поведение черновата C99 стандарт в раздел 6.4.5 Низови литерали параграф 6 казва (акцентът е мой):

Не е уточнено дали тези масиви са различни, при условие че техните елементи имат подходящите стойности. Ако програмата се опита да промени такъв масив, поведението е недефинирано.

person Shafik Yaghmour    schedule 04.11.2013
comment
@glglgl Разбирам какво означава it's, това беше печатна грешка. - person Shafik Yaghmour; 04.11.2013