#c #pointers #reference
#c #указатели #ссылка
Вопрос:
Прежде чем сказать, что это будет дублирующий вопрос и понижающий голос (как это было раньше), я искал и не нашел ничего похожего.
Я, как и многие другие, пытаюсь изучить использование ссылочных переменных C и связать их с указателями. Мне было проще создать таблицу, и мне нужно знать, нужно ли ее исправлять.
int *n int n int amp;n caller/local
void foo(int *n) n amp;n amp;n caller
void foo(int n) *n n n local
void foo(int amp;n) *n n n caller
Таблица хочет отражать все допустимые переданные параметры.
[1,1]: passing by reference (trivial)
[1,2]: passing by reference
[1,3(1)]: passing by reference, an is an address(?)
[1,3(2)]: passing by reference, as n is used as alias(?)
[2,1]: passing by value, as dereferencing
[2,2]: passing by value (trivial)
[2,3(1)]: passing by value, using value of n (where n is an alias)
[2,3(2)]: passing by value (dereferencing n, which is an address)
[3,1(1)]: passing by reference, as foo accepts address
[3,1(2)]: passing by reference, reference of value at address n
[3,2(1)]: passing by reference (trivial)
[3,2(2)]: passing by reference, as foo accepts address or reference
[3,3]: passing by reference (trivial, as argument matches parameter exactly)
- Правильны ли таблица и пояснения?
- Есть ли какие-либо случаи, исключенные из таблицы (кроме производных, таких как *amp; n, указатель на указатель и т. Д.)?
Комментарии:
1.
void foo(int amp;n)
Передается только по ссылке, все остальные передаются по значению.2. 1 для усердного изучения новичка C
3. Можете ли вы уточнить, к какой ячейке относится каждый из ваших комментариев? (например, [строка, столбец]?)
4. Спасибо всем за ответ. Это [строка, столбец (subcol)] . Передавая по ссылке, я имел в виду, что функция может изменять переменную «outter scope».
5. @mireazma: вам также не хватает ссылок на значения rvalue: en.cppreference.com/w/cpp/language/reference
Ответ №1:
Функция
void foo(intamp; n);
не принимает адрес (указатель), а также не литералы.
Таким образом, вы не можете назвать это как
int a = ...;
foo(amp;a); // Trying to pass a pointer to a function not taking a pointer
или
foo(1); // Passing R-value is not allowed, you can't have a reference to a literal value
Однако есть исключение, если у вас есть постоянная ссылка, например
int foo(const intamp; n);
тогда разрешены литеральные значения, потому что тогда ссылочное значение не может быть изменено.
Аналогично для
void foo(int* n);
вы должны передать указатель.
Так, например:
int a = ...;
intamp; ra = a; // ra references a
foo(amp;a); // OK
foo(amp;ra); // OK
foo(a); // Fail, a is not a pointer
foo(ra); // Fail, ra is not a pointer
foo(1); // Fail, the literal 1 is not a pointer
И для последнего:
void foo(int n);
С примерами:
int a = ...;
intamp; ra = a; // ra references a
int* pa = amp;a; // pa points to a
foo(a); // OK, the value of a is copied
foo(ra); // OK, the value of the referenced variable is copied
foo(*pa); // OK, dereferences the pointer, and the value is copied
foo(pa); // Fail, passing a pointer to a function not expecting a pointer
foo(1); // OK, the literal value 1 is copied
Комментарии:
1. Вы должны упомянуть
const
ссылки, которые принимают литералы.2. Спасибо. Я думал, что указатель amp;a будет функционировать так же, как и ссылка amp;a, что, очевидно, неверно.
Ответ №2:
Перегружен operator*
и operatoramp;
Оба operatoramp;
(унарный) и operator*
(унарный) могут быть перегружены.
Это означает, что if n
является типом класса type
, то *n
может буквально иметь любую форму, включая, но не ограничиваясь type*
, typeamp;
, type
.
Примером (глупым, но все же допустимым примером) было бы:
struct type {
int x;
};
intamp; operatoramp;(typeamp; t) {
return t.x;
}
Косвенность
У вас также может быть бесконечное количество косвенных указателей. В этом случае *n
, **n
, ***n
также может привести Type*
к, а также Typeamp;
.
В качестве примера, учитывая две функции:
void func_ptr(int*) {}
void func_ref(intamp;) {}
и следующие объекты:
int a;
int* b = amp;a;
int** c = amp;b;
int*** d = amp;c;
тогда допустимо любое из следующих действий:
func_ptr(b);
func_ptr(*c);
func_ptr(**d);
func_ref(*b);
func_ref(**c);
func_ref(***d);
Заключение
Правильны ли таблица и пояснения? Есть ли какие-либо случаи, исключенные из таблицы (кроме производных, таких как *amp; n, указатель на указатель и т. Д.)?
Поэтому таблица не только является неполной, но и не может содержать все возможные случаи.
Комментарии:
1. Хорошая вещь — онлайн-песочница. Меня смущает
type() : x(0) {}
: похоже, у него есть роль конструктора. Я бы очень хотел знать, что это значит.2. @mireazma Это конструктор по умолчанию
type()
, который имеет список инициализаторовx(0)
, который инициализирует объект-членx
путем передачи0
его конструктору. Тело конструктора по умолчанию пустое{}
.
Ответ №3:
Возможно, я неправильно интерпретирую вашу таблицу, но предполагая, что вы находитесь внутри трех функций слева и хотите получить int*
n
, использовать n
и привязать ссылку к int
значению, доступ к которому осуществляется через n
соответственно:
Ваша исходная таблица
int *n int n int amp;n
void foo(int *n) n amp;n n/amp;n
void foo(int n) *n n n/*n
void foo(int amp;n) n/*n n/amp;n n
Учитывая сигнатуры функций слева, у вас есть следующие ошибки:
В foo(int* n)
:
- чтобы получить
int
, что вы*n
не хотитеamp;n
- вы должны привязать любые новые
intamp;
s к*n
В foo(int n)
- чтобы получить указатель на
n
выamp;n
не хотите*n
- чтобы привязать an
intamp;
кn
тому, что вы хотитеn
, а не*n
- обратите внимание, что
n
это локальное, скопированное и отличное от вызывающегоn
В foo(intamp; n)
:
- чтобы получить указатель на
n
то, что выamp;n
неn
хотите или*n
- чтобы использовать
n
, вы простоn
не хотите / никогдаamp;n
Итак, я бы написал таблицу следующим образом (я добавил столбцы для адреса n
и является ли n
это n
из вызывающего контекста или локальной копии)
Предлагаемая исправленная таблица
int* int intamp; n2 = ... int* p = ... caller/local
void foo(int* p_n) p_n *p_n *p_n p_n caller
void foo(int n) amp;n n n amp;n local
void foo(intamp; n) amp;n n n amp;n caller
Однако я не уверен, что такая таблица имеет большую полезность для разъяснения роли ссылок и указателей.
ОБНОВЛЕНИЕ: в вашем комментарии объясняется, что вы хотели, чтобы таблица передавала «форму передачи», которая, как я понимаю, является кодом, который вам понадобится внутри вызывающей стороны foo
. Правильная таблица для этого должна быть:
int *n int n int amp;n caller/local
void foo(int *n) n amp;n amp;n caller
void foo(int n) *n n n local
void foo(int amp;n) *n n n caller
Комментарии:
1. Спасибо. Теперь я понимаю, благодаря вам и Йоахиму Пилеборгу. Однако у меня путаница: элементы представляют, как они должны быть объявлены? В моей таблице объявление полностью соответствует имени столбца, а элементы представляют форму передачи.
2. @mireazma: «представлять форму передачи» — аааа, это объясняет, почему я нашел вашу таблицу запутанной, а вы мою! 😉 В моей таблице показана форма для использования переданного параметра внутри функции: т. Е. Тип локальной переменной, которую вы могли бы ввести в тело функции и инициализировать с помощью параметра функции. Например,
foo(int* p_n) { int* p_n2 = p_n; int n = *p_n; intamp; n2 = *p_n; int* p = p-N; }
локальные переменные этих типов могут быть инициализированы с помощью выражений в столбце. Я добавлю еще одну таблицу для правильной «формы передачи».3. @mireazma: хорошо, и выглядит здесь хорошо (но в качестве общего принципа переполнения стека — имейте в виду, что иногда, когда вы редактируете вопрос (вместо того, чтобы сказать, добавляете обновление), это может несправедливо сделать некоторые существующие комментарии и ответы недействительными или бессмысленными и раздражать тех, кто пытается вам помочь). Приветствия.
4. Я обнаружил, что моя таблица уже отредактирована (удалены неправильные записи). Я добавил только столбец caller / local, поскольку это было просто недостающее разъяснение.
5. Мне не нравится, что другие могут редактировать мои сообщения без моего согласия. В качестве примечания мне потребовалось четверть часа, чтобы понять, почему я не смог опубликовать вопрос: SE думал, что я исправляю код, только потому, что я использовал «[1,2]», «где», «используя». Это естественный язык, а не код вообще, и даже если бы это было так, я беру на себя полную ответственность за то, что я печатаю, и это мое право, что это понимается так, как я хочу; мое письмо — моя собственность. Ну, это не о тебе, Тони, очевидно.