这个问题在那里向C大师们提出:
在C中,可以如下声明指针:
char (* p)[10];
..这基本上表明这个指针指向一个10个字符的数组.声明这样的指针的巧妙之处在于,如果尝试将不同大小的数组指针分配给p,则会出现编译时错误.如果您尝试将简单char指针的值赋给p,它也会给出编译时错误.我用gcc尝试了这个,它似乎适用于ANSI,C89和C99.
在我看来,声明像这样的指针非常有用 - 特别是在将指针传递给函数时.通常,人们会编写这样一个函数的原型:
void foo(char * p, int plen);
如果您期望具有特定大小的缓冲区,则只需测试plen的值.但是,您无法保证将p传递给您的人真的会在该缓冲区中为您提供有效的内存位置.你必须相信调用这个函数的人正在做正确的事情.另一方面:
void foo(char (*p)[10]);
..会强制调用者为您提供指定大小的缓冲区.
这似乎非常有用,但我从未见过在我遇到过的任何代码中都声明了这样的指针.
我的问题是:人们有没有理由不宣布像这样的指针?我没有看到一些明显的陷阱吗?
你在帖子中说的话绝对正确.我会说每个C开发人员都会得到完全相同的发现,并且当(如果)他们达到一定程度的C语言熟练程度时得出完全相同的结论.
当应用程序区域的细节调用特定固定大小的数组(数组大小是编译时常量)时,将这样的数组传递给函数的唯一正确方法是使用指向数组的指针
void foo(char (*p)[10]);
(在C++语言中,这也是通过引用完成的
void foo(char (&p)[10]);
).
这将启用语言级类型检查,这将确保提供完全正确大小的数组作为参数.实际上,在许多情况下,人们隐式地使用这种技术,甚至没有意识到,将数组类型隐藏在typedef名称后面
typedef int Vector3d[3]; void transform(Vector3d *vector); /* equivalent to `void transform(int (*vector)[3])` */ ... Vector3d vec; ... transform(&vec);
另外请注意,上述代码与Vector3d
作为数组或类型的类型无关struct
.您可以Vector3d
随时将数组的定义从数组切换为a struct
和back,并且您不必更改函数声明.在任何一种情况下,函数都将"通过引用"接收聚合对象(对此有例外,但在本讨论的上下文中这是真的).
但是,您不会经常过度地使用这种数组传递方法,因为太多人会因为语法相当复杂而感到困惑,并且对C语言的这些功能使用起来不够舒服.出于这个原因,在平均现实生活中,将数组作为指向其第一个元素的指针传递是一种更流行的方法.它看起来"更简单".
但实际上,使用指向数组传递的第一个元素的指针是一个非常小众的技术,一个技巧,它有一个非常特定的目的:它的唯一目的是促进传递不同大小的数组(即运行时大小) .如果你真的需要能够处理运行时大小的数组,那么传递这样一个数组的正确方法是通过指向其第一个元素的指针,其具有由附加参数提供的具体大小
void foo(char p[], unsigned plen);
实际上,在许多情况下,能够处理运行时大小的数组非常有用,这也有助于该方法的流行.许多C开发人员根本不会遇到(或永远不会认识到)处理固定大小数组的需要,因此对于正确的固定大小技术仍然无知.
但是,如果数组大小是固定的,则将其作为指向元素的指针传递
void foo(char p[])
这是一个重大的技术级错误,不幸的是,这些日子相当普遍.在这种情况下,指针到数组技术是一种更好的方法.
可能妨碍采用固定大小的数组传递技术的另一个原因是天真的方法对动态分配的数组的类型的主导地位.例如,如果程序调用类型的固定数组char[10]
(如在您的示例中),则平均开发人员将malloc
此类数组作为
char *p = malloc(10 * sizeof *p);
此数组不能传递给声明为的函数
void foo(char (*p)[10]);
这使得普通开发人员感到困惑,并使他们放弃了固定大小的参数声明,而没有进一步考虑.但实际上,问题的根源在于天真的malloc
方法.malloc
上面显示的格式应保留给运行时大小的数组.如果数组类型具有编译时大小,则更好的方法malloc
如下所示
char (*p)[10] = malloc(sizeof *p);
当然,这可以很容易地传递给上面声明的 foo
foo(p);
并且编译器将执行正确的类型检查.但同样,这对于一个毫无准备的C开发人员来说过于混乱,这就是为什么你不会经常在"典型的"日常平均代码中看到它.
我想补充一下AndreyT的答案(万一有人在这个页面上发现了关于这个主题的更多信息):
当我开始更多地使用这些声明时,我意识到在C中存在与它们相关的主要障碍(显然不是在C++中).通常情况下,您希望为调用者提供指向您已写入的缓冲区的const指针.不幸的是,当在C中声明这样的指针时,这是不可能的.换句话说,C标准(6.7.3 - 第8段)与这样的事情不一致:
int array[9]; const int (* p2)[9] = &array; /* Not legal unless array is const as well */
这种约束似乎不存在于C++中,使得这些类型的声明更加有用.但是在C的情况下,每当你想要一个指向固定大小缓冲区的const指针时,有必要回退到常规指针声明(除非缓冲区本身被声明为const开始).您可以在此邮件主题中找到更多信息:链接文本
这在我看来是一个严重的约束,它可能是人们通常不会在C中声明这样的指针的主要原因之一.另一个原因是大多数人甚至不知道你可以声明这样的指针AndreyT指出.