Правильное использование / создание динамических cstrings в C с помощью malloc?


Следующий код всегда segfaults:

  char *test3 = (char *) malloc(sizeof(char) * 5);
  test3 = "asdf";
  printf("%sn", test3);

Следующий код не сегментируется:

  char *test3 = (char *) malloc(sizeof(char) * 5);
  test3[0] = 'a';
  test3[1] = 'b';
  test3[2] = 'c';
  test3[3] = 'd';
  test3[4] = '';
  printf("%sn", test3);

Я предполагаю, что вопрос может заключаться в том, как мне назначить литерал cstring динамически создаваемому cstring?

4 3

4 ответа:

Правильный способ "заполнить" строку:

 strcpy(test3, "abcd"); 

Однако я настоятельно рекомендую вам не использовать malloc [и определенно не использовать (char *) malloc(...) - поскольку это может скрыть некоторые довольно неприятные ошибки, которые подпрыгивают и кусают вас, по крайней мере, в подходящий момент, поскольку ошибки имеют тенденцию делать это - вы, вероятно, делаете это, потому что вы компилируете свой C-код как C++-код, что неправильно и учит вас плохим привычкам, таким как эта].

Использование malloc для выделения маленьких строк является большим пустая трата пространства. Ваша строка из 5 символов, вероятно, имеет накладные расходы в размере 16-32 байт и будет округлена до 8 или 16 байт. Таким образом, в общей сложности он может использовать 48 байт, хранить 5 байт - это большая потеря пространства.

Другие (правильно) предложили скопировать строку в выделенную память.

Вот почему ваш подход является segfaulting: строка "asdf" является строковым литералом, и во время компиляции она сохраняется в rodata, или только для чтения данных. Когда ваша программа пытается

test3 = "asdf";

Он пытается создать указатель на rodata. C не допускает указателей в rodata, и поэтому ваше утверждение не только не работает, но и seg ошибается.

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

Во-первых, спасибо за этот вопрос у него есть интересная морщинка.

Я запустил ваш код с Eclipse / Microsoft C и не получил ошибки сегментации, и он напечатал "asdf", как ожидалось.

Однако это не означает и не подразумевает, что вы не получаете ошибки сегментации. Ваш результат подразумевает рассмотрение того, как компилятор реализует два оператора:
   char *test3 = (char *) malloc(sizeof(char) * 5);

Выделяет хранилище в куче и устанавливает указатель test3, чтобы указать на это место. Следующий оператор также обновляет тот же указатель.

   test3 = "asdf";

Однако в этом случае test3 указывает на литерал "asdf", где-либо этот литерал хранится. Некоторые компиляторы генерируют пул литералов строк и хранят их где-то в исполняемом файле, поэтому для некоторых компиляторов эти литералы не могут быть изменены.

Так зачем компилятору хранить литерал там, где к нему нельзя получить доступ? Не имеет смысла, отсюда вопрос: какой компилятор Си вы используете? И что это за версия с придерживаясь??

Чтобы обойти то, что может быть ошибкой компилятора, и все же указать test3 на литерал, попробуйте?? (Опять же, компиляторы C различаются тем, что и как они реализуют языковые конструкции.)

  const char *literal = "asdf";  // also try without a const stmt 
  // other code here
  test3 = literal;
Наконец, во втором примере хранилище в куче, которое было malloced, модифицируется и, очевидно, адресуемо.

Вы не можете назначить строковое значение с " = " в вашем случае.

Вам нужно использовать функцию strcpy или sprintf. В конце программы (или когда строка больше не используется) не забудьте ее освободить ! Например:

#define BUFSIZE 5

int main(void) {
    char *test3 = malloc(sizeof(char) * BUFSIZE);
    snprintf(test3,BUFSIZE,"test");
    printf("%s\n", test3);
    free(test3);
    return 0;
}

Или вы можете просто написать:

int main(void) {
    char buf[BUFSIZE] = "test";
    printf("%s\n", buf);
    return 0;
}