Проходя по ссылке в С - PullRequest
       25

Проходя по ссылке в С

190 голосов
/ 09 февраля 2010

Если 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 

Ответы [ 17 ]

303 голосов
/ 09 февраля 2010

Поскольку вы передаете значение указателя на метод, а затем разыменовываете его, чтобы получить целое число, на которое указывает.

105 голосов
/ 29 мая 2015

Это не передача по ссылке, а передача по значению, как заявили другие.

Язык Си передается по значению без исключения. Передача указателя в качестве параметра не подразумевается передача по ссылке.

Правило следующее:

Функция не может изменить фактическое значение параметров.


Давайте попробуем увидеть разницу между скалярными и указательными параметрами функции.

Скалярные переменные

Эта короткая программа показывает передачу по значению с использованием скалярной переменной. 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 было увеличено, и это побочный эффект, который заставляет нас думать, что это был вызов функции передачи по ссылке.

Указатели - переданные по значению

Как мы можем показать / доказать этот факт? Ну, может быть, мы можем попробовать первый пример скалярных переменных, но вместо скалярных мы используем адреса (указатели). Посмотрим, поможет ли это.

#include <stdio.h>

void function2(int *param) {
    printf("param's address %d\n", param);
    param = NULL;
}

int main(void) {
    int variable = 111;
    int *ptr = &variable;

    function2(ptr);
    printf("ptr's address %d\n", ptr);
    return 0;
}

В результате оба адреса будут равны (не беспокойтесь о точном значении).

Пример результата:

param's address -1846583468
ptr's address -1846583468

По моему мнению, это ясно показывает, что указатели передаются по значению. В противном случае ptr будет NULL после вызова функции.

69 голосов
/ 09 февраля 2010

В C, Pass-by-reference моделируется передавая адрес переменной (указатель) и разыменование этого адрес в функции для чтения или написать фактическую переменную. Это будет упоминается как "стиль C пройти по ссылке. "

Источник: www-cs-students.stanford.edu

48 голосов
/ 09 февраля 2010

Поскольку в вышеприведенном коде нет передачи по ссылке.Использование указателей (таких как void func(int* p)) является передачей по адресу.Это передача по ссылке в C ++ (не будет работать в C):

void func(int& ref) {ref = 4;}

...
int a;
func(a);
// a is 4 now
27 голосов
/ 09 февраля 2010

Ваш пример работает, потому что вы передаете адрес вашей переменной в функцию, которая манипулирует ее значением с помощью оператора разыменования .

Хотя C не поддерживает ссылочные типы данных , вы все равно можете имитировать передачу по ссылке, явно передавая значения указателя, как в вашем примере.

Тип справочных данных C ++ менее мощный, но считается более безопасным, чем тип указателя, унаследованный от C. Это ваш пример, адаптированный для использования ссылок C ++ :

void f(int &j) {
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}
12 голосов
/ 09 февраля 2010

Вы передаете указатель (адрес) по значению .

Это все равно что сказать "вот место с данными, которые я хочу, чтобы вы обновили".

6 голосов
/ 09 февраля 2010

p - указатель переменной. Его значением является адрес i. Когда вы вызываете f, вы передаете значение из p, которое является адресом i.

5 голосов
/ 09 февраля 2010

Нет передачи по ссылке в C, но p "ссылается" на i, и вы передаете p значением.

5 голосов
/ 10 июля 2017

В 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
4 голосов
/ 22 октября 2014

Краткий ответ: Да, C осуществляет передачу параметров по ссылке с помощью указателей.

При реализации передачи параметров разработчики языков программирования используют три разные стратегии (или семантические модели): передавать данные в подпрограмму, получать данные из подпрограммы или выполнять обе. Эти модели обычно называются соответственно в режиме, режиме выхода и режиме выхода.

Разработчики языка разработали несколько моделей для реализации этих трех элементарных стратегий передачи параметров:

Передача по значению (в семантике режима) Pass-by-Result (семантика режима выхода) Pass-by-Value-Result (семантика входного режима) Передача по ссылке (семантика входного режима) Передача по имени (семантика входного режима)

Передача по ссылке является вторым методом передачи параметров в режиме ожидания. Вместо копирования данных назад и вперед между основной подпрограммой и подпрограммой, среда выполнения отправляет путь прямого доступа к данным для подпрограммы. В этой стратегии подпрограмма имеет прямой доступ к данным, эффективно разделяя данные с основной программой. Основным преимуществом этого метода является то, что он абсолютно эффективен во времени и пространстве, поскольку нет необходимости дублировать пространство и нет операций копирования данных.

Реализация передачи параметров в C: В C реализована семантика передачи по значению, а также передачи по ссылке (входной режим) с использованием указателей в качестве параметров. Указатель отправляется в подпрограмму, и никакие фактические данные не копируются вообще. Однако, поскольку указатель является путем доступа к данным основной подпрограммы, подпрограмма может изменить данные в основной подпрограмме. C принял этот метод от ALGOL68.

Реализация передачи параметров в C ++: C ++ также реализует семантику передачи по ссылке (inout mode) с использованием указателей, а также с использованием специального вида указателя, называемого ссылочным типом. Указатели ссылочных типов неявно разыменовываются внутри подпрограммы, но их семантика также передается по ссылке.

Таким образом, ключевая концепция здесь заключается в том, что передача по ссылке реализует путь доступа к данным вместо копирования данных в подпрограмму. Пути доступа к данным могут быть указателями с явной разыменовкой или указателями с автоматической разыменовкой (ссылочный тип).

Для получения дополнительной информации, пожалуйста, обратитесь к книге «Концепции языков программирования» Роберта Себесты, 10-е издание, глава 9.

...