是否值得声明作为参数传递的数组的(常量)大小

Is it worth declaring the (constant) size of an array that is passed as an argument?

本文关键字:数组 大小 常量 参数传递 值得 声明 是否      更新时间:2023-10-16
    const int N = 100;
    void function1(int array[]){
        // ...
    }
    void function2(int array[N]){
        // ...
    }
    int main(int argc, char *argv[]){
        int a[N] = {1, 2, 3, ... , 100};
        function1(a);
        function2(a);
        return 0;
    }

我想知道由于某种类型的C++编译器优化(例如,编译器在编译时计算出sizeof(array)),function2是否有可能比function1更快。

对于 C,之前已经讨论过相同的主题:我应该声明作为函数参数传递的数组的预期大小吗?

谢谢!

两个版本的函数之间应该没有任何性能差异;如果有的话,可以忽略不计。但是在你的function2()中,N 没有任何意义,因为你可以传递任何大小的数组。函数签名不会对数组大小施加任何约束,这意味着您不知道传递给函数的数组的实际大小。尝试传递大小50数组,编译器不会生成任何错误!

要解决这个问题,你可以将函数写成(它接受一个类型为 int 的数组,大小正好是 100!

const int N = 100;
void function2(int (&array)[N]) 
{
}
//usage
int a[100];
function2(a);  //correct - size of the array is exactly 100
int b[50];
function2(b);  //error - size of the array is not 100  

可以通过编写一个函数模板来概括这一点,该模板接受对类型为 T 且大小N数组的引用,如下所示:

template<typename T, size_t N>
void fun(T (&array)[N])
{
    //here you know the actual size of the array passed to this function!
    //size of array is : N
    //you can also calculate the size as
     size_t size_array = sizeof(array)/sizeof(T); //size_array turns out to be N
}
//usage
 int a[100];
 fun(a);  //T = int, N = 100  
 std::string s[25];
 fun(s);  //T = std::string, N = 25
 int *b = new [100];
 fun(b); //error - b is not an array!

大多数编译器的两个代码通常会将指向数组的指针作为堆栈(或指定寄存器中的)的函数参数传递......其他任何事情都需要将数组的数据复制到堆栈上函数激活记录的参数部分,这根本不会很快。

因此,例如,对于一个 6 个整数的数组,这两个实现在程序集中看起来像下面这样,该数组在调用 function1function2 的函数的范围内是本地的:

leal -24(%ebp), %eax  //store the address of the array in EAX
pushl %eax            //push the address on the stack as the first argument
call function1        //call function1 (or function2)

function1function2中,访问数组将像任何其他指针一样完成。 例如,int sum = array[0] + 5;的汇编代码如下所示:

movl 8(%ebp), %eax   //get the pointer to the array off the stack
movl (%eax), %eax    //dereference array[0] and store in EAX
addl $5, %eax        //add 5 to the value in EAX
movl %eax, -4(%ebp)  //store in "sum", which is at [EBP - 4] on the stack

我不认为明确传递大小有任何区别,因为它没有在任何地方收到,最终会丢失。人们仍然可以超越数组的限制进行访问。通常,我遵循的最佳做法是将大小作为单独的参数传递。

void doSomething(int *ptrary,int size)
{
}

不会有速度差异,但是声明大小可能出于另外两个原因而有用,一个是它将作为其他人的文档。另一个原因是,如果您将其声明为引用参数(如 Nawaz 建议的那样,只是没有模板化),它将防止其他人传入太小的缓冲区,这很棒!缺点是它还将防止传递太大的缓冲区。