C Программирование - Pass-Reference
-
26-09-2019 - |
Вопрос
В программе 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'
Решение
void foo(char buf[])
такой же как
void foo(char* buf)
Когда вы называете это, foo(buf)
, Вы передаете указатель по значению, поэтому копия указателя сделана.
Копия указателя указывает на тот же объект, что и оригинальный указатель (или в этом случае, к начальному элементу массива).
C не имеет прохождения по ссылке семантики в том смысле, что C ++ проходит по ссылке семантики. Все в C передается по значению. Указатели используются для пропуска по ссылкой семантики.
Другие советы
Массив - это просто модный способ использовать указатель. Когда вы проходите buf
к функции, вы проходите указатель По значению, но когда вы разымеваете указатель, вы все еще ссылаетесь на строку, которые он указывает.
Массив в качестве параметра функции эквивалентен указателю, поэтому декларация
void foo( char buf[] );
такой же как
void foo( char* buf );
Аргумент массива тогда распадается указателю на свой первый элемент.
Массивы обрабатываются по-разному, чем другие типы; Вы не можете пройти массив «по значению» в C.
Онлайн стандарт C99 (проект N1256), Раздел 6.3.2.1, "Lvalues, массивы и дизайн функции", пункт 3:
За исключением случаев, когда он является операндом оператора SIZEOF или NANY & OPERAL OPERANCE, или является строковым литералом, используемым для инициализации массива, выражение, которое имеет тип «массив типа», преобразуется в выражение с помощью указателя типа Тип «», который указывает на начальный элемент объекта массива и не является Lvalue. Если объект Array имеет класс хранения регистрации, поведение не определено.
В вызове
foo(buf);
выражение массива buf
не оперен sizeof
или &
, и не является строковым буквальным, не используемым для инициализации массива, поэтому оно неявно преобразовано («распады») от типа «10-элементный массив CHAR», чтобы «Указатель на Char» и адрес первого элемента передается Foo. Поэтому все, что вы делаете, чтобы buf
в foo()
будет отражен в buf
массив внутри main()
. Отказ Из-за того, как определяется подписка массива, вы можете использовать оператор подплиги на типе указателя, так что это выглядит Как вы работаете с типом массива, но вы нет.
В контексте объявления параметров функции, T a[]
а также T a[N]
являются синонимом T *a
, но это Только случай, когда это правда.
* Char Buf [] на самом деле означает Char **, так что вы проходите по указателю / ссылке. Это дает вам, что Buf является указателем, как в основной() а также фюра() Функция.
Потому что вы передаете указатель на buf
(по стоимости). Так что контент указан buf
изменился.
С указателями это другое; Вы проходите по значению, но то, что вы проходите, - это значение указателя, которое не совпадает с значением массива.
Итак, значение указателя не меняется, но вы изменяете то, на что он указывает.
Массивы и указатели (почти) то же самое.
int* foo = malloc(...)
foo[2]
такой же как *(foo+2*sizeof(int))
Anecdote: Вы написали
int main(int argc, char *argv[])
Это также законно (будет сознавать и работать так же), чтобы написать
int main(int argc, char **argv)
а также
int main(int argc, char argv[][])
Они эффективно одинаковы. Его чуть сложнее, потому что массив знает, сколько элементов у него есть, и указатель не. Но они используются то же самое.
Для того, чтобы пройти, по значению, функция должна будет знать размер аргумента. В этом случае вы просто проходите указатель.
Вы проходите по ссылке здесь. В этом примере вы можете решить проблему, передав один символ по индексу желаемого массива.
Если вы хотите сохранить содержимое исходного массива, вы можете скопировать строку на временное хранилище в функции.
Редактировать: Что произойдет, если вы завернули массив Char в структуре и передали структуру? Я верю, что это может работать тоже, хотя я не знаю, какого рода накладных расходов может создать на уровне компилятора.
Пожалуйста, обратите внимание на одну вещь,
декларация
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]. Итак ... это указатель.
Массивы в C не передаются значением. Они не даже законные параметры функции. Вместо этого компилятор видит, что вы пытаетесь пройти массив и демотируют его указатель. Это делает это молча, потому что это зло. Он также любит пинать щенков.
Использование массивов в параметрах функций - это хороший способ сигнализировать для своих пользователей API, что эта штука должна быть блоком памяти, сегментированной в N-байтовые куски размером, но не ожидайте, что компиляторы заботы, если вы хотите записать char *foo
char foo[]
или char foo[12]
в функциональных параметрах. Они не будут.