Указатели и функции в C++
-
21-09-2019 - |
Вопрос
Из конспектов лекций университетского курса по «вызову по значению»:
void fun(int *ip) { *ip =100; }
позвонил
int n=2; int *np; np = &n; fun(np);
изменит значение n на 100.
Когда мы говорим «int *ip», что именно мы имеем в виду?Указатель целочисленного типа?Если да, то когда мы вызываем fun() с аргументом np, не должна ли возникнуть ошибка, поскольку np имеет адрес n, который не является целым числом?
А затем мы меняем значение ip на 100, не означает ли это, что n теперь имеет значение, которое находится в «слоте памяти» с адресом 100?Я уверен, что что-то упускаю.:)
Решение
Указатель целочисленного типа?
Нет, указатель к целое число.
когда мы вызываем fun() с аргументом np, не должна ли возникнуть ошибка, поскольку np имеет адрес n, который не является целым числом?
n
является целым числом, поэтому проблем нет. &n
, np
и ip
все они имеют один и тот же тип в вашем коде: int*
.
А затем меняем значение ip на 100
Нет… мы меняем значение *ip
, не из ip
.То есть мы меняем значение, которое ip
указывает на (которое также иногда называют пуант).
Другие советы
Да, когда вы используете int *
как тип параметра fun
вы говорите, что он принимает указатель на int.С np
это указатель на int, передающий его в fun
все в порядке.
np
и ip
указывают на адрес n
, поэтому, когда вы присваиваете значение типа *ip = 42
, он присвоит значение 42 ячейке памяти, которая ip
указывает на - что n
.
То, что у вас есть в примере кода, называется пройти по указателю.Ваша функция имеет параметр типа указатель на целое число.Таким образом, значение, передаваемое в функцию, равно адрес переменной n
.Функция назначает 100
к переменной (т.е.место памяти) указал на по параметру указателя ip
.Звезда *
это оператор разыменования на С/С++.
в fun() вы не меняете значение ip на 100, вы меняете место в памяти, на которое указывает ip.
type*
использование в качестве типа означает «указатель на этот тип»
общее использование - это int *var
, но мне нравится писать это как int* var
поскольку это делает его лучшим отличием от разыменования указателя, т.е. *ptr
.
так int* a
означает, что a является указателем на объект типа int, *a
означает объект, на который указывают и &a
означает адрес объекта a.
думать о np
по типу int*
.Имейте в виду, что для этого звездочка и int
вместе образуют тип «указатель на int» и не являются разделяемыми.
ip
это указатель, с ip*
вы получаете доступ к «слоту памяти», на который он указывает. np
также является указателем.С использованием &n
вы назначаете адрес слота памяти n
к np
.Поэтому при звонке fun()
, внутри fun()
единственный доступный слот памяти - это один из n
и таким образом n
назначено 100.
Здесь возможна некоторая путаница, поскольку и n, и np хранят числа — однако при компиляции компилятор будет использовать числа по-разному;то есть, пока
n++;
и
np++;
обе на самом деле являются арифметическими операциями, сгенерированная сборка отличается.Важно помнить, что, в конечном счете, все данные в компьютере представляют собой числа.Они становятся другими типы данных просто потому, что мы относимся к ним по-разному.
Конкретно по вашему примеру.
*np = 100;
вам нужно помнить, что *
означает разыменование, и эта операция происходит до присваивания.Это может быть понятнее с лишними круглыми скобками:
(* (np) ) = 100;
или в другом контексте:
int n = *np;
Теперь, должен сказать, мое сердце согревается, когда вы говорите:
мы меняем значение ip на 100, не означает ли это, что n теперь имеет значение, которое находится в «слоте памяти» с адресом 100?поскольку это противоречит тому, что я считаю важным пониманием.Однако я считаю, что прав, когда говорю, что вы должны изо всех сил стараться делать такие вещи с указателями:
static_cast<int>(np) = 100;
Это будет делать то, что вы описали, потому что это говорит компьютеру рассматривать число np как число другого типа;таким же образом, что static_cast<char*>(np)
будет рассматривать число, на которое указывает np, как символ, а не как целое число.