Ну, так как вы прекрасно поняли операторы &
и *
, остальное очень очень просто.
Допустим, у вас есть:
int q;
int *m;
m = &q;
Тогда, если вы скажете:
int *m2;
m2 = m;
m2
будет содержать то же значение, что и m
, то есть будет иметь адрес q
. Следовательно, *m
и *m2
приведут вас к одному и тому же значению (которое является значением q
) (вы делаете понимаете, что *
является обратным оператором &
, верно? *(&q) = q
и &(*m) = m
(в последнем случае m
должен быть указателем для *
, чтобы быть применимым.))
Итак, как это работает с функциями? Просто! Когда вы передаете аргументы функциям, вы передаете их по значению. Когда вы передаете указатель, вы фактически передаете значение, указатель на переменную.
Итак, давайте подробно рассмотрим вызов вашей функции:
reverse_number(in_orig, &out_orig);
Я переименовал ваши in_val
и out_val
в основном в in_orig
и out_orig
, чтобы они не смешивались с reverse_number
.
Теперь &out_orig
- это адрес out_orig
. При передаче в качестве аргументов это копируется в out_val
аргумент reverse_number
. Это в точности как запись:
int *out_val = &out_orig;
Теперь, если бы у вас была указанная выше строка в вашем main
, вы могли бы просто написать *out_val = something;
, и это изменит out_orig
, верно? Ну, так как у вас есть адрес out_orig
в out_val
, то кого волнует, установлен ли *out_val
в main
или reverse_number
?
Так ты видишь? Когда у вас есть указатель, вы можете просто скопировать его, скопировав его в другую переменную или передав в качестве аргумента функции (что в основном то же самое), вы все равно можете получить доступ к той же переменной, на которую указывает. Ведь все копии имеют одинаковое значение: адрес out_orig
. Теперь, если вы хотите получить к нему доступ в функции или в main
, это не имеет значения.
Редактировать : *
в определении указателя
*
также может использоваться для определения указателя, и это не имеет ничего общего с предыдущим использованием *
в качестве оператора, который получает значение адреса.
Это просто определение, поэтому вы должны изучить его:
Если у вас есть значение типа type
(например, int
), то адрес этой переменной (с использованием operator &
) имеет тип type *
(в этом примере int *
). Поскольку указатель принимает этот адрес, тип указателя type *
.
Напротив, если указатель имеет тип type *
(например, int *
), то получение значения, на которое указывает указатель (используя operator *
), имеет тип type
(в этом примере int
).
Таким образом, вы можете сказать что-то вроде этого:
operator &, adds one * to the type of the variable
operator *, removes one * from the type of the expression
Итак, давайте посмотрим несколько примеров:
int x;
x has type int
&x has type int *
float *y;
y has type float *
&y has type float **
*y has type float
struct Data ***d;
d has type struct Data ***
&d has type struct Data ****
*d has type struct Data **
*(*d) has type struct Data *
*(*(*d)) has type struct Data
Если вы заметили, я сказал, что &
добавляет один *
к типу переменной , но *
удаляет один *
из типа выражения, Это почему? Потому что &
дает адрес переменной. Конечно, потому что ничто другое не имеет адреса. Например, a+b
(возможно) не имеет какого-либо адреса в памяти, а если он есть, он просто временный и бесполезный.
operator *
однако, работает по адресам. Независимо от того, как вы вычисляете адрес, operator *
работает на нем. Примеры:
*(0x12345678) -> Note that even if the compiler let's you do this,
your program will most likely crash
*d -> like we saw before
*(d+4) -> This is the same as writing d[4]
And now you know why arrays and pointers are treated as one
В случае динамического 2d-массива:
*(*(d+4)+6) -> This is the same as writing d[4][6]