Ответ 1
Поскольку вы передаете значение указателя на этот метод и затем разыгрываете его, чтобы получить целое число, на которое указывает.
Если C не поддерживает передачу переменной по ссылке, почему это работает?
#include <stdio.h>
void f(int *j) {
(*j)++;
}
int main() {
int i = 20;
int *p = &i;
f(p);
printf("i = %d\n", i);
return 0;
}
$ gcc -std=c99 test.c
$ a.exe
i = 21
Поскольку вы передаете значение указателя на этот метод и затем разыгрываете его, чтобы получить целое число, на которое указывает.
Это не перекрестная ссылка, это пропускная способность, как указано другими.
Язык C - это без исключения пробел. Передача указателя как параметр не означает "прохождение по ссылке".
Правило следующее:
Функция не может изменить значение фактических параметров.
Попробуйте увидеть различия между скалярными и указательными параметрами функции.
Эта короткая программа показывает значение pass-by-value с использованием скалярной переменной. param
называется формальным параметром, а variable
при вызове функции называется фактическим параметром. Примечание. Приращение param
в функции не изменяется variable
.
#include <stdio.h>
void function(int param) {
printf("I've received value %d\n", param);
param++;
}
int main(void) {
int variable = 111;
function(variable);
printf("variable %d\m", variable);
return 0;
}
Результат
I've received value 111
variable=111
Мы немного изменим часть кода. param
теперь является указателем.
#include <stdio.h>
void function2(int *param) {
printf("I've received value %d\n", *param);
(*param)++;
}
int main(void) {
int variable = 111;
function2(&variable);
printf("variable %d\n", variable);
return 0;
}
Результат
I've received value 111
variable=112
Это заставляет вас поверить, что параметр был передан по ссылке. Не было. Он был передан по значению, а значение параметра - адресом. Значение типа int было увеличено, и это побочный эффект, который заставляет нас думать, что это вызов функции передачи по ссылке.
Как мы можем показать/доказать этот факт? Ну, может быть, мы можем попробовать первый пример переменных Scalar, но вместо скалярного мы используем адреса (указатели). Посмотрите, поможет ли это.
#include <stdio.h>
void function2(int *param) {
printf("param address %d\n", param);
param = NULL;
}
int main(void) {
int variable = 111;
int *ptr = &variable;
function2(ptr);
printf("ptr address %d\n", ptr);
return 0;
}
Результат будет таким, что два адреса равны (не беспокойтесь о точном значении).
Результат:
param address -1846583468
ptr address -1846583468
По-моему, это ясно показывает, что указатели передаются по значению. В противном случае ptr
будет NULL
после вызова функции.
В C моделируется сквозная ссылка путем передачи адреса переменной (указатель) и разыменование адрес внутри функции для чтения или напишите фактическую переменную. Это будет называться "Стиль C" пройти по ссылке ".
Потому что в приведенном выше коде отсутствует пропускная ссылка. Использование указателей (например, void func(int* p)
) является сквозным адресом.
Это передача по ссылке в С++ (не будет работать в C):
void func(int& ref) {ref = 4;}
...
int a;
func(a);
// a is 4 now
Ваш пример работает, потому что вы передаете адрес своей переменной функции, которая управляет ее значением с помощью оператора разыменования.
В то время как C не поддерживает ссылочные типы данных, вы все же можете моделировать передачу по ссылке, явно передавая значения указателя, как в вашем примере.
Тип ссылочных данных С++ менее мощный, но считается более безопасным, чем тип указателя, унаследованный от C. Это будет ваш пример, адаптированный для использования ссылок С++:
void f(int &j) {
j++;
}
int main() {
int i = 20;
f(i);
printf("i = %d\n", i);
return 0;
}
Вы передаете указатель (расположение адреса) по значению.
Мне нравится говорить "здесь место с данными, которые я хочу вам обновить".
p - указательная переменная. Его значение - адрес i. Когда вы вызываете f, вы передаете значение p, которое является адресом i.
Нет пропусков в C, но p "ссылается" на i, и вы передаете p по значению.
В C все передается по значению. Использование указателей дает нам иллюзию того, что мы передаем ссылку, потому что изменяется значение переменной. Однако, если вы хотите распечатать адрес переменной указателя, вы увидите, что это не влияет. копия значения адреса передается функции. Ниже приведен фрагмент, иллюстрирующий это.
void add_number(int *a) {
*a = *a + 2;
}
int main(int argc, char *argv[]) {
int a = 2;
printf("before pass by reference, a == %i\n", a);
add_number(&a);
printf("after pass by reference, a == %i\n", a);
printf("before pass by reference, a == %p\n", &a);
add_number(&a);
printf("after pass by reference, a == %p\n", &a);
}
before pass by reference, a == 2
after pass by reference, a == 4
before pass by reference, a == 0x7fff5cf417ec
after pass by reference, a == 0x7fff5cf417ec
Потому что вы передаете указатель (адрес памяти) переменной p в функцию f. Другими словами, вы передаете указатель, а не ссылку.
Короткий ответ: Да, C реализует передачу параметров по ссылке с помощью указателей.
При реализации передачи параметров разработчики языков программирования используют три разные стратегии (или семантические модели): переносят данные в подпрограмму, получают данные из подпрограммы или выполняют оба. Эти модели обычно известны как в режиме, в режиме выхода и в неактивном режиме соответственно.
Несколько моделей были разработаны разработчиками языка для реализации этих трех стратегий управления элементарными параметрами:
Передача по значению (в семантике режима) Передача по результату (семантика режима вывода) Pass-by-Value-Result (inout mode semantics) Pass-by-Reference (семантика inout) Pass-by-Name (семантика inout mode)
Передача по ссылке - это второй метод передачи параметров inout-mode. Вместо копирования данных назад и вперед между основной подпрограммой и подпрограммой система времени выполнения отправляет путь прямого доступа к данным для подпрограммы. В этой стратегии подпрограмма имеет прямой доступ к данным, эффективно разделяющим данные с основной программой. Основное преимущество этого метода заключается в том, что он абсолютно эффективен во времени и пространстве, потому что нет необходимости дублировать пространство и нет операций копирования данных.
Передача параметров в C: C реализует pass-by-value, а также pass-by-reference (inout mode) семантику с использованием указателей в качестве параметров. Указатель отправляется в подпрограмму, и никакие фактические данные не копируются вообще. Однако, поскольку указатель представляет собой путь доступа к данным основной подпрограммы, подпрограмма может изменить данные в основной подпрограмме. C принял этот метод от ALGOL68.
Передача параметров в С++: С++ также реализует семантику pass-by-reference (inout mode) с помощью указателей, а также использует специальный тип указателя, называемый ссылочным типом. Указатели ссылочного типа неявно разыменовываются внутри подпрограммы, но их семантика также является передачей по ссылке.
Итак, ключевая концепция здесь заключается в том, что pass-by-reference реализует путь доступа к данным вместо копирования данных в подпрограмму. Пути доступа к данным могут быть явно разыменованными указателями или указателями автоматической разыменования (ссылочный тип).
Для получения дополнительной информации см. книгу "Концепции языков программирования" Роберта Себеста, 10-е изд., глава 9.
Вы не передаете int по ссылке, вы передаете указатель-на-int по значению. Различный синтаксис, то же значение.
В C для передачи по ссылке вы используете адрес-оператора &
, который должен использоваться против переменной, но в вашем случае, поскольку вы использовали указательную переменную p
, вам не нужно префикс это с адресом оператора. Это было бы верно, если бы вы использовали &i
в качестве параметра: f(&i)
.
Вы также можете добавить это, к разыменованию p
и посмотреть, как это значение соответствует i
:
printf("p=%d \n",*p);
указатели и ссылки являются двумя разными тиггами.
Несколько вещей, о которых я не упоминал.
Указатель - это адрес чего-то. Указатель может быть сохранен и скопирован как любая другая переменная. Таким образом, он имеет размер.
Ссылка должна рассматриваться как ALIAS. Он не имеет размера и не может быть сохранен. Он ДОЛЖЕН указать что-то, т.е. он не может быть нулевым или измененным. Ну, иногда компилятор должен хранить ссылку как указатель, но это детализация реализации.
В ссылках у вас нет проблем с указателями, например, с обработкой прав собственности, нулевой проверкой, де-ссылкой на использование.
"Передать по ссылке" (с помощью указателей) с самого начала был в C. Почему вы думаете, что это не так?
Я думаю, что C фактически поддерживает передачу по ссылке.
В большинстве языков требуется, чтобы синтаксический сахар передавался по ссылке вместо значения. (Например, требуется С++ и в объявлении параметра).
C также требует для этого синтаксического сахара. Он * в объявлении типа параметра и в аргументе. Таким образом, * и и является синтаксисом C для передачи по ссылке.
Теперь можно утверждать, что для реального прохода по ссылке требуется только синтаксис объявления параметра, а не аргументации.
Но теперь идет С#, который поддерживает передачу ссылок и требует синтаксического сахара как для сторон, так и для аргументов.
Аргумент о том, что C не имеет байтов-ссылок, заставляет синтаксические элементы выражать его, показывает, что базовая техническая реализация не является аргументом вообще, поскольку это более или менее применимо ко всем реализациям.
Единственным оставшимся аргументом является то, что передача ref в C не является монолитной, но сочетает в себе две существующие функции. (Возьмем аргумент аргумента by &, предположим, что ref набирает by *.) Например, для С# требуется два синтаксических элемента, но они не могут использоваться без друг друга.
Это, очевидно, опасный аргумент, потому что многие другие функции на языках состоят из других функций. (например, поддержка строки в С++)
То, что вы делаете, - это пропуск по значению, не проходящий по ссылке. Поскольку вы отправляете значение переменной 'p' в функцию 'f' (в основном как f (p);)
Такая же программа в C с передачей по ссылке будет выглядеть так: (!!! эта программа дает 2 ошибки, так как передача по ссылке не поддерживается в C)
#include <stdio.h>
void f(int &j) { //j is reference variable to i same as int &j = i
j++;
}
int main() {
int i = 20;
f(i);
printf("i = %d\n", i);
return 0;
}
Вывод: -
3:12: error: expected ';', ',' or ')' before '&' token void f(int &j); ^ 9:3: warning: implicit declaration of function 'f' f(a); ^