2010-04-07 3 views
0

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

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

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

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.

ответ

-3

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

+0

Я не понимаю, почему я хотел бы изменить переменную tp. Это указатель, и он указывает на некоторую память, которая уже была выделена для структуры токенов. Если я malloc, это будет выделять некоторую новую память, которая не является структурой токенов, не так ли? И я не хочу этого. – Joe

+2

Вы ошибаетесь в отношении права собственности на память, на которую ссылается «tp» в примере кода. Это адрес структуры, которая была объявлена ​​в стеке. Совершенно законно использовать указатель на него, как показано, до тех пор, пока ссылочная структура не выходит за рамки. В этот момент память освобождается, и ее более безопасно ссылаться. Ошибка в коде OP находится в другом месте (см. Мой ответ). –

+0

-1: Это не имеет абсолютно никакого отношения к его проблеме. – rampion

7

Следуя линию

tokens.mnem = "load" 

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

9

Проблема заключается в том, что 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, хотя у вас все еще есть риск перебора вашего буфера.

+1

Если вы собираетесь обрабатывать mnem как строку, вы должны сделать ее одним символом дольше и убедиться, что она завершена нулем. В противном случае вы можете найти другие поля и gunk, добавленные к нему в sprintf. –

+0

@Ian G: Хороший момент, я обновил свой ответ, чтобы отразить это предложение. –

2

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

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

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

Также обратите внимание, что strncpy() имеет довольно странную семантику. Проверьте, есть ли у вас strlcpy(); это лучшая функция.

3

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

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

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

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

2

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

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

пытается скопировать четыре символа из «линии» в месте, где находится строковый литерал, присвоенный здесь:

tokens.mnem = "load"; 

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

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

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

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

0

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

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

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

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

Смежные вопросы