Грешка в сегментирането с sprintf

Правя създател на makefile, но останах в тази грешка на sprintf, най-любопитното е, че имам няколко sprintf преди този с грешката и те работят добре.

Ето кода:

if ( WIFEXITED(stat)  ){

    if ( WEXITSTATUS(stat) ) {

        if ( cFiles == 0 && cFolders == 0 ) {
            Crear(path);
        }

        cFolders = 1;
        TEMP = malloc( sizeof(char)*( strlen(direntp->d_name) + 25 ) );

        if ( TEMP == NULL ) {
            perror("Malloc Error: ");
            exit(1);
        }

        if ( sprintf(TEMP, "\n%s/%s.a: force\n\t$(MAKE) -C %s\n",direntp->d_name, direntp->d_name, direntp->d_name) < 0 ) {
        perror("Sprintf Error: ");
        exit(1);
        }

        write(STDOUT_FILENO,TEMP,strlen(TEMP));
        f.name = malloc( sizeof(char)*( strlen(direntp->d_name)*2 + 3 ) );

        if ( f.name = NULL ) {
            perror("Malloc Error: ");
            exit(1);
        } 
            //This is the one with the problem!!!       
            if ( sprintf(f.name, "%s/%s.a", direntp->d_name, direntp->d_name) < 0 ) {
            perror("Sprintf Error: ");
            exit(1);
        }

        l = AddToList(l,&f);
    }
}

person Domingo De Abreu    schedule 25.03.2012    source източник
comment
Най-доброто ми предположение е, че един от низовете не е завършен с нула. Имах този проблем няколко пъти, когато за първи път изучавах C.   -  person forivall    schedule 25.03.2012


Отговори (4)


Изглежда, че не разпределяте TEMP, за да бъде достатъчно голям

TEMP = malloc( sizeof(char)*( strlen(direntp->d_name) + 25 ) );

би трябвало:

TEMP = malloc( sizeof(char)*( strlen(direntp->d_name)*3 + 25 + 1 ) );

Отпечатвате d_name три пъти и също така се нуждаете от допълнителен байт за нулевия терминатор.

По същия начин на този ред:

   f.name = malloc( sizeof(char)*( strlen(direntp->d_name)*2 + 3 ) );

би трябвало

   f.name = malloc( sizeof(char)*( strlen(direntp->d_name)*2 + 3 + 1) );

за отчитане на нулевия терминатор.

Използването на твърдения като това може да ви помогне да се уверите, че вашите изчисления са правилни:

    int TEMP_size = strlen(direntp->d_name)*3 + 25 + 1; 
    TEMP = malloc(sizeof(char)*TEMP_size);

    if ( TEMP == NULL ) {
        perror("Malloc Error: ");
        exit(1);
    }

    if ( sprintf(TEMP, "\n%s/%s.a: force\n\t$(MAKE) -C %s\n",direntp->d_name, direntp->d_name, direntp->d_name) < 0 ) {
      perror("Sprintf Error: ");
      exit(1);
    }
    assert(strlen(TEMP)+1==TEMP_size);
person Vaughn Cato    schedule 25.03.2012
comment
Съжалявам, кодът, който публикувах, беше старият и разпределението на TEMP беше коригирано, но грешката в сегментирането не идва от TEMP, а от sprintf(f.name.....). Това е, което ми се стори странно, дори с f.name = malloc( sizeof(char)*( strlen(direntp-›d_name)*2 + 3 + 1) ) все още стартира същата грешка. - person Domingo De Abreu; 25.03.2012

"%s/%s.a", така че мисля, че ако "%s" е с дължина n, тогава целият низ ще вземе n*2+1+1+1+1=2n+4, а не 2n+3... не забравяйте завършващия '\0',,, но що се отнася до това дали това трябва да причини грешка в сегментирането... нямам представа...

//Да, много грешки относно броя на байтовете за разпределяне в тази част от кода ...

person phoeagon    schedule 25.03.2012

Не сте заделили достатъчно памет за този sprintf:

sprintf(TEMP, "\n%s/%s.a: force\n\t$(MAKE) -C %s\n",direntp->d_name, direntp->d_name, direntp->d_name

Вие разпределяте sizeof(direntp->d_name) +25, но аз виждам 25 char + 3* sizeof(direntp->d_name) +1 допълнителен char (вашият \0, който показва края на вашия низ)

за разбирането

person grifos    schedule 25.03.2012
comment
Благодаря, но както казах в другите коментари, това не беше грешката, въпреки че TEMP вече има корекцията, грешката на seg идва от sprintf(f.name.....) - person Domingo De Abreu; 25.03.2012
comment
sprintf(f.name, %s/%s.a, direntp-›d_name, direntp-›d_name, виждам 2* strlen(direntp-›d_name) + 3 символа + 1 допълнителен знак (\0 за край на низ). Вие разпределени 2*strlen(direntp-›d_name) + само 3 знака. Пропускате интервала \0 на вашия низ. Редактиране на arf не видяхте, че сте опитвали това преди. - person grifos; 26.03.2012

Някои бележки в допълнение към предишните отговори:

  1. Повечето съвременни Unix-подобни системи имат asprintf() и vasprintf() в стандартната библиотека, която разпределя буфера сами. Използването им е по-лесно от изчисляването на необходимия размер, разпределяне и извикване на sprintf(), дори ако преразпределят за малко.

  2. Попълването на буфер и след това отпечатването му в stdout с помощта на write() изглежда като ненужно усложнение спрямо обикновения printf(). Използването на stdio за такива задачи е много по-добро, освен ако не използвате нещо, което stdio не може да обработва стабилно (като неблокиращ I/O).

  3. Задачата, която изпълнявате, се вписва много по-добре в някой скриптов език (Perl, Python, Tcl, Ruby, какъвто и да е), освен ако някои външни странни обстоятелства не ви принуждават да използвате C; Предполага се, че C по Unix начин запълва области, по-близки до ядрото и ниско ниво. Вижте напр. "Изкуството на програмирането на Unix" за разсъждения.

person Netch    schedule 25.03.2012
comment
Това е проект от моя университет, затова трябва да използвам C, знам, че asprintf() е по-добър, но имам някои ограничения какви команди мога да използвам. - person Domingo De Abreu; 25.03.2012