почему c / c ++ допускает пропуск крайнего левого индекса многомерного массива в вызове функции? - PullRequest
14 голосов
/ 21 апреля 2011

Мне было просто интересно, почему разрешено пропускать крайний левый индекс многомерного массива при передаче массива в функцию? Почему не более одного индекса? И как компилятор узнает размер без одного индекса?

Ответы [ 3 ]

15 голосов
/ 21 апреля 2011

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

В Си массивы обозначают плотно упакованные элементы в памяти.

A -> _ _ _ _ _ _ ...
i:   0 1 2 3 4 5 ...

В приведенном выше примере каждый из элементов массива имеет ширину 1 _.Таким образом, чтобы найти i-й элемент, мы должны перейти по i-му адресу.(Обратите внимание, что крайнее левое измерение (размер) здесь не имеет значения)

Теперь рассмотрим многомерный массив:

B -> [_ _ _][_ _ _][_ _ _][_ _ _]...
i:    0 0 0  1 1 1  2 2 2  3 3 3
j:    0 1 2  0 1 2  0 1 2  0 1 2
     ^first row    ^third row

Чтобы найти смещение A[i][j], нам нужно перепрыгнутьi строк (3 * i), а затем над j элементами -> (3 * i + j).Обратите внимание, что размер первого измерения здесь также не нужен.

Теперь уже должно быть ясно, что крайний левый размер не нужен при использовании массива, он нужен только тогда, когда высоздайте его .


Так как не требуется для определения размера крайнего левого индекса, тогда почему бы не дать его в любом случае для полноты картины?В конце концов, это то, что делается на языке программирования Pascal (современный C).

Ну, большинство функций, которые работают с массивами, работают одинаково для всех возможных длин массивов, поэтому указание размера только повредит вашемувозможность их повторного использования.

например, почему

int sum(int arr[10]){
    int s = 0, i;
    for(i=0; i<10; i++){
        s += arr[i];
    }
    return s;
}

Когда вы можете сделать это вместо этого:

int sum(int arr[], int n){
    int s = 0, i;
    for(i=0; i<n; i++){
        s += arr[i];
    }
    return s;
}

Что касается пропуска более одногоизмерение, это невозможно при использовании обычных многомерных массивов (потому что вам нужно знать измерение, чтобы знать, когда заканчивается первая строка, а начинается вторая).Однако, если вы готовы потратить немного (немного) дополнительной памяти на пустое место, вполне можно использовать указатели вместо указателей: http://www.eskimo.com/~scs/cclass/int/sx9b.html

7 голосов
/ 21 апреля 2011

В декларации

На самом деле вы не можете полностью исключить самое правое или левое измерение.

Однако, если у вас есть инициализатор, для вас можно вывести только крайний левый .

В списке аргументов функции

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

Рассмотрим:

void f(int ar[3])

void f(int ar[])

Оба сбивают с толку синтаксис для эквивалентного:

void f(int* ar)

Никаких следов массива, не говоря уже об одном из трех элементов.

Сейчас:

void f(int ar[][3])

Это сбивает с толку синтаксис для эквивалента:

void f(int (*ar)[3])

где int (*)[3] - тип указателя на первый элемент вашего массива (указатель на int[3]).

В заключение, не обращайте слишком много внимания на массивоподобный синтаксис, который выглядит как []; это не совсем то, что происходит на самом деле.

3 голосов
/ 21 апреля 2011

За исключением случаев, когда он является операндом операторов sizeof или унарных & или является строковым литералом, используемым для инициализации массива в объявлении, выражение типа "массив N-элементов из T"его тип будет неявно преобразован в «указатель на T» и будет оцениваться по адресу первого элемента в массиве.

Какое отношение это имеет к вашему вопросу?

Предположим следующие строки кода:

int arr[10] = {0,1,2,3,4,5,6,7,8,9};
foo(arr);

Передаем выражение массива arr в качестве аргумента foo. Так как arr не является операндом sizeof или &его тип неявно преобразуется из «массива из 10 элементов из int» в «указатель на int». Таким образом, мы передаем значение pointer в foo, а не в массив.

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

Мы можем написать определение прототипа дляfoo как

void foo(int *a)     // <- foo receives a pointer to int, not an array

или

void foo(int a[])    // <-- a[] is a synonym for *a

Оба означают одно и то же;оба объявляют a как указатель на int.

Теперь давайте посмотрим на многомерные массивы.Предположим следующий код:

int arr[10][20];
foo(arr);

Выражение arr имеет тип "массив из 10 элементов из массива из 20 элементов int".По правилу, описанному выше, оно будет неявно преобразовано в «указатель на массив из 20 элементов int».Таким образом, определение прототипа для foo может быть записано как

void foo(int (*a)[20])  // <-- foo receives a pointer to an array, not an array of arrays

или

void foo(int a[][20])  // <-- a[][20] is a synonym for (*a)[20]

Опять, оба они объявляют a как указатель , а немассив.

Вот почему вы можете удалить самый левый (и только крайний левый) индекс массива в объявлении параметра функции.

...