2016-01-06 3 views
4

Ниже приведен фрагмент кода с начала программы, которую я пишу (в которой содержится ошибка).Malloc, strlen, strcat

char *name; 
    char *name2; 

    if (argn != 2) { 
     printf("You have to enter the name of the input file"); 
     return 1; 
    } 

    name = malloc(strlen(arg[1]) + 1);  
    name2 = malloc(strlen(arg[1]) + 1); 

    strcpy(name, arg[1]); 
    strcpy(name2, arg[1]); 

    strcat(name2, "-results.pdb"); 

Существует ошибка здесь что с strcat, действительно name2 не имеет достаточного размера для выполнения операции выше. Тем не менее strcat выполняет без проблем. Однако позже в полностью несвязанной части программы операция с другим массивом, который был инициализирован после этого strcat, дает ошибку. Это целочисленный массив, для которого я присваиваю значения его элементам, и он дает ошибку, прежде чем я могу назначить все значения. Я предполагаю, что, поскольку для операции над ним недостаточно памяти в имени2, это как-то влияет на следующие инициализированные массивы. Я хотел бы понять:

1 Что может произойти здесь, так что дополнительная информация, которая не может быть записана в имя2, влияет на другие массивы, объявленные позже?

2- Возможно, я не смог бы легко справиться с этой проблемой в более сложной программе, поскольку ошибка возникает в другом месте, а не в strcat. Как я могу предотвратить такие скрытые ошибки, как процесс memory problematic, влияющий на абсолютно несвязанные массивы в другом месте?

+0

(1) - трудно сказать, так как вы не отправляли код с проблемой; (2) проверять длины перед использованием 'strcat' и других подобных функций –

+0

, к сожалению, код слишком длинный с несколькими заголовочными файлами. Могу ли я спросить, есть ли отладчик кода, который бы распознал эту ошибку? cppcheck doesn't. – Sina

+0

не может быть заменой для правильного изучения языка –

ответ

10

И все же strcat выполняет без проблем.

Нет, это не так. Он возвращается, но он запустил бомбу замедленного действия. Как вы заметили позже.

Что происходит undefined поведение. Вы записали в память, которую вам не разрешили писать. Независимо от того, что было сохранено, теперь есть мусор, и какой бы код не ожидал найти значимые ценности, в настоящее время существует неправильное поведение. Особенно, если внутренние данные malloc были повреждены, наблюдение происходит случайным сбоем при попытке перераспределить или освободить память позже.

Правильный путь, чтобы выделить память с

name2 = malloc(strlen(arg[1]) + sizeof "-results.pdb"); 

Это заботится о «+1» для завершающего NUL, поскольку sizeof "-results.pdb" является 13.

Еще проще в использовании asprintf (не ISO C, но доступный для любого современного Unix), который выделяет память по мере необходимости:

asprintf(&name2, "%s-results.psb", arg[1]); 

Существует! Нет strlen, нет strcat, no sizeof, no malloc. Просто все-в-один вызов, делающий Right ThingTM.

+0

Ну, тогда, может быть, я должен попросить ссылку, в которой говорится об этом «неопределенном поведении», чтобы в следующий раз у меня был один, я знаю, где искать. Такие проблемы обычно дают мне головную боль, так как мои коды становятся все больше и больше. Как насчет контрольных кодов? cppcheck не обнаруживает это, например. – Sina

+1

@Sina Стандарт ISO C является каноническим источником, если вы хотите знать все места, где может возникнуть неопределенное поведение. К сожалению, в целом не все случаи могут быть обнаружены автоматически. Некоторые оттенки лучше других. Некоторые коммерческие инструменты подходят к большой длине (Gimpel's FlexeLint, Mathworks 'Polyspace, чтобы назвать два, которые я использую на работе). – Jens

+0

По неопределенным вы имеете в виду, что какое-то случайное место в памяти перезаписано? Он всегда производит ту же ошибку в одном и том же месте, поэтому я предполагаю, что это не случайное или неопределенное? Почему он называется неопределенным? – Sina

5

Как руководству strcat сказал вам:

char *strcat(char *dest, const char *src);

strcat() функция добавляет Src строку в Dest строку, перезаписана завершающий нулевой байт ('\ 0') в конце dest и , то добавляет завершающий нулевой байт. Строки могут не перекрываться, а строка dest должна иметь достаточно места для результата.Если dest не большой достаточно, поведение программы непредсказуемо; Переполнение буфера является любимым средством для атаки на защищенные программы.

Так непредсказуемый означает «все может случилось», ваша ситуация является один видом всех.

Вы должны знать, что все действительно все, поэтому программа может обрушиться на вызов strcat, может даже работать так, как вы ожидали (на этот раз), может произойти сбой где-то в другом месте, поскольку она перезаписана некоторой памятью, используемой внутренними компонентами malloc, например, и теперь он не знает, что освободить. Фактически это зависит от вашей системы и где int memory - ваш char *dest, и это может меняться каждый раз, когда вы запускаете свою программу.

Вот почему это всегда лучше использовать strncat, так что вы можете указать размер буфера, или даже вы можете использовать asprintf для объединения строк, он будет выделять больше памяти для вас, как это необходимо.

Для примера вы бы написать что-то вроде этого:

char *newstr = NULL; 
asprintf(&newstr, "%s%s", name2,"-results.pdb"); 

И тогда вы будете иметь указатель на новую строку malloc эд, в вашем newstr, не забудьте освободить его после.

+2

Эта страница руководства должна использовать технический термин ** неопределенное поведение ** вместо ** непредсказуемого **. – Jens

+0

Ну, это из linux default 'man 3 strcat'. Я не знаю, почему они так пишут. – coredump

+0

'asprintf' не соответствует ISO C –

-4

Вы могли бы просто быть более щедрым с таНосом

name2 = malloc(strlen(arg[1])+100); 

Это позволит избежать вопроса о размере Dest с strcat, который перезаписью произвольной части памяти вызывают Вас проблемы позже ... Как и других ответы говоря, «неопределенное поведение» ...

Если вы можете использовать компилятор Microsoft C++, то детектор утечки памяти поможет: https://msdn.microsoft.com/en-us/library/e5ewb1h3%28v=vs.90%29.aspx

Добавьте это к вашему Заголовочный файл

#ifdef _Windows 
#include "stdlib.h" 
#include "crtdbg.h" 
#endif 

Затем использовать эти функции в коде, в основном, перед тем многое другое:

_CrtSetDbgFlag 
_CrtSetBreakAlloc 

Там должно быть много хороших ресурсов там, и проще в использовании, чем это выглядит , Смотрите эту ссылку для более вариаций:

GCC memory leak detection equivalent to Microsoft crtdbg.h?

+4

Это нехорошее решение, что, если код немного изменится, а данные strcatted длиннее 100? –

+0

NB: OP не запрашивал «хорошего решения» – Grantly

+1

ОП не просил плохого решения либо –

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