Почему я получаю ошибку сегментации при использовании strncpy? - PullRequest
0 голосов
/ 07 апреля 2010

Я получаю ошибку сегментации при использовании strncpy и (указатель на структуру) -> (member) нотации:

Я упростил свой код.Я инициализирую структуру и устанавливаю все ее токены в пустую строку.Затем я объявляю указатель на структуру и присваиваю ей адрес структуры.

Я передаю указатель на функцию.Я могу распечатать содержимое структуры в начале функции, но если я пытаюсь использовать мнемонику tp -> в функции strncpy, я получаю ошибку seg.Может кто-нибудь сказать мне, что я делаю неправильно?

typedef struct tok  {
    char* label;
    char* mnem;
    char* operand;
}Tokens;

Tokens* tokenise(Tokens* tp, char* line)  {
    // This prints "load"
    printf("Print this - %s\n", tp -> mnem);

    // This function gives me segmentation fault
    strncpy(tp -> mnem, line, 4);

    return tp;
}

int main()  {
    char* line = "This is a line";
    Tokens tokens;
    tokens.label = "";
    tokens.mnem = "load";
    tokens.operand = "";

    Tokens* tp = &tokens;
    tp = tokenise(tp, line);

    return 0;
}

Я использовал операторы printf, чтобы подтвердить, что код определенно перестает выполняться в функции strncpy.

Ответы [ 7 ]

9 голосов
/ 07 апреля 2010

Проблема в том, что tp-> mnem указывает на строковый литерал, который обычно размещается в сегменте памяти, доступном только для чтения. Поэтому незаконно перезаписывать его. Скорее всего, вам нужно сделать что-то вроде этого:

Tokens tokens;
tokens.label = "";
tokens.mnem  = strdup("load");
tokens.operand = "";

Это даст вам динамически распределенный блок памяти для mnem, который вы затем можете записать в любое количество раз. Конечно, у вас также есть пара других проблем: во-первых, вам нужно помнить, чтобы освободить эту память с free позже; во-вторых, вам нужно знать размер выделенного буфера, чтобы не перезаписывать его.

Если вы знаете, что содержимое mnem никогда не превысит 4 байта, вы можете вместо этого изменить объявление структуры следующим образом:

typedef struct tok  {
    char* label;
    char mnem[5]; // note: +1 byte for a NULL terminator
    char* operand;
}Tokens;

Тогда вы бы инициализировали это так:

Tokens tokens;
tokens.label = "";
strcpy(tokens.mnem, "load");
tokens.operand = "";

Это освобождает вас от ответственности за управление памятью для mnem, хотя у вас все еще есть некоторый риск переполнения буфера.

7 голосов
/ 07 апреля 2010

Следующая строка

tokens.mnem = "load"

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

3 голосов
/ 07 апреля 2010

Проблема в том, что вы присвоили строковые литералы членам вашей структуры Tokens и пытаетесь перезаписать эту память (в частности, поле mnem) в tokenise.

Большинство современных ОС выделяют память для строковых литералов из специального раздела только для чтения адресного пространства вашей программы. Если вы попытаетесь записать в эту память, ваша программа умрет с ошибкой.

Вот почему тип строкового литерала const char *, а не char *. Ваш компилятор должен предупредить вас, когда вы попытаетесь присвоить их полям tokenise.

Если вы хотите перезаписать память позже, вам нужно динамически выделить память, используя malloc, или изменить элементы структуры Tokens на массивы фиксированной длины, а затем скопировать начальное значение в выделенную память. Конечно, если вы распределяете память динамически, вам нужно free это позже тоже.

2 голосов
/ 07 апреля 2010

Вы получаете ошибку сегментации, потому что эта строка:

strncpy(tp -> mnem, line, 4);

Пытается скопировать четыре символа из строки в место, занятое строковым литералом, как здесь:

tokens.mnem = "load";

Строковый литерал хранится в специальной текстовой части вашей программы и не может быть изменен.

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

tokens.mnem = (char*) malloc (bufferSize);

И освободите буфер, когда вы закончите, используя его.

2 голосов
/ 07 апреля 2010

Вы звоните strncpy(), не выделив буферное пространство, как сказал Shadow.

Литеральная строка "load", для которой вы установили элемент mnem в инициализаторе, не перезаписывается.

Если вы хотите изменить сохраненную строку, и размер является разумным, проще всего изменить объявление поля структуры на char mnem[5];.

Также обратите внимание, что strncpy() имеет довольно странную семантику. Проверьте, есть ли у вас <a href="http://en.wikipedia.org/wiki/Strlcpy" rel="nofollow noreferrer">strlcpy()</a>; это лучшая функция.

0 голосов
/ 07 апреля 2010

Эта строка сомнительна:

strncpy(tp -> mnem, line, 4);

Вы полагаетесь на функцию, которая возвращает указатель на память, которая не выделена. Возвращение *tokenise() не определено. Возвращает указатель на память, которая может содержать все виды вещей, и у вас нет прав на изменение.

Это должно вернуть выделенный указатель.

0 голосов
/ 07 апреля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...