Почему результат strcat пустой

#c #pointers #dynamic-memory-allocation #string-literals #strcat

#c #указатели #динамическое выделение памяти #строковые литералы #strcat

Вопрос:

 char* path = malloc(128);
path = "/bin/"
char* path2 = malloc(128);
path2 = "ls"
strcat(path,path2);
fprintf(stderr, "%sn",path);
  

Результат пустой.

Но если я использую

 char path[128] = "/bin/";
char* path2 = malloc(128);
path2 = "ls"
strcat(path,path2);
fprintf(stderr, "%sn",path);
  

в чем разница между
char path[128] и malloc?

Ответ №1:

В приведенном ниже коде

 char* path = malloc(128);
path = "/bin/";                   //added ;, you're overwriting the returned pointer
char* path2 = malloc(128);
path2 = "ls";                     // same as above
strcat(path,path2);               // the destination buffer is non-modifiable!!
fprintf(stderr, "%sn",path);
  

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

Второй фрагмент менее проблематичен

 char path[128] = "/bin/";   // this is to be used as destination, OK
char* path2 = malloc(128);
path2 = "ls";               // you wouldn't need malloc here            
strcat(path,path2);
fprintf(stderr, "%sn",path);
  

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

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

1. почему я могу получить правильный результат, даже если path2 неверен в моем втором примере?

2. @MarsEclipse Случай 1: неопределенное поведение. Случай 2: это не синтаксическая ошибка, но этого недостаточно, чтобы помешать вам запустить программу и получить выходные данные. Утечка памяти становится проблемой в более поздний момент времени.

Ответ №2:

char* не работает как «строковый тип» на других языках. Это указатель, поэтому, когда вы пишете:

 char* path = malloc(128);
path = "/bin/"
  

Память, возвращаемая malloc утечкой, поскольку вы потеряли указатель на нее.

Затем, когда вы пытаетесь объединить память в память, где "/bin/" был сохранен литерал, вы получаете неопределенное поведение, поскольку вы пытаетесь изменить память, которая поддерживает строковый литерал.

Ответ №3:

Позвольте мне прокомментировать это для вас…

В первом примере происходит утечка 256 байт памяти и может произойти сбой, в зависимости от того, где константы находятся в памяти.

 // Declare pointer path; malloc 128 bytes of heap memory and assign the address
char* path = malloc(128);
// Reassign the pointer (losing track of the malloc) to a string constant
// (could be in read-only memory if the compiler has deemed it wise)
path = "/bin/";
// Declare pointer path2; malloc 128 bytes of heap memory and assign the address
char* path2 = malloc(128);
// Reassign the pointer (losing track of the malloc) to a string constant
// (could be in read-only memory if the compiler has deemed it wise)
path2 = "ls";
// Attempt to concatenate strings; if `path` resides in read-only memory, this will segfault.
strcat(path, path2);
fprintf(stderr, "%sn",path);
  

Во втором примере происходит утечка 128 байт памяти, но не должно произойти сбоя (с этими значениями; например, с пользовательским вводом, вы должны быть осторожны в использовании strncat ).

 // Declare pointer path to point to 128 bytes of stack memory (assuming in-function)
// and initialize it to "/bin/"
char path[128] = "/bin/";
// Declare pointer path2; malloc 128 bytes of heap memory and assign the address
char* path2 = malloc(128);
// Reassign the pointer (losing track of the malloc) to a string constant
// (could be in read-only memory if the compiler has deemed it wise)
path2 = "ls";
// Concatenate path2 to path in stack memory; this is fine (but could overflow)
strcat(path, path2);
fprintf(stderr, "%sn",path);
  

Правильным способом было бы что-то вроде этого (если вы не собираетесь возвращаться path , который распределяется по стеку). Однако это не заботится о переполнениях.

 // Declare pointer path to point to 128 bytes of stack memory (assuming in-function)
// and initialize it to "/bin/"
char path[128] = "/bin/";
// Concatenate the constant "ls" to path in stack memory (could still overflow)
strcat(path, "ls");
fprintf(stderr, "%sn",path);