Изучение С, одно из препятствий, с которым все должны преодолевать, подружится с указателями. В C, в отличие от других языков, вы кодируете на очень низком уровне машины и что почти все, что вы делаете на C, - это операция с памятью. Ключ к обучению C означает «Что у вас на этом адресе памяти?», «Насколько большой блок памяти у меня есть?» и «Как мне работать с ним правильно?». К сожалению, много раз люди делают этот процесс намного сложнее, чем нужно.
Одна вещи, которая действительно помогает, и что имеет фундаментальное значение для понимания объемности программирования низкого уровня, просто, что все переменные являются не более чем этикеткой, или еще лучше, псевдонима адреса памяти ,
Единственное, что отличает обычные переменный и переменный указатель является то, что вместо того, чтобы быть псевдонимом, где некоторые прямых или непосредственное значения сохраняются в памяти (например, 5
или 21
), указатель a псевдоним в ячейку памяти, где хранится адрес . Проще говоря, указатель сохраняет адрес памяти в качестве значения.
Придумайте основы таким образом:
int x = 5;
Что такое x
? Это метка для адреса памяти. Какой адрес памяти делает x
ярлык (псевдоним)? &x
. Что хранится по этому адресу? 5
. До сих пор хорошо?
Ну, что такое указатели?
int *y; /* what's so special about the '*' in the declaration?
Nothing, just basic syntax for pointer declaration. */
y = &x; /* which stores the address of x as the value of y.
And what is x? -- a label to a memory location */
Что такое y
? Это метка для адреса памяти. Какой адрес памяти делает y
ярлык (псевдоним)? &y
. Что хранится по этому адресу? &x
(адрес x
). Все еще хорош?
Теперь давайте рассмотрим короткий пример, который, надеюсь, поможет цементировать основы, чтобы вы могли по-настоящему использовать указатели, как предполагалось, для доступа к блокам памяти и манипуляциям с ними, таких как массивы, связанные списки, стеки и т. Д. .:
#include <stdio.h>
void prn_values (int val, int *ptr);
int main (void) {
int x = 5; /* declare variable & pointer */
int *y = &x; /* y now holds the address of x */
prn_values (x, y);
int z = 7;
y = &z; /* y now holds the address of z */
prn_values (z, y);
z = x; /* y still holds the value of z, */
prn_values (z, y); /* but the value of z has changed */
*y = 9; /* y still holds z, but we have changed z by */
/* changing the value at the address stored in y */
printf ("\n z : %-14d (value)\n &z : %-14p (address)\n", z, &z);
/* actually using a pointer to an array */
char array[] = "pointer arithmetic.";
char *p = array; /* p points to the start address of array */
printf ("\n array : %p (address)\n array [0]: %p (address)\n\n",
array, &array[0]);
while (*p) {
printf (" %c : %p\n", *p, p);
p++;
}
return 0;
}
void prn_values (int val, int *ptr)
{
printf ("\n x : %-14d (value)\n &x : %-14p (address)\n", val, &val);
printf (" y : %-14p (value)\n &y : %-14p (address)\n *y : %-14d "
"(dereference)\n", ptr, &ptr, *ptr);
}
Compile
gcc -Wall -Wextra -o pointers pointers.c
Выход
Ниже указателю целого числа y
присваивается значение x
, затем z
и выводятся значения, адреса и значения по адресу указателя. Примечание как значение указателя является адресом переменной, на которую указывает. Пока y
указывает на z
, значение z
изменено. Так как y
хранит (указывает на) адрес памяти, псевдоним которого z
, *y
отражает изменение. Затем обратите внимание, как изменение работает в обоих направлениях. Если значение по адресу, содержащемуся y
, изменяется путем присвоения *y
, это изменение также отражается в z
.
Это все рудиментарные основы указателя, что очень мало, чтобы показать истинную силу и использование указателей. Это указатель arithmetic и возможность передавать указатель как аргумент функции, где вид использования и значения указателей. Обратите внимание, как конечный цикл while использует указатель p
в массиве символов для доступа к каждому адресу (содержащему символ) в массиве не более чем приращением p
на 1 (p++;
) на каждой итерации.
$ ./pointers
x : 5 (value)
&x : 0x7ffff2c72590 (address)
y : 0x7ffff2c72590 (value)
&y : 0x7ffff2c725b0 (address)
*y : 5 (dereference)
z : 7 (value)
&z : 0x7ffff2c725a0 (address)
y : 0x7ffff2c725a0 (value)
&y : 0x7ffff2c725b0 (address)
*y : 7 (dereference)
z : 5 (value)
&z : 0x7ffff2c725a0 (address)
y : 0x7ffff2c725a0 (value)
&y : 0x7ffff2c725b0 (address)
*y : 5 (dereference)
z : 9 (value)
z : 0x7ffff2c725a0 (address)
array : 0x7ffff2c725c0 (address)
array [0]: 0x7ffff2c725c0 (address)
p : 0x7ffff2c725c0
o : 0x7ffff2c725c1
i : 0x7ffff2c725c2
n : 0x7ffff2c725c3
t : 0x7ffff2c725c4
e : 0x7ffff2c725c5
r : 0x7ffff2c725c6
: 0x7ffff2c725c7
a : 0x7ffff2c725c8
r : 0x7ffff2c725c9
i : 0x7ffff2c725ca
t : 0x7ffff2c725cb
h : 0x7ffff2c725cc
m : 0x7ffff2c725cd
e : 0x7ffff2c725ce
t : 0x7ffff2c725cf
i : 0x7ffff2c725d0
c : 0x7ffff2c725d1
. : 0x7ffff2c725d2
Арифметика указателей для всех типов массивов (char, int, struct foo, whatever). Объявив указатель type X
, компилятор знает размер типа данных X
. Через арифметику указателя доступ и итерация сложных типов данных ничем не отличается. Указатели на функции и указатели на указатели работают одинаково. По мере того, как вы приобретаете больше опыта, вы будете полагаться на указатели как на большую часть вашего инструментария C, так что стоит потратить время на тщательное понимание основ. Передача указателей в качестве аргументов функции - еще одна целая грань того, как указатели используются в C. Поскольку этот ответ рос намного дольше, чем первоначально предполагалось, мы оставим это на другой день.Дайте знать, если у вас появятся вопросы. Удачи с C.
Шестнадцатеричные и десятичные числа - это просто произвольные представления чисел. Вы не можете сказать, что число явно * hex или dec, это и то, и другое. – CoffeeandCode
Ваш вопрос: «Является ли четверть 25 центов, или 1/4 доллара?». Hex и decimal - это всего лишь два способа взглянуть на одно и то же значение. – user657267
Я понимаю это сейчас. Я не должен рассматривать их как два разных числа, но одно число представлено двумя способами. Большое спасибо. –