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

Почему я могу использовать указатели как строки при объявлении с двойными кавычками, но не фигурные скобки, в 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.

4b9b3361

Ответ 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] сохраняет данные в стеке. Данные в стеке могут быть изменены, но данные в текстовом сегменте не могут быть изменены.