Подтвердить что ты не робот

Когда выделять память на char *

Я немного смущен, когда выделяю память на char * и когда указывать ее на строку const.

Да, я понимаю, что если я хочу изменить строку, мне нужно выделить ее.

Но в тех случаях, когда я не хочу изменять строку, на которую я указываю, и мне просто нужно передать значение, я должен просто сделать это ниже? Каковы недостатки следующих шагов по сравнению с распределением памяти с помощью malloc?

char *str = NULL;

str = "This is a test";

str = "Now I am pointing here";
4b9b3361

Ответ 1

Повторите свой пример с предупреждающим знаком -Wwrite-strings, вы увидите предупреждение:

warning: initialization discards 'const' qualifier from pointer target type

Это потому, что тип "Это тест" const char *, а не char *. Таким образом, вы теряете информацию о constost, когда вы назначаете литеральный адрес указателю.

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

Это, однако, плохое поведение, и я предлагаю вам использовать -Wwrite-strings все время.

Если вы хотите доказать это сами, попробуйте изменить строку:

char *str = "foo";
str[0] = 'a';

Это поведение программы undefined, но во многих системах может наблюдаться ошибка сегментации. Запустив этот пример с Valgrind, вы увидите следующее:

Process terminating with default action of signal 11 (SIGSEGV)
  Bad permissions for mapped region at address 0x4005E4

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

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

  • Система ввода C, которая поможет вам написать правильный код и может быть легко "отключена" (путем кастингов и т.д.).

  • Разрешения страницы памяти ядра, которые предназначены для защиты вашей системы и которые всегда должны выполняться.

Опять же, по историческим причинам, это точка, где 1. и 2. не согласны. Или, чтобы быть более понятным, 1. намного более разрешительный, чем 2. (в результате ваша программа была убита ядром).

Итак, не комментируйте, строковые литералы, которые вы объявляете, действительно постоянны, и вы ничего не можете с этим поделать!

Учитывая ваш указатель str чтение и запись в порядке. Однако, чтобы написать правильный код, он должен быть const char *, а не char *. При следующем изменении ваш пример является допустимым фрагментом C:

const char *str = "some string";
str = "some other string";

(const char * указатель на строку const)

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

Примечание. Указатель const для строки const, являющийся const char *const:

const char *const str = "foo";

Эмпирическое правило: всегда должно быть как можно более постоянным.

Если вам нужно изменить строку, используйте динамическое распределение (malloc() или лучше, некоторые функции управления строкой более высокого уровня, такие как strdup и т.д. из libc), если вам не нужно, используйте строковый литерал.

Ответ 2

Если вы знаете, что str всегда будет доступен только для чтения, почему бы не объявить его как таковой?

char const * str = NULL;
/* OR */
const char * str = NULL;

Ну, на самом деле есть одна причина, почему это может быть сложно - когда вы передаете строку в функцию только для чтения, которая не объявляет себя как таковую. Предположим, вы используете внешнюю библиотеку, объявляющую эту функцию:

int countLettersInString(char c, char * str);
/* returns the number of times `c` occurs in `str`, or -1 if `str` is NULL. */

Эта функция хорошо документирована, и вы знаете, что она не будет пытаться изменить строку str, но если вы вызываете ее с постоянной строкой, ваш компилятор может дать вам предупреждение! Вы знаете, что в этом нет ничего опасного, но ваш компилятор этого не делает.

Почему? Поскольку, что касается компилятора, возможно, эта функция пытается изменить содержимое строки, что приведет к сбою вашей программы. Возможно, вы очень сильно полагаетесь на эту библиотеку, и есть много функций, которые все ведут себя так. Тогда, возможно, проще не объявлять строку как const в первую очередь - но тогда все зависит от вас, чтобы вы не пытались ее модифицировать.

С другой стороны, если вы пишете функцию countLettersInString, просто убедитесь, что компилятор знает, что вы не будете изменять строку, объявив ее с помощью const:

int countLettersInString(char c, char const * str);

Таким образом, он будет принимать как постоянные, так и непостоянные строки без проблем.

Ответ 3

Одним из недостатков использования строковых литералов является то, что они имеют ограничения по длине. Поэтому вы должны иметь в виду документ ISO/IEC: 9899 (акцент мой)

5.2.4.1 Пределы перевода

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

[...]

- 4095 символов в литеральной строке символа или в широком строковом литерале (после конкатенации)

Итак, если ваш постоянный текст превышает этот счет (что может быть несколько раз, особенно если вы пишете динамический веб-сервер в C) вам запрещено использовать строковый литерал, если вы хотите оставаться независимым от системы.

Ответ 4

В вашем коде нет проблем, если вы не планируете изменять содержимое этой строки. Кроме того, память для таких строковых литералов останется на весь срок службы программы. Память, выделяемая malloc, чтение-запись, поэтому вы можете манипулировать содержимым этой памяти.

Ответ 5

Если у вас есть строковый литерал, который вы не хотите изменять, что вы делаете, это нормально:

char *str = NULL;
str = "This is a test";
str = "Now I am pointing here";

Здесь str указатель имеет память, на которую указывает. Во второй строке вы пишете в эту память "This is a test", а затем снова в 3 строках, которые вы пишете в этой памяти "Now I am pointing here". Это законно в C.

Вы можете найти это немного противоречивым, но вы не можете изменить строку, что-то вроде этого -

str[0]='X' // will give a problem.

Однако, если вы хотите изменить его, используйте его как буфер для хранения строки ввода и т.д., используйте malloc:

char *str=malloc(BUFSIZE);   // BUFSIZE size what you want to allocate
free(str);                   // freeing memory

Используйте malloc(), когда вы не знаете объем памяти, необходимый во время компиляции.

Ответ 6

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

Скажем

str[0] = 'Y'; //No compiler error, undefined behavior

Ответ 7

Он будет работать нормально, но вы можете получить предупреждение от компилятора, потому что вы указываете на константу.

P.S.: Он будет работать только тогда, когда вы его не модифицируете. Поэтому единственным недостатком использования malloc является то, что вы не сможете его изменить.