В программе C ниже, я не понимаю, почему buf [0] = после того, как я назову нечто. Разве нечто не делает передачи значением?
#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
, вы передаете указатель по значению, но при разыменовании указателя вы по-прежнему ссылаетесь на строку, на которую он указывает.
*char buf[] на самом деле означает char **, поэтому вы передаете по указателю/ссылке. Это дает вам понять, что buf является указателем, как в функции main(), так и в foo().
Потому что вы передаете указатель на buf
(по значению). Поэтому содержимое, на которое указывает buf
, изменяется.
Массив как параметр функции эквивалентен указателю, поэтому объявление
void foo( char buf[] );
такое же, как
void foo( char* buf );
Аргумент массива затем распадается на указатель на его первый элемент.
Массивы обрабатываются иначе, чем другие типы; вы не можете передавать массив «по значению» в 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
, но это только случай, когда это правда.
С указателями все иначе; вы передаете по значению, но передаете значение указателя, которое не совпадает со значением массива.
Итак, значение указателя не меняется, но вы изменяете то, на что он указывает.
массивы и указатели (почти) одно и то же.
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[][])
они фактически одинаковы. это немного сложнее, потому что массив знает, сколько элементов у него есть, а указатель - нет. но они используются одинаково.
, чтобы передать это по значению, функции необходимо знать размер аргумента. В этом случае вы просто передаете указатель.
Вы проходите здесь по ссылке. В этом примере вы можете решить проблему, передав один символ по индексу желаемого массива.
Если вы хотите сохранить содержимое исходного массива, вы можете скопировать строку во временное хранилище в функции.
edit: Что произойдет, если вы обернете свой массив символов в структуру и передадите эту структуру? Я считаю, что это тоже может сработать, хотя я не знаю, какие накладные расходы могут возникнуть на уровне компилятора.
обратите внимание на одну вещь,
объявление
void foo(char buf[])
говорит, что будет использоваться нотация []. Не то, какой элемент массива вы будете использовать.
Если вы хотите указать, что вы хотите получить какое-то конкретное значение, тогда вы должны объявить эту функцию как
void foo(char buf[X]); //where X would be a constant.
Конечно, это невозможно, потому что это было бы бесполезно (функция для работы с n-м элементом массива?). Вам не нужно записывать информацию, какой элемент массива вы хотите получить. Все, что вам нужно, это простое объявление:
voi foo(char value);
so ...
void foo(char buf[])
- это объявление, в котором указано, какую нотацию вы хотите использовать ([] - часть), а также оно содержит указатель на некоторые данные.
Более того ... чего вы ожидаете ... вы отправили функции foo имя массива
foo(buf);
, которое эквивалентно & buf [0]. Итак ... это указатель.
Массивы в C не передаются по значению. Они даже не являются допустимыми параметрами функции. Вместо этого компилятор видит, что вы пытаетесь передать массив, и понижает его статус до указателя. Он делает это тихо, потому что это зло. Еще он любит пинать щенков.
Использование массивов в параметрах функции - хороший способ сообщить вашим пользователям API, что эта штука должна быть блоком памяти, сегментированным на блоки размером n байтов, но не ожидайте, что компиляторы позаботятся, если вы напишете char * foo
char foo []
или char foo [12]
в параметрах функции. Они этого не сделают.