Программирование на C - передача по ссылке - PullRequest
6 голосов
/ 05 августа 2010

В программе на C ниже я не понимаю, почему buf [0] = 'A' после вызова foo.Разве foo не выполняет передачу по значению?

#include <stdio.h>
#include <stdlib.h>

    void foo(char buf[])
    {
      buf[0] = 'A';
    }

    int main(int argc, char *argv[])
    {
      char buf[10];

      buf[0] = 'B';
      printf("before foo | buf[0] = %c\n", buf[0]);
      foo(buf);
      printf("after foo | buf[0] = %c\n", buf[0]);

      system("PAUSE"); 
      return 0;
      }

вывод:

before foo | buf[0] = 'B' 
after foo | buf[0] = 'A'

Ответы [ 12 ]

12 голосов
/ 05 августа 2010
void foo(char buf[])

совпадает с

void foo(char* buf)

Когда вы вызываете его, foo(buf), вы передаете указатель по значению, поэтому создается копия указателя.

Копия указателя указывает на тот же объект, что и исходный указатель (или, в этом случае, на начальный элемент массива).

C не имеет проходной семантики ссылокв том смысле, что C ++ имеет обходную семантику.Все в C передается по значению.Указатели используются для передачи по семантике ссылок.

4 голосов
/ 05 августа 2010

массив - это просто модный способ использовать указатель. Когда вы передаете buf в функцию, вы передаете указатель на значение, но когда вы разыменовываете указатель, вы все равно ссылаетесь на строку, на которую он указывает.

2 голосов
/ 05 августа 2010

Массивы обрабатываются иначе, чем другие типы;Вы не можете передать массив "по значению" в C.

Онлайновый стандарт C99 (черновик n1256) , раздел 6.3.2.1, "L-значения, массивы и обозначения функций", пункт 3:

За исключением случаев, когда это операнд оператора sizeof или унарный оператор &, или строковый литерал, используемый для инициализации массива, выражение, имеющее тип '' массив типа '', преобразуетсяна выражение с типом '' указатель на тип '', которое указывает на начальный элемент объекта массива и не является lvalue.Если объект массива имеет класс хранения регистров, поведение не определено.

В вызове

foo(buf);

выражение массива buf не является операндом sizeof или&, и при этом это не строковый литерал, используемый для инициализации массива, поэтому он неявно преобразуется («распадается») из типа «10-элементный массив char» в «указатель на char» и адрес первого элемента передается в foo.Поэтому все, что вы сделаете с buf в foo(), будет отражено в массиве buf в main().Из-за того, как определяется подписка на массив, вы можете использовать оператор индекса для типа указателя, чтобы он выглядел так, как будто вы работаете с типом массива, но это не так.

В контексте объявления параметров функции T a[] и T a[N] являются синонимами T *a, но это только , где это верно.

2 голосов
/ 05 августа 2010

Массив в качестве параметра функции эквивалентен указателю, поэтому объявление

void foo( char buf[] );

совпадает с

void foo( char* buf );

Тогда аргумент массива затухает на указатель на его первый элемент.

1 голос
/ 05 августа 2010

массивы и указатели - это (почти) одно и то же.

int* foo = malloc(...)

foo[2] совпадает с *(foo+2*sizeof(int))

анекдот: вы написали

int main(int argc, char *argv[])

также допустимо (скомпилировать и работать так же), чтобы написать

int main(int argc, char **argv)

, а также

int main(int argc, char argv[][])

они фактически одинаковы. это немного сложнее, потому что массив знает, сколько у него элементов, а указатель - нет. но они используются одинаково.

1 голос
/ 05 августа 2010

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

Таким образом, значение указателя не изменяется, но выизменяете то, на что он указывает.

1 голос
/ 05 августа 2010

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

1 голос
/ 05 августа 2010

* char buf [] на самом деле означает char **, поэтому вы передаете по указателю / ссылке. Это означает, что buf является указателем как в функции main (), так и foo ().

0 голосов
/ 05 августа 2010

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

Использование массивов в параметрах функций - это хороший способ дать понять пользователям API, что эта вещь должна быть блоком памяти, сегментированным на куски размером n байтов, но не ожидайте, что компиляторы будут заботиться, если вы произнесете char *foo char foo[] или char foo[12] в параметрах функции. Они не будут.

0 голосов
/ 05 августа 2010

обратите внимание на одну вещь,

Объявление

void foo(char buf[])

говорит, что будет использовать [] нотацию. Не какой элемент массива вы будете использовать.

Если вы хотите указать это, вы хотите получить какое-то конкретное значение, тогда вам следует объявить эту функцию как

void foo(char buf[X]); //where X would be a constant.

Конечно, это невозможно, потому что это было бы бесполезно (функция для работы с n-м элементом массива?). Вам не нужно записывать информацию, какой элемент массива вы хотите получить. Все, что вам нужно, это простая декларация:

voi foo(char value);

так ...

void foo(char buf[])

- это объявление, которое говорит, какую нотацию вы хотите использовать ([] - часть), а также содержит указатель на некоторые данные.

Более того ... что бы вы ожидали ... вы отправили в функцию foo имя массива

foo(buf);

, что эквивалентно & buf [0]. Итак ... это указатель.

...