В чем разница в указателях при определении char и int?

Я понимаю основы работы указателей, но следующий пример меня смущает.

int *myNum = 10; // Produces an error

char *myChar = "Something"; // Works fine

Почему назначение char работает, но целое не имеет значения (возможно, причина char рассматривается как массив)?

Также, что меня смущает при прямом назначении переменной указателя, автоматически ли он получает адрес?

char *myChar = "Something";

и

char myChar = "Something";
char *charAddr = &myChar;

Какая разница здесь или равна?

Ответ 1

"Something"

существенно сокращается:

static const char some_hidden_array[] = {'S', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', '\0'};
some_hidden_array

То есть, когда вы пишете "Something", компилятор генерирует массив за кулисами и дает вам указатель на начало этого массива. Так как это уже указатель на char, вам не составит труда назначить его переменной типа "указатель на char" (написанный как char*).

10

не подходит для чего-либо подобного. Это просто номер 10 - это не указатель на массив, содержащий число 10, или что-то в этом роде.

Обратите внимание, что a char - это один символ, а не строка, поэтому синтаксис строки необычен по сравнению с большинством других типов - строка содержит несколько символов, а не только одну. Если вы попытаетесь использовать простой старый char, вы увидите то же самое:

char *myChar = 'a'; // error

или для любого другого типа:

float *myFloat = 42.1f; // error
Другими словами, не странно, что 10 дает ошибку - если что-то странное, что "Something" нет. (По крайней мере, странно, пока вы не знаете, как работают строковые литералы)

Ответ 2

Это то же самое (магия от компилятора не происходит). По умолчанию литералы типа 10 являются значениями int, а не int *.

Вам нужно бросить:

int *myNum = (int*)10; // Need to cast
char *myChar = "Something"; // No need to cast "..." is already a char*

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

Что касается вашего второго вопроса, "..." рассматривается как непрерывная последовательность char в памяти, подобной массиву, и эквивалентна char *.

Для продуманного понимания C, указателей и различий между массивами и указателями вы должны прочитать следующее: Программирование программистов на языке C: Deep C Secrets by Peter van der Linden.

Ответ 3

Когда вы выполняете char *myChar = "Something";, вы создаете литерал-строку только для чтения где-то в памяти, которая заканчивается нулевым символом. Теперь это что-то особенное с компилятором, что он интерпретирует кусок переменных char, хранящихся непрерывно и заканчивающихся нулевым символом в виде строки. Таким образом, в основном вы создали массив символов, а когда вы делаете *myChar*, он возвращает строку.

В случае целых чисел или любых других типов данных он различает int *ptr как указатель на целое число и int ptr как целое число. Вероятно, вы получаете ошибку, потому что введенный вами адрес может быть недействительным/доступным для вас.

Кроме того, делая

char myChar = "Something";  //this is an error, since char can hold one character
char *charAddr = &myChar;

Обратите внимание, что myChar и &myChar совпадают, так как myChar является указателем!

Изменить: см. здесь о строковых литералах: Можно ли изменить строку char в C?

Ответ 4

Почему назначение char работает, но целое не имеет значения (возможно, причина char рассматривается как массив)?

Вы правы, "Something" является строковым литералом и может рассматриваться как массив char. После char *myChar = "Something"; происходит следующее: выделяется длина + 1 байт памяти, где "Something" будет сохранен, myChar указывается на начальный адрес этой памяти. Строковые литералы несколько особенные.

Вот общий способ инициализации массива с постоянными значениями:

// valid initializations;
char s2[] = { 'a', 'b', 'c' };
int a[] = { 1, 2, 3 };
char s1[] = "123";

Также, что меня смущает при прямом назначении переменной указателя, автоматически ли он получает адрес?

Да.

Взгляните на 8.5.2 Массивы символов документов С++

Ответ 5

Хотя теоретически первый int *myNum = 10 имеет смысл, особенно если вы знаете, что есть полезный int по адресу ten — в общем, он редко бывает полезен и потенциально опасен.

Однако существуют определенные назначения указателей, которые широко используются и достаточно безопасны:

int *myNum = 0;

В 99,9 +% современных архитектур процессора это то же самое, что

int *myNum = NULL;

См. определение NULL в <stddef.h> здесь.

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

int k, *p = &k;