Я пытаюсь связать ссылку C с указателем

#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)  
 
  1. Правильны ли таблица и пояснения?
  2. Есть ли какие-либо случаи, исключенные из таблицы (кроме производных, таких как *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;
}
 

Live demo

Косвенность

У вас также может быть бесконечное количество косвенных указателей. В этом случае *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);
 

Live demo

Заключение

Правильны ли таблица и пояснения? Есть ли какие-либо случаи, исключенные из таблицы (кроме производных, таких как *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]», «где», «используя». Это естественный язык, а не код вообще, и даже если бы это было так, я беру на себя полную ответственность за то, что я печатаю, и это мое право, что это понимается так, как я хочу; мое письмо — моя собственность. Ну, это не о тебе, Тони, очевидно.