когда я должен передать массив функции, кажется, что все следующие объявления функции будут работать
void f(int arr[])
void f(int arr[4]) // is this one correct?
для этого:
int a[]={1,2,3,4};
f(a);
Но когда я присваиваю массив другому массиву, он перестал работать
int a[]={1,2,3,4};
int b[4] = a; // error: array must be initialized with a brace-enclosed initializer
То итак, почему массив передал как аргумент функции, хорошо, но используемый на rhs простого присвоения является неправильным?
Для понимания разности мы должны понимать два разных контекстов .
T
эквивалентно указателю на тип t
, и равен указателю на Первый элемент массива. T
не уменьшается до указателя. Что такое контекст объекта?
в A = B;
, A
находится в контексте объекта. Когда вы приняли адрес переменной, он используется в контексте объекта. Наконец, при использовании SizeOf
Оператор в переменной, он используется в контексте объекта. Во всех других случаях переменная используется в контексте значений.
Теперь, когда у нас есть эти знания, когда мы делаем:
void f(int arr[4]);
это точно , эквивалентно
void f(int *arr);
, как вы узнали, мы можем опустить размер (4 выше) из объявления функции. Это означает, что вы не можете знать размер «массива», переданный F ()
. Позже, когда вы делаете:
int a[]={1,2,3,4};
f(a);
в вызове функций, имя находится в контексте значения
, поэтому он уменьшается до указателя на int
. Это хорошо, потому что f
Z ожидает указатель на int
, поэтому определение функции и совпадение. Прошло в f ()
f () , является указателем на первый элемент A
( & A [0]
).
В случае
int a[]={1,2,3,4};
int b[4] = a;
имя B
используется в контексте объекта и не уменьшается до указателя. (Кстати, здесь
в контексте значений, и уменьшает указатель.)
Теперь int B [4];
назначает стоимость того Из 4 int
S и дает имя b
. А
также был назначен аналогичное хранение. Таким образом, по сути, приведенное выше назначение означает, что «я хочу сделать место хранения так же, как и предыдущее местоположение». Это не имеет смысла.
Если вы хотите Copy Содержание A
в b
, тогда вы можете сделать:
#include <string.h>
int b[4];
memcpy(b, a, sizeof b);
или, если вы хотите указатель B
, который указал на A
:
int *b = a;
здесь,
находится в контексте значений, и уменьшает указку на int
, поэтому мы можем Назначить A
к int *
.
Наконец, При инициализации массива вы можете назначить ему явные значения:
int a[] = {1, 2, 3, 4};
здесь есть 4 элемента, инициализированные до 1, 2, 3 и 4. Вы также можете сделать:
int a[4] = {1, 2, 3, 4};
Если в списке меньше элементов меньше, чем количество элементов в массиве, то остальные значения принимаются для наборов 0:
int a[4] = {1, 2};
A [2]
и [3]
к 0.
void f(int arr[]);
void f(int arr[4]);
Синтаксис вводит в заблуждение. Они оба одинаковые:
void f(int *arr);
т.е. вы передаете указатель на начало массива. Вы не копируете массив.
C не поддерживает назначение массивов. В случае вызова функции массив распадается на указатель. C действительно поддерживает назначение указателей. Об этом спрашивают здесь практически каждый день - какую книгу на Си вы читаете, которая этого не объясняет?
.Попробуй мемкпи.
int a[]={1,2,3,4};
int b[4];
memcpy(b, a, sizeof(b));
Спасибо, что указал на это, Стив, прошло много времени с тех пор, как я использовал C.
Чтобы получить интуицию, вы должны понять, что происходит на уровне машины.
Семантика инициализации (= {1,2,3,4}) означает "поместите это на ваш двоичный образ именно таким образом", чтобы это можно было скомпилировать.
Присвоение массива будет другим: компилятор должен будет перевести его в цикл, который на самом деле будет итерацией над элементами. Компилятор Си (или Си++, если уж на то пошло) никогда такого не делает. Он по праву ожидает, что Вы сделаете это сами. Почему? Потому что вы можете. Итак, это должна быть подпрограмма, написанная на C (memcpy). Речь идет о простоте и близости к вашему оружию, которым являются C и C++.
Обратите внимание, что типом a
в int a [4]
является int [4]
.
Но TypeOf ( & a
) == int (*) [4]
! = int [4]
.
Также обратите внимание, что типом значения из a
является int *
, что отличается от всего вышеперечисленного!
Вот пример программы, которую вы можете попробовать:
int main() {
// All of these are different, incompatible types!
printf("%d\n", sizeof (int[4])); // 16
// These two may be the same size, but are *not* interchangeable!
printf("%d\n", sizeof (int (*)[4])); // 4
printf("%d\n", sizeof (int *)); // 4
}