为什么 C++ 中的编译器知道数组的大小通过引用传递给函数?
Why is the size of an array passed to a function by reference known to the compiler in C++?
我知道当我想将一个数组传递给一个函数时,它会衰减为指针,所以它的大小是未知的,这两个声明是等价的:
void funtion(int *tab, int size);
和
void funtion(int tab[], int size);
我明白为什么。但是,我检查了当我传递一个数组作为参考时:
void funtion(int (&tab)[4]);
编译器会知道数组的大小,不会让我将不同大小的数组作为此函数的参数传递。
这是为什么?我知道当我按地址传递数组时,在计算数组中 ith 元素的位置时不会考虑大小,因此即使我明确包含它也会被丢弃它在函数声明中:
void funtion(int tab[4], int size);
但是当我通过引用传递数组时有什么不同?为什么编译器知道它的大小?
注意:我对编译时已知大小的数组感兴趣,所以我没有使用任何模板。
我在 Stack Overflow 上找到了 a similar question,但是它没有回答我的问题 - 它没有解释 为什么 编译器知道数组的大小,只有一些关于如何将数组传递给函数的信息。
因为它可以,而且因为检查增加了额外的安全性。编译器知道数组的大小,因为你在函数声明中这样告诉它。既然该信息可用,为什么不使用它来指示源代码中的错误呢?
真正的问题是为什么你的最后一个例子不会做同样的检查。不幸的是,这是 C 的另一个遗留问题——你永远不会传递一个数组,它总是会退化为一个指针。然后数组的大小变得无关紧要。
可以加一张支票吗?有可能,但它的用途有限(因为我们现在都在使用 std::array - 对吧!?),而且它无疑会破坏一些代码。例如:
void func (char Values [4]);
func ("x");
这目前是合法的,但不会对数组大小进行额外检查。
因为在这种情况下编译器没有提交奇怪的隐式类型更改。通常当你写:
void func(int[4]);
或
void func(void());
编译器决定 "help" 你并将它们翻译成:
void func(int *);
或
void func(void(*)());
虽然有趣 - 当您尝试返回其中一个时,它不会以这种方式帮助您。试试写:
int func()[4], func1()();
糟糕 - 惊喜 - 编译器错误。
否则数组就是数组,大小不变,可以使用sizeof
运算符获取。
然而,这经常被遗忘,因为上面提到的编译器行为,也因为隐式指针转换应用于数组类型的对象,而这不是预期的。这很常见。尽管以下是未应用隐式数组对象转换时的少数例外情况:
size_t arr[4],
(*parr)[3] = &arr, //taking the address of array
(&refarr)[3] = arr, //storing reference to array
sizearrobject = sizeof(arr); //taking the array object size
上面的例子会因为第二行和第三行的类型不兼容而触发编译错误。
我说的是 arr
对象没有自动转换成这样的情况:
(size_t*)&arr
好吧,有几种方法可以将数组传递给函数。您可以通过指针和引用传递它,并且有两种方法可以明确定义或不定义它的大小。
在您的问题中,您比较了这两种方式:
- 指向第一个元素的指针:
void f(int *arr)
- 对整个数组的引用:
void f(int (&arr)[size])
你问为什么只在其中一种情况下才需要指定大小。
您似乎假设它们之间的唯一区别是一个使用指针而另一个使用引用。但是这个说法是不正确的,它们有更多的区别:一个是指向第一个元素的指针,而第二个是对整个数组的引用。
您可以通过指向整个数组的指针传递数组:
void f(int (*arr)[size])
将它与您的示例进行比较,通过引用传递给整个数组:
void f(int (&arr)[size])
它们很相似,它们有相似的语法,它们都明确定义了数组大小。
另外,考虑一下:
void f(int &arr)
看起来像通过引用传递单个 int,但您可以向它传递一个未知大小的数组。
它的替代指针是
void f(int *arr)
您问为什么仅在其中一种情况下才需要指定数组大小。这是因为你使用的语法,而不是因为一个是指针另一个是引用。
正如我所说,您可以使用指针或引用。您可以指定数组大小,也可以允许使用任意大小的数组。这两个没有联系。
// by pointer by reference
/* Any size */ void f(int *arr) void f(int &arr)
/* Specific size */ void f(int (*arr)[x]) void f(int (&arr)[x])
我知道当我想将一个数组传递给一个函数时,它会衰减为指针,所以它的大小是未知的,这两个声明是等价的:
void funtion(int *tab, int size);
和
void funtion(int tab[], int size);
我明白为什么。但是,我检查了当我传递一个数组作为参考时:
void funtion(int (&tab)[4]);
编译器会知道数组的大小,不会让我将不同大小的数组作为此函数的参数传递。
这是为什么?我知道当我按地址传递数组时,在计算数组中 ith 元素的位置时不会考虑大小,因此即使我明确包含它也会被丢弃它在函数声明中:
void funtion(int tab[4], int size);
但是当我通过引用传递数组时有什么不同?为什么编译器知道它的大小?
注意:我对编译时已知大小的数组感兴趣,所以我没有使用任何模板。
我在 Stack Overflow 上找到了 a similar question,但是它没有回答我的问题 - 它没有解释 为什么 编译器知道数组的大小,只有一些关于如何将数组传递给函数的信息。
因为它可以,而且因为检查增加了额外的安全性。编译器知道数组的大小,因为你在函数声明中这样告诉它。既然该信息可用,为什么不使用它来指示源代码中的错误呢?
真正的问题是为什么你的最后一个例子不会做同样的检查。不幸的是,这是 C 的另一个遗留问题——你永远不会传递一个数组,它总是会退化为一个指针。然后数组的大小变得无关紧要。
可以加一张支票吗?有可能,但它的用途有限(因为我们现在都在使用 std::array - 对吧!?),而且它无疑会破坏一些代码。例如:
void func (char Values [4]);
func ("x");
这目前是合法的,但不会对数组大小进行额外检查。
因为在这种情况下编译器没有提交奇怪的隐式类型更改。通常当你写:
void func(int[4]);
或
void func(void());
编译器决定 "help" 你并将它们翻译成:
void func(int *);
或
void func(void(*)());
虽然有趣 - 当您尝试返回其中一个时,它不会以这种方式帮助您。试试写:
int func()[4], func1()();
糟糕 - 惊喜 - 编译器错误。
否则数组就是数组,大小不变,可以使用sizeof
运算符获取。
然而,这经常被遗忘,因为上面提到的编译器行为,也因为隐式指针转换应用于数组类型的对象,而这不是预期的。这很常见。尽管以下是未应用隐式数组对象转换时的少数例外情况:
size_t arr[4],
(*parr)[3] = &arr, //taking the address of array
(&refarr)[3] = arr, //storing reference to array
sizearrobject = sizeof(arr); //taking the array object size
上面的例子会因为第二行和第三行的类型不兼容而触发编译错误。
我说的是 arr
对象没有自动转换成这样的情况:
(size_t*)&arr
好吧,有几种方法可以将数组传递给函数。您可以通过指针和引用传递它,并且有两种方法可以明确定义或不定义它的大小。
在您的问题中,您比较了这两种方式:
- 指向第一个元素的指针:
void f(int *arr)
- 对整个数组的引用:
void f(int (&arr)[size])
你问为什么只在其中一种情况下才需要指定大小。
您似乎假设它们之间的唯一区别是一个使用指针而另一个使用引用。但是这个说法是不正确的,它们有更多的区别:一个是指向第一个元素的指针,而第二个是对整个数组的引用。
您可以通过指向整个数组的指针传递数组:
void f(int (*arr)[size])
将它与您的示例进行比较,通过引用传递给整个数组:
void f(int (&arr)[size])
它们很相似,它们有相似的语法,它们都明确定义了数组大小。
另外,考虑一下:
void f(int &arr)
看起来像通过引用传递单个 int,但您可以向它传递一个未知大小的数组。
它的替代指针是
void f(int *arr)
您问为什么仅在其中一种情况下才需要指定数组大小。这是因为你使用的语法,而不是因为一个是指针另一个是引用。
正如我所说,您可以使用指针或引用。您可以指定数组大小,也可以允许使用任意大小的数组。这两个没有联系。
// by pointer by reference
/* Any size */ void f(int *arr) void f(int &arr)
/* Specific size */ void f(int (*arr)[x]) void f(int (&arr)[x])