Я думаю, что ваш вопрос сводится к этому:
Если у программиста C есть возможность создать только int x
или просто int *x
(оба статически распределены)
Первый оператор выделяет память для целого числа. В зависимости от размещения оператора он может выделять память в стеке выполняемой в данный момент функции или выделять память в разделах программы .data
или .bss
(если это глобальная переменная или static
переменная, либо в области видимости файла, либо в области функций).
Второй оператор выделяет память для указателя на целое число - он фактически не выделил память для самого целого числа. Если вы попытаетесь присвоить значение с помощью указателя *x=1
, вы получите либо очень быстрое SIGSEGV
нарушение сегментации , либо испортит какой-то случайный фрагмент памяти. C не обнуляет память, выделенную в стеке:
$ cat stack.c
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
int j;
int k;
int *l;
int *m;
int *n;
printf("i: %d\n", i);
printf("j: %d\n", j);
printf("k: %d\n", k);
printf("l: %p\n", l);
printf("m: %p\n", m);
printf("n: %p\n", n);
return 0;
}
$ make stack
cc stack.c -o stack
$ ./stack
i: 0
j: 0
k: 32767
l: 0x400410
m: (nil)
n: 0x4005a0
l
и n
указывают на что-то в памяти - но эти значения являются просто мусором и, вероятно, не принадлежат адресному пространству исполняемого файла. Если мы будем хранить что-либо в этих указателях, программа, вероятно, умрет. Однако он может повредить несвязанные структуры, если они отображаются в адресное пространство программы.
m
по крайней мере - указатель NULL
- если вы попытаетесь записать его, программа наверняка умрет на современном оборудовании.
Ни один из этих трех указателей на самом деле не указывает на целое число. Память для этих целых чисел не существует. Память для указателей существует существует - и изначально заполнена значениями мусора, в данном случае.
Статья в Википедии о L-значениях - в большинстве своем слишком тупая, чтобы полностью ее рекомендовать - содержит одно замечание, которое представляло для меня довольно существенное препятствие, когда я впервые изучал C: В языках с присваиваемые переменные становится необходимым различать между R-значением (или содержимым) и L-значением (или местоположением) переменной.
Например, вы можете написать:
int a;
a = 3;
Сохраняет целочисленное значение 3
в той памяти, которая была выделена для хранения содержимого переменной a
.
Если вы позже напишите:
int b;
b = a;
Принимает значение , сохраненное в памяти, на которую ссылается a
, и сохраняет его в ячейке памяти, выделенной для b
.
Те же операции с указателями могут выглядеть следующим образом:
int *ap;
ap=malloc(sizeof int);
*ap=3;
Первое назначение ap=
сохраняет ячейку памяти в указателе ap
. Теперь ap
фактически указывает на некоторую память. Второе назначение, *ap=
, сохраняет значение в этой ячейке памяти . Он вообще не обновляет указатель ap
; он читает значение, хранящееся в переменной с именем ap
, чтобы найти место в памяти для назначения.
Когда вы позже используете указатель, вы можете выбрать, какое из двух значений, связанных с указателем, использовать: либо фактическое содержимое указателя , либо значение, на которое указывает указатель:
int *bp;
bp = ap; /* bp points to the same memory cell as ap */
int *bp;
bp = malloc(sizeof int);
*bp = *ap; /* bp points to new memory and we copy
the value pointed to by ap into the
memory pointed to by bp */
Я обнаружил, что сборка гораздо легче, чем C, потому что я нахожу разницу между foo = malloc();
и *foo = value;
сбивающей с толку. Я надеюсь, что нашел то, что вас смутило и серьезно надеюсь, что я не сделал это хуже.