Почему я могу использовать указатели как строки при объявлении с двойными кавычками, но не фигурные скобки, в C?
Если я объявляю и использую указатель следующим образом:
int counter;
char *pCow = "pCow goes MOO";
for(counter = 0; counter < 14; counter++)
printf("%c", pCow[counter]);
он отображает всю строку и работает, и да, и есть много радости.
Однако, если я использую инициализатор следующим образом:
int counter;
char *pCow = {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};
for(counter = 0; counter < 14; counter++)
printf("%c", pCow[counter]);
программа выходит из строя, и pCow отказывается от moo для моего гедонистического удовольствия!
3 Warnings. 0 Errors
line 11 (near initialization for 'pCow') [enabled by default] C/C++ Problem
line 11 excess elements in scalar initializer [enabled by default] C/C++ Problem
line 11 initialization makes pointer from integer without a cast [enabled by default] C/C++ Problem
Любопытно протестирован в CDT Eclipse.
Ответы
Ответ 1
В этом случае pCow
устанавливается в адрес c-строки в статической памяти:
char *pCow = "pCow goes MOO";
В этом случае pCow
установлено значение 'p'
(т.е. 112
):
char *pCow = {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};
Так как адрес 112
, скорее всего, указывает на ограниченную/недопустимую память, что заставляет вашу программу взорваться при попытке получить доступ к pCow[counter]
.
Предупреждение "лишние элементы в скалярном инициализаторе" говорит вам, что он игнорирует все материалы после 'p'
, поскольку указателю требуется только одно значение.
Предупреждение "инициализация делает указатель из целого без приведения" говорит вам, что вы используете 'p'
в качестве указателя, что, вероятно, не очень хорошая идея...
Что вы хотите сделать, это объявить pCow
как массив символов, а не указатель на символ, если вы хотите использовать синтаксис инициализатора:
char pCow[] = {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};
Ответ 2
"pCow goes MOO"
является строковым литералом и имеет два разных использования. Либо вы можете использовать его как инициализатор для массива:
char aCow[] = "pCow goes MOO";
В этом случае содержимое строкового литерала копируется в массив.
Или, альтернативно, вы можете использовать строковый литерал как автономный постоянный массив в любой точке вашей программы. Например strcpy(cow, "pCow goes MOO");
. Таким образом, существует отличительная разница между этими двумя:
char aCow[] = "pCow goes MOO";
char* pCow = "pCow goes MOO";
В первом случае литерал копируется в массив. Во втором случае литерал остается автономной константой в постоянной памяти, на которую мы указываем указатель. Первый может быть изменен, последний не может.
Что касается случая
char *pCow = {'p','C','o','w',' ','g','o','e','s',' ','M','O','O','\0'};
Вы используете указатель, но у вас нет строкового литерала. Вместо этого у вас есть список инициализаторов, предназначенный для массива. Хороший компилятор предупредит вас о "избыточном инициализаторе". Причина, по которой компилируется код, является очень странным правилом в C, что позволяет инициализировать простые переменные с помощью фигурных скобок, например int x = {1};
. Поэтому компилятор использует это правило для инициализации вашего указателя, указывая на адрес 'p'
, который, конечно же, вздор, а затем он отбрасывает остальную часть списка инициализаторов.
Ответ 3
Поскольку char * указывает данные на сегмент Text в памяти, а char [size] сохраняет данные в стеке. Данные в стеке могут быть изменены, но данные в текстовом сегменте не могут быть изменены.