C原型范围

时间:2009-11-15 04:28:06

标签: c prototype scope designer specifications

我了解到了

  

声明的类型说明符   参数列表中的标识符   函数原型中的声明   (不是函数定义的一部分),   标识符有函数原型   范围,终止于   函数声明器。

请参阅下面提到的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 ;

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

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

4 个答案:

答案 0 :(得分:4)

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

考虑内存设置功能:

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

     

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

     

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

     

@AndreyT Yeh!我们俩都淹死在同一条船上:) - Ganesh Gopalasubramanian

反例

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

#include <cstdio>
using namespace std;

extern void x(struct c {int y;} b);

void x(struct c b)
{
    printf("b.y = %d\n", b.y);
}

int main()
{
    struct c a;
    a.y = 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 ++编译器和平台允许它?

答案 1 :(得分:2)

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

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

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

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

struct {
    int obj;
};

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

答案 2 :(得分:1)

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

int f(void *);

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

int f(struct dummy *);

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

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

答案 3 :(得分:0)

两个 - 让我们做三个 - 在函数原型中有参数名称的明显原因:

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