为什么在传递给C ++函数时将数组大小指定为参数?

时间:2018-10-30 09:46:21

标签: c++ arrays function parameters

我搜索了这个问题,大多数人都说同样的话。他们说,由于我们仅在函数中传递数组地址,因此编译器无法通过查看地址来了解数组大小。我试图通过使用此代码进行测试,并且两个函数都给出了相同的结果。那么,将数组大小指定为函数参数如何对我有实际帮助呢?在什么情况下指定尺寸对我们有帮助?。

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 :(得分:8)

这是关于您作为程序员有机会进行边界检查的,而不是编译器是否可以进行边界检查。

只需尝试打印出数组中所有元素,其大小为:

 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;
 }

答案 1 :(得分:3)

请注意,您的代码正在调用未定义的行为,因为您正在访问只有5个元素大的数组的元素7。使用size参数,例如,您可以检查索引是否超出其大小,而不必执行该调用。

在您的示例中,由于实际上没有使用参数,因此获得了相同的结果:

 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(如果它确实存在)。

答案 2 :(得分:2)

正如您所说,编译器无法确定将数组传递给函数的大小。您的第一个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;
  }

这样,您知道长度,并且函数可以在访问无效的内存位置之前对其进行检查。

答案 3 :(得分:0)

  

为什么在传递给C ++函数时为什么将数组大小指定为参数?

可以吗?
好吧,有时候。尽管在最终无处不在使用Range-TS的情况下,即使我能看到它演变成使用 ranges ,但在C ++中传递范围的规范方法还是使用迭代器对。

无论如何,还有其他方法可以传达我们想要处理的(子)范围。因此,让我们看一下:

  1. 带内信令,例如c字符串的NUL终止符。
  2. 函数协定的隐式部分,例如“它将始终完全是12个元素”。
  3. 传递所需零件的视图。不幸的是,在range-TS被完全合并之前,对此的标准库支持严重贫乏,在C ++ 17中仅限于std::string_view,并为连续范围(如数组)扩展了std::span在C ++ 20中(请立即查看guideline-support-library)。
  4. 使用迭代器对。迭代器的完全灵活性,尽管计算长度可能很昂贵,或者不消耗范围就不可能。这是标准库中的首选方法。
  5. 使用起始迭代器和长度。同样很普遍,但程度不同,并且不允许迭代器在您迭代时确定长度,这在这里不是问题。
  6. 使用(适当时为常数)对整个容器或范围的引用,可能是为了通用而模板化。可以将其与第3点结合使用,但不必这样做。

其中,如果您知道元素类型并限制为连续数组,则指针+长度是目前使用起来最舒适,最灵活的方法,不需要为不同的长度使用不同的代码,就是这样。

答案 4 :(得分:0)

将数组大小作为函数参数传递是一个坏主意,因为如果您需要将数组作为函数中的数组,则传递其大小将不会有任何效果。您传递的数组将被衰减为指针。因此,您需要按原样维护数组。

模板为将数组作为函数参数传递时提供了一种防止数组衰减的简单有效的方法。

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);
}

希望这会有所帮助。