Тип массива - Правила для присвоения/использования как параметр функции

когда я должен передать массив функции, кажется, что все следующие объявления функции будут работать

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 простого присвоения является неправильным?

6
задан Lightness Races with Monica 23 December 2011 в 13:51
поделиться

6 ответов

Для понимания разности мы должны понимать два разных контекстов .

  • В Значение Конечные элементы, имя массива типа 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.

12
ответ дан 8 December 2019 в 03:27
поделиться
void f(int arr[]);
void f(int arr[4]);

Синтаксис вводит в заблуждение. Они оба одинаковые:

void f(int *arr);

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

7
ответ дан 8 December 2019 в 03:27
поделиться

C не поддерживает назначение массивов. В случае вызова функции массив распадается на указатель. C действительно поддерживает назначение указателей. Об этом спрашивают здесь практически каждый день - какую книгу на Си вы читаете, которая этого не объясняет?

.
6
ответ дан 8 December 2019 в 03:27
поделиться

Попробуй мемкпи.

int a[]={1,2,3,4};
int b[4];
memcpy(b, a, sizeof(b));

Спасибо, что указал на это, Стив, прошло много времени с тех пор, как я использовал C.

3
ответ дан 8 December 2019 в 03:27
поделиться

Чтобы получить интуицию, вы должны понять, что происходит на уровне машины.

Семантика инициализации (= {1,2,3,4}) означает "поместите это на ваш двоичный образ именно таким образом", чтобы это можно было скомпилировать.

Присвоение массива будет другим: компилятор должен будет перевести его в цикл, который на самом деле будет итерацией над элементами. Компилятор Си (или Си++, если уж на то пошло) никогда такого не делает. Он по праву ожидает, что Вы сделаете это сами. Почему? Потому что вы можете. Итак, это должна быть подпрограмма, написанная на C (memcpy). Речь идет о простоте и близости к вашему оружию, которым являются C и C++.

1
ответ дан 8 December 2019 в 03:27
поделиться

Обратите внимание, что типом 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
}
0
ответ дан 8 December 2019 в 03:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: