这是我在学习期间发现的:
#include<iostream>
using namespace std;
int dis(char a[1])
{
int length = strlen(a);
char c = a[2];
return length;
}
int main()
{
char b[4] = "abc";
int c = dis(b);
cout << c;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
所以在变量中int dis(char a[1])
,[1]
似乎什么都不做,根本不起作用
,因为我可以使用a[2]
.就像int a[]
或char *a
.我知道数组名称是一个指针,以及如何传达一个数组,所以我的谜题不是这个部分.
我想知道的是为什么编译器允许这种行为(int a[1]
).或者它有其他我不知道的含义?
M.M*_*M.M 155
这是将数组传递给函数的语法的怪癖.
实际上,无法在C中传递数组.如果您编写的语法看起来应该通过数组,那么实际发生的是传递指向数组第一个元素的指针.
由于指针不包含任何长度信息,因此[]
实际上忽略了函数形式参数列表中的内容.
允许这种语法的决定是在20世纪70年代做出的,并且自从......以来引起了很大的混乱.
pat*_*pat 143
忽略第一个维度的长度,但需要额外维度的长度以允许编译器正确计算偏移量.在下面的示例中,foo
函数传递指向二维数组的指针.
#include <stdio.h>
void foo(int args[10][20])
{
printf("%zd\n", sizeof(args[0]));
}
int main(int argc, char **argv)
{
int a[2][20];
foo(a);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
[10]
忽略第一个维度的大小; 编译器不会阻止你索引结束(请注意,正式需要10个元素,但实际只提供2个元素).但是,第二个维度的大小[20]
用于确定每一行的步幅,这里,形式必须与实际匹配.同样,编译器也不会阻止您索引第二个维度的末尾.
从数组基址到元素的字节偏移量由下式args[row][col]
确定:
sizeof(int)*(col + 20*row)
Run Code Online (Sandbox Code Playgroud)
请注意,如果col >= 20
,那么您将实际索引到后续行(或整个数组的末尾).
sizeof(args[0])
,80
在我的机器上返回sizeof(int) == 4
.但是,如果我尝试接受sizeof(args)
,我会得到以下编译器警告:
foo.c:5:27: warning: sizeof on array function parameter will return size of 'int (*)[20]' instead of 'int [10][20]' [-Wsizeof-array-argument]
printf("%zd\n", sizeof(args));
^
foo.c:3:14: note: declared here
void foo(int args[10][20])
^
1 warning generated.
Run Code Online (Sandbox Code Playgroud)
在这里,编译器警告它只会给出数组已经衰减的指针的大小而不是数组本身的大小.
Sho*_*hoe 33
这个问题已经得到了广泛的解释由拍拍和马特.编译器基本上忽略了数组大小的第一个维度,有效地忽略了传递参数的大小.
另一方面,在C++中,您可以通过两种方式轻松克服此限制:
std::array
(自C++ 11起)如果您的函数只是尝试读取或修改现有数组(而不是复制它),则可以轻松使用引用.
例如,假设您想要一个函数来重置int
每个元素的十进制数组0
.您可以使用以下函数签名轻松完成此操作:
void reset(int (&array)[10]) { ... }
Run Code Online (Sandbox Code Playgroud)
您还可以使用模板使上述代码具有通用性:
template<class Type, std::size_t N>
void reset(Type (&array)[N]) { ... }
Run Code Online (Sandbox Code Playgroud)
最后你可以利用const
正确性.让我们考虑一个打印10个元素数组的函数:
void show(const int (&array)[10]) { ... }
Run Code Online (Sandbox Code Playgroud)
通过应用const
限定符,我们正在阻止可能的修改.
如果您认为上述语法既丑陋又不必要,就像我一样,我们可以把它放在can中并使用std::array
(因为C++ 11).
这是重构的代码:
void reset(std::array<int, 10>& array) { ... }
void show(std::array<int, 10> const& array) { ... }
Run Code Online (Sandbox Code Playgroud)
不是很棒吗?更不用说我之前教过你的通用代码技巧仍然有效:
template<class Type, std::size_t N>
void reset(std::array<Type, N>& array) { ... }
template<class Type, std::size_t N>
void show(const std::array<Type, N>& array) { ... }
Run Code Online (Sandbox Code Playgroud)
不仅如此,你还可以免费获得复制和移动语义.:)
void copy(std::array<Type, N> array) {
// a copy of the original passed array
// is made and can be dealt with indipendently
// from the original
}
Run Code Online (Sandbox Code Playgroud)
你还在等什么?去使用std::array
.
这是C的一个有趣的特点,如果你如此倾向,它可以让你有效地射击自己.
我认为原因是C只比汇编语言高出一步.大小检查和类似的安全功能已被删除,以便获得最佳性能,这是不是一件坏事,如果程序员正在很勤快.
此外,分配大小的函数参数的优点是当函数被另一个程序员,有一个机会,他们会发现一个大小限制.仅使用指针不会将该信息传达给下一个程序员.
首先,C从不检查数组边界.如果它们是本地的,全局的,静态的,参数等等都无所谓.检查数组边界意味着更多的处理,并且C应该是非常有效的,因此数组边界检查由程序员在需要时完成.
其次,有一个技巧可以将数组传递给函数.也可以从函数中按值返回值.您只需要使用struct创建一个新的数据类型.例如:
typedef struct {
int a[10];
} myarray_t;
myarray_t my_function(myarray_t foo) {
myarray_t bar;
...
return bar;
}
Run Code Online (Sandbox Code Playgroud)
你必须访问这样的元素:foo.a [1].额外的".a"可能看起来很奇怪,但这个技巧为C语言增添了很多功能.
告诉编译器myArray指向一个至少10个int的数组:
void bar(int myArray[static 10])
Run Code Online (Sandbox Code Playgroud)
如果访问myArray [10],一个好的编译器应该会给你一个警告.如果没有"static"关键字,那么10就没有任何意义.
这是C的一个众所周知的"特性",传递给C++,因为C++应该正确编译C代码.
问题来自几个方面:
你可以说在C中并不真正支持数组(这不是真的,正如我之前所说,但它是一个很好的近似值); 数组实际上被视为指向数据块的指针,并使用指针算法进行访问.由于C没有任何形式的RTTI你必须在函数原型中声明数组元素的大小(以支持指针运算).对于多维数组来说,这甚至更"真实".
无论如何,以上所有都不再是真的:p
大多数现代C/C++编译器都支持边界检查,但标准要求默认情况下关闭(为了向后兼容).例如,合理的最新版本的gcc使用"-O3 -Wall -Wextra"进行编译时范围检查,并使用"-fbounds-checking"进行完整的运行时边界检查.
归档时间: |
|
查看次数: |
9888 次 |
最近记录: |