Не могу понять, почему я получаю ошибку сегментации из-за strncpy

#c #malloc

#c #malloc

Вопрос:

При использовании я получаю ошибку сегментации strncpy , и я не могу понять, как это исправить.

Вот мой rectangle.h файл. Это всего лишь заголовочный файл.

 #define NAMESIZE 20

struct point {
    int x;
    int y;
};

struct rectangle {
    struct point upperleft;
    struct point lowerright;
    //char label[NAMESIZE   1];
    char *label;
};

struct point *create_point(int x, int y);

struct rectangle *create_rectangle(struct point ul, struct point lr, 
                                   char *label);

int area1(struct rectangle r);
int area2(struct rectangle *r);
void change_label(struct rectangle *r, char *newlabel);
void broken_change_label(struct rectangle r, char * newlabel);
void print_rectangle(struct rectangle *r);
  

Это мой rectangle.c файл. Я просто собираюсь показать одну функцию из него:

/* create_rectangle динамически выделяет память для хранения прямоугольника, присваивает ему * начальные значения и возвращает указатель на вновь созданный прямоугольник. */

 struct rectangle *create_rectangle(struct point ul, struct point lr, 
                                   char *label) {

    struct rectangle *r = malloc(sizeof(struct rectangle));
    /* TASK 1: fill in the rest of this function */
    r->upperleft = ul;
    r->lowerright = lr;
    r->label = malloc(strlen(label) * sizeof(char));
    strncpy(r->label, label, strlen(label)   1);

    return r;
}
  

В приведенном выше коде я использую malloc , чтобы выделить достаточно места для указателя метки.

Когда я запускаю tester.c приведенную ниже программу, я получаю ошибку ошибки сегментации (из-за strncpy ).

 int main(void) {    

    char *str1 = "Big rectangle";
    char *str2 = "Square";

    struct point *p1 = create_point(10, 10);
    struct point *p2 = create_point(100, 100);

    struct rectangle *r1 = create_rectangle(*p1, *p2, str1);
    print_rectangle(r1);
    printf("    expecting: (10, 10) (100, 100) Big rectanglen");

    free(p2);
    p2 = create_point(20, 20);

    struct rectangle r2;

    strncpy(r2.label, str2, NAMESIZE); //GETTING SEGMENTATION FAULT DUE TO THIS LINE
}
  

Я подозреваю, что я получаю ошибку ошибки сегментации, потому что я делаю strncpy это напрямую r2.label . Я подозреваю, что из-за того, что я не выделяю никакого пространства char *label в структуре rectangle, я получаю ошибку ошибки сегментации. Но когда я пишу

 char *label = malloc(sizeof(NAMESIZE) * sizeof(char));
  

Я получаю сообщение об ошибке:

 error: expected ';' at end of declaration list` error.
  

Комментарии:

1. Внимательно strncpy подумайте, действительно ли это правильный инструмент для этой работы. Это не просто «безопаснее» strcpy . См. Мою разглагольствование по этой теме , а также вопрос 13.2 из часто задаваемых вопросов comp.lang.c . (Я не предполагаю, что это является причиной проблемы, которую вы видите.)

2. malloc(sizeof(NAMESIZE) * sizeof(char)) — что? NAMESIZE это int, так что, вероятно, это выделяет 4 байта.

Ответ №1:

 strncpy(r2.label, str2, NAMESIZE);
  

Вы пытаетесь выполнить запись в r2.label , который является указателем, которому не было выделено никакого места.

И будьте осторожны с кодом, который вы выделили пространство:

 r->label = malloc(strlen(label) * sizeof(char));
  

strlen(label) недостаточно, строка должна заканчиваться нулем.

Комментарии:

1. Что произойдет, если строка не завершается нулем?

2. Тогда это не строка. Значение string таково: серия символов, за которыми следует нулевой терминатор.

Ответ №2:

Этот код:

 r->label = malloc(strlen(label) * sizeof(char));
strncpy(r->label, label, strlen(label)   1);
  

должно быть:

 r->label = malloc( strlen(label)   1 );
strcpy( r->label, label );
  
  • Вам нужно выделить место для нулевого терминатора.
  • sizeof(char) всегда 1
  • strcpy Функция завершается после записи strlen(label) 1 байтов, поэтому пытаться использовать ее излишне strncpy .

strncpy в целом довольно опасно, потому что иногда он не выводит строку; мой совет — никогда не использовать его.

Далее вниз,

 strncpy(r2.label, str2, NAMESIZE);
  

неверно, потому r2.label что в настоящее время является диким указателем. Вам также необходимо выделить память таким же образом:

 r2.label = malloc( strlen(str2)   1 );
strcpy( r2.label, str2 );
  

Комментарии:

1. Есть ли какой-либо способ зарезервировать место для строкового литерала метки в самой структуре? Что-то вроде: char * label = malloc(sizeof(NAMESIZE) 1);

2. Да, измените на char label[NAMESIZE 1];

3. Прекратите писать sizeof(NAMESIZE) . NAMESIZE есть 20 . sizeof(NAMESIZE) означает sizeof(20) , то есть количество байтов, необходимых для хранения int 20 . То есть sizeof(int) , это 4 или 8 в обычных системах.

Ответ №3:

Похоже, как вы сказали, вы не выделяете никакой памяти для своего char* label .

Сделать:

 r2.label = malloc(sizeof(NAMESIZE) * sizeof(char));`
  

Ответ №4:

Вы malloc strlen(label) вводите байты для метки create_rectangle , затем копируете в strlen(label) 1 байтах. Обратите внимание, что если новая метка, в которую вы копируете, длиннее, вы переполните это выделение. Но, как указывает @YuHao, это не имеет значения, потому что вы никогда не инициализировали ни одно из полей r2 (в частности, label ).

Ответ №5:

 r->label = malloc(strlen(label) * sizeof(char));
  

Вы неправильно распределяете завершающий символ, он должен быть

 r->label = malloc((strlen(label) 1) * sizeof(char));