Почему мы указываем размер массива в качестве параметра при передаче функции в C ++? - PullRequest
0 голосов
/ 30 октября 2018

Я искал этот вопрос, большинство из них говорит то же самое. Так как мы передаем только адрес массива в функции, компилятор может не узнать размер массива, посмотрев на адрес, говорят они. Я попытался проверить это с помощью этого кода, и обе функции дали одинаковые результаты. Итак, как мне помогает практическое определение размера массива в качестве параметра функции? В каких условиях нам помогает указание размера?

class ArrayTest
{
    public:
     void say(int ar[])
     {
         cout<<ar[1]<<endl;
         cout<<ar[7]<<endl;
     }

      void say(int ar[],int sizeAn)
     {
         cout<<ar[1]<<endl;
         cout<<ar[7]<<endl;
     }

};

int main()
{
    ArrayTest test;
   int anAr[5] = {1,2,3,4,5};
   test.say(anAr);
   test.say(anAr,5);
    return 0;

}

Ответы [ 5 ]

0 голосов
/ 30 октября 2018

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

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

template<std::size_t N>
void foo(int (&your_array)[N])
{
   for(int i = 0; i < N; i++)
      //process array, N will be your array size.
}
//simply pass array when calling the function. N be taken automatically.

//somewhere else
int main()
{
  int arr[10];
  foo(arr);
}

надеюсь, это поможет.

0 голосов
/ 30 октября 2018

Почему мы указываем размер массива в качестве параметра при передаче функции в C ++?

Мы?
Ну иногда. Каноническим способом передачи диапазона в C ++ является использование пары итераторов, хотя, даже если я вижу, она развивается до использования диапазонов , когда Range-TS наконец используется повсеместно.

В любом случае, есть другие способы передать, с каким (под) диапазоном мы хотим работать. Итак, давайте посмотрим:

  1. Внутриполосная сигнализация, такая как NUL -терминатор для c-струн.
  2. Неявная часть контракта функций, такая как «это всегда будет ровно 12 элементов».
  3. Передача части, которую мы хотим. К сожалению, до тех пор, пока диапазоны-TS полностью не включены, поддержка стандартной библиотеки для этого является крайне анемичной, ограничена std::string_view в C ++ 17 и расширена std::span для смежных диапазонов (таких как массивы) в C ++ 20 (посмотрите сейчас на guideline-support-library).
  4. Использование пары итераторов. Полная гибкость итераторов, хотя вычисление длины может быть дорогостоящим или невозможным без использования диапазона. Это предпочтительный способ в стандартной библиотеке.
  5. Использование start-iterator и length. Также довольно часто, но не в той же степени, и не позволяет итераторам определять длину во время итерации, но это не проблема.
  6. Использование (константы, где это уместно) ссылки на весь контейнер или диапазон, вероятно, для общности. Это может быть объединено с пунктом 3, но не обязательно.

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

0 голосов
/ 30 октября 2018

Как вы говорите, компиляторы не могут сказать, насколько велик массив, если передан функции. Ваша первая функция say пытается сослаться на конец массива (ar [7] больше 5). Ваша вторая функция say означает, что вы можете проверить длину, чтобы убедиться, что вы не делаете эту ошибку.

  void say(int ar[], int sizeAn) 
  {
     if(sizeAn>1)
        cout<<ar[1];endl;
     if(sizeAn>7)
        cout<<ar[7];endl;
  }

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

0 голосов
/ 30 октября 2018

Речь идет о вас, как о программисте, имеющем возможность граничной проверки, а не о том, может ли это сделать компилятор.

Просто попробуйте распечатать все элементы в массиве с размером:

 void say(int ar[],int sizeAn)
 {
     for(int i=0; i< sizeAn; ++i)
         cout<<ar[i]<<endl;
 }

теперь без размера:

 void say(int ar[])
 {
     for(int i=0; i< /*HOW DO I KNOW NOW?*/; ++i)
         cout<<ar[i]<<endl;
 }
0 голосов
/ 30 октября 2018

Обратите внимание, что ваш код вызывает неопределенное поведение, потому что вы обращаетесь к элементу 7 массива, размер которого составляет всего 5 элементов. Используя параметр размера, вы можете, например, проверить, превышает ли индекс размер, и не делать этого вызова вместо этого.

В вашем примере вы получите те же результаты, потому что вы на самом деле не используете параметр:

 void say(int ar[],int sizeAn)
 {
     cout<<ar[1]<<endl;
     cout<<ar[7]<<endl;
 }

sizeAn не используется, поэтому не имеет значения. Но рассмотрим, например, следующий код:

void say(int ar[],int sizeAn)
     {
         for (int i = 0; i < sizeAn; i++){
             cout<<ar[i]<<endl;
         }
     }

Здесь он печатает все элементы в массиве, поэтому ему нужно знать, насколько велик массив. Например, если вы используете std::vector, вам не нужно передавать размер, так как вы можете просто вызвать функцию size, но вы не можете сделать это с массивами в стиле C, поэтому вам нужно передать этот размер в качестве параметра, если вы хотите написать функцию, которая ведет себя по-разному в зависимости от размера).

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

void say(int ar[],int sizeAn)
 {
     cout<<ar[1]<<endl;
     if (sizeAn >= 8){
         cout<<ar[7]<<endl;
     }
 }

Теперь он такой же, как ваш код с изменением, что он печатает только элемент 7, если он действительно существует.

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