C原型范围

我了解到了

在函数原型(不是函数定义的一部分)中的参数声明列表中声明标识符的类型说明符,标识符具有函数原型作用域,它终止于函数声明符的末尾。

请参阅下面提到的C程序。

void fn (struct st {int a;} a, struct st b) ; struct st obj ; 

编译器会立即发出错误,因为’obj’大小未知(或)struct st不是’type’。 那就对了! 结构’struct st’的声明在原型声明中结束。

我相信原型有这个限制因为我们也可以在原型声明中使用一些变量名。 这些名称可能与同一范围内的变量(与函数原型的变量)冲突。 如下。

 void fn (int a) ; int a ; 

因此,为了允许上述声明,原型的范围是有限的。 (如果我错了,请纠正我)

但是,对于原型声明,参数变量名称没有用。 那么,为什么它是“狭隘的范围”? 拥有参数变量名称有什么意义? 什么是语言设计者(或)规范对此的想法?

参数名称可以帮助记录参数的使用。

考虑一个内存设置function:

 void mem_set(void *, int, int); void mem_set(void *buffer, int value, int nbytes); 

哪个更容易理解?


写入的结构类型声明是函数原型的本地。 正如您可能知道的那样(现在,如果不是之前),您需要定义原型范围之外的类型才能成功使用它。 也就是说,你必须写:

 struct st {int a;}; void fn(struct st a, struct st b); 

你说:

我相信原型有这个限制因为我们也可以在原型声明中使用一些变量名。 这些名称可能与同一范围内的变量(与函数原型的变量)冲突。 如下。

 void fn(int a); int a; 

因此,为了允许上述声明,原型的范围是有限的。 (如果我错了,请纠正我)

有’-Wshadow’的GCC可能会警告参数’a’遮蔽全局变量’a’ – 它肯定会在函数定义中这样做,并且可能在函数声明中这样做。 但这不是强制性警告; 所写的代码是合法的C – 尽管由于阴影而略显可疑。


在评论中有一个旷日持久的讨论“为什么C限制(阻止)你在参数列表中声明一个类型”,子文本“因为C ++允许你这样做”:

评论

被允许使用/ ** /,应该由程序员负责(根据编码实践)添加关于语言使用的正确评论。 我认为除了为评论提供帮助外,还必须有“其他”的东西。 – Ganesh Gopalasubramanian

好的 – 相信。 与C ++所做的兼容是剩下的原因,并在那里添加了参数名称以提高可读性。 请参阅Stroustrup的“C ++的设计和演变”。 请注意,原型中参数的名称不是界面的一部分 – 请参阅有关按名称而不是位置提供参数的讨论。 – 乔纳森莱弗勒

我相信OP提出的问题是“具有function原型范围的重点是什么?”。 不幸的是,你的答案并未对此有任何启示。 坦率地说,我也不知道。 如果他们只是想在OP猜测时限制命名参数声明的范围(在非定义声明中),他们可以在不引入范围的情况下完成它(例如在C ++中完成)。 – AndreyT

@AndreyT:在函数定义中,参数是函数体的局部参数(不再可能通过函数体最外面的块中的局部变量隐藏参数)。 原型在逻辑上声明了函数内部的类型,因此应该按照定义的范围 – 因此,仅在函数原型中定义的类型不能在函数外部使用,并且无法调用的函数对任何人都没有什么好处。 – 乔纳森莱弗勒

@Jonathan Leffler:您似乎在解释为什么允许参数名称(“与C ++的兼容性” – 确定)。 我想知道的是引入“函数原型范围”的基本原理。 为什么他们认为需要引入这样的范围? C ++没有这样做。 C ++中没有函数原型范围。 – AndreyT

@AndreyT Yeh! 我们俩都在同一条船上溺水:) – Ganesh Gopalasubramanian

反例

这表明C ++“确实这样做”。

 #include  using namespace std; extern void x(struct c {int y;} b); void x(struct cb) { printf("by = %d\n", by); } int main() { struct ca; ay = 0; x(a); return(0); } 

此代码不能使用G ++(MacOS X 10.5.8上的4.0.1)进行编译。 它抱怨说:

 $ g++ -o xx xx.cpp xx.cpp:4: error: types may not be defined in parameter types $ 

错误发生在警告/错误的默认级别 – 以及迂腐级别。

因此,在这种情况下,“C表现为C ++的行为”似乎是公平和准确的。 你能用反例来演示如何在C ++中用函数原型定义一个类型,指定哪个C ++编译器和平台允许它?

我会回应chrisharris和Jonathan Leffler所说的话。 将标识符限定为原型是有用的。 比不拥有它们更有用。 另外,它允许原型遵循与函数声明相同的语法,从语言设计的角度以及从实现者的角度来看,这可能是很好的。

我不认为这是你提出的建议,但只是为了完整性,使这些标识符具有原型之外的范围将没有多大意义并且可能导致问题。

事实上你可以在原型之外声明一些在原型之外无用的东西是一个缺点(我猜),但这是非常无害的。 修复是“不要那样做”。 我没有意识到任何隐藏的问题,让你宣布一些你不能在原型之外使用的东西。

如果我们要解决这个问题,我们还应该解决C允许你声明类似的事实:

 struct { int obj; }; 

其中声明了一个不太有用的结构类型。

原型范围的一种可能用途是声明一个函数,该函数需要传递void *类型的指针,为此传递其他类型会产生诊断消息。 如果您将函数原型化为:

 int f(void *); 

然后传递任何指针类型是有效的。 但是如果你将函数原型化为:

 int f(struct dummy *); 

那么就没有办法声明兼容的指针来传递函数,而且函数本质上需要一个void *参数。

这是否具有任何现实世界的实用性,以及它是美丽的还是丑陋的,都是我将要留下的另一天的主题。

二 – 让这三个 – 在函数原型中有参数名称的明显原因:

  • 名称表示每个参数的用途。
  • 名称允许您在API文档中更容易地引用参数(例如在Doxygen中)。
  • 您可以更轻松地从定义生成原型(剪切和粘贴,或自动化)。