когда нам нужно передать размер массива в качестве параметра - PullRequest
7 голосов
/ 10 июля 2011

Я немного озадачен передачей массива в C / C ++. Я видел несколько случаев, когда подпись выглядит так

void f(int arr[])

у некоторых это так

void f(int arr[], int size)

Кто-нибудь может объяснить, в чем разница, когда и как ее использовать?

Ответы [ 8 ]

9 голосов
/ 10 июля 2011

Во-первых, массив, переданный функции, фактически передает указатель на первый элемент массива, например, если у вас есть

int a[] = { 1, 2, 3 };
f(a);

Затем, f() получает &a[0], переданный ему.Таким образом, при написании прототипов вашей функции следующее эквивалентно:

void f(int arr[]);
void f(int *arr);

Это означает, что размер массива потерян, и f(), в общем, не может определить размер.(По этой причине я предпочитаю void f(int *arr) форму, а не void f(int arr[]).)

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

Во-первых, в arr есть специальное согласованное значение, которое и вызывающий, и f() принимают за "конец".Например, можно согласиться, что значение 0 означает «Готово».

Тогда можно написать:

int a[] = { 1, 2, 3, 0 }; /* make sure there is a 0 at the end */
int result = f(a);

и определить f() что-то вроде:

int f(int *a)
{
    size_t i;
    int result = 0;
    for (i=0; a[i]; ++i)  /* loop until we see a 0 */
        result += a[i];
    return result;
}

Очевидно, что приведенная выше схема работает только в том случае, если и , вызывающий и вызываемый стороны согласны с соглашением и следуют ему.Примером является strlen() функция в библиотеке C.Он вычисляет длину строки, находя 0.Если вы передадите ему что-то, что не имеет 0 в конце, все ставки выключены, и вы находитесь на неопределенной территории поведения.

Второй случай, когда у вас на самом деле нетмассив.В этом случае f() получает указатель на объект (int в вашем примере).Итак:

int change_me = 10;
f(&change_me);
printf("%d\n", change_me);

с

void f(int *a)
{
    *a = 42;
}

в порядке: f() все равно не работает с массивом.

2 голосов
/ 10 июля 2011

Когда массив передается в C или C ++, передается только его адрес. Вот почему второй случай довольно распространен, где вторым параметром является количество элементов в массиве. Функция не знает, только посмотрев на адрес массива, сколько элементов она должна содержать.

1 голос
/ 10 июля 2011

C и C ++ - это не одно и то же.У них есть некоторое общее подмножество, хотя.Здесь вы заметили, что «первое» измерение массива при передаче в функцию всегда приводит к передаче указателя.«Подпись» (C не использует этот термин) функции, объявленной как

void toto(double A[23]);

, всегда просто

void toto(double *A);

То есть, 23 выше несколько избыточени не используется компилятором.Современный C (он же C99) имеет здесь расширение, которое позволяет вам объявить, что у A всегда есть 23 элемента:

void toto(double A[static 23]);

или указатель const квалифицирован

 void toto(double A[const 23]);

Если выдобавьте другое измерение, которое меняется на картинке, тогда размер массива будет использован:

void toto(double A[23][7]);

в C и C ++ равен

void toto(double (*A)[7]);

, который является указателем намассив 7 элементов.В C ++ эти границы массива должны быть целочисленной константой.В Си это может быть динамически.

void toto(size_t n, size_t m, double A[n][m]);

Единственное, что вы должны здесь наблюдать, это то, что здесь n и m предшествуют A в списке параметров.Так что лучше всегда объявлять функции с параметрами в таком порядке.

1 голос
/ 10 июля 2011

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

Иногда сам массив содержит информацию о его размере.Например, в вашем первом примере, возможно, arr[0] установлен на размер массива, а фактические данные начинаются с arr[1].Или рассмотрим случай c-строк ... вы предоставляете только char[], и предполагается, что массив заканчивается первым элементом, равным \0.В вашем примере отрицательное значение может выступать в качестве аналога дозорного.Или, возможно, функция просто не заботится о размере массива и просто предположит, что он достаточно большой.

Такие методы небезопасны, хотя ... легко забыть установить arr[0] илислучайно перезаписать нулевой терминатор.Тогда f внезапно не сможет узнать, сколько места у него есть. Всегда предпочитают указывать размер явно, либо с помощью параметра size, как вы показываете, либо с помощью второго указателя на конец массива.Последний является методом, обычно используемым стандартными библиотечными функциями в C ++.У вас все еще есть проблема с предоставлением неправильного размера, поэтому в C ++ не рекомендуется вообще использовать такой массив ... использовать реальный контейнер, который будет отслеживать эту информацию для вас.

1 голос
/ 10 июля 2011

Размер массива не передается вместе с самим массивом. Поэтому, если другой функции нужен размер, она будет иметь его в качестве параметра.

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

И затем есть те функции (назовем их «ужасными», потому что они есть), которые будут заполнять массив произвольными данными до точки, определенной этими данными. sprintf это, пожалуй, «лучший» пример. Он будет продолжать помещать символы в этот массив до тех пор, пока не закончит их запись Это очень плохо, потому что между пользователем и функцией нет явного или неявного соглашения о том, насколько большим может быть этот массив. sprintf напишет некоторое количество символов, но у пользователя нет возможности точно узнать, сколько написано (в общем случае).

Вот почему вы должны никогда использовать sprintf; используйте snprintf или _snprintf, в зависимости от вашего компилятора.

1 голос
/ 10 июля 2011

Первая подпись просто пропускает массив, но никак не может сказать, насколько большой массив, и может привести к проблемам с за пределами ошибок и / или недостатками безопасности. \

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

Если это не домашняя работа, необработанные массивы немного устарели.Вместо этого используйте std :: vector.Это позволяет передавать вектор без необходимости вручную передавать размер, как это делает для вас.

1 голос
/ 10 июля 2011

вы также можете написать

void f( int *arr, int size )

, имея последний (размер), позволяющий не выходить за границы массива при чтении / записи в него

0 голосов
/ 10 июля 2011

Разница в том, что второй содержит параметр, который указывает размер массива.Логический вывод состоит в том, что если вы не используете такой параметр, функция не знает, каков размер массива.И это действительно так.На самом деле, он не знает, что у вас есть массив.На самом деле, вам не нужно иметь массив для вызова функции.

Синтаксис массива здесь, без указанного размера в квадратных скобках, является фальшивкой.Параметр на самом деле является указателем.Для получения дополнительной информации см. http://c -faq.com / aryptr / index.html , особенно раздел 4.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...