C标准是否禁止重新申报?

时间:2015-08-27 14:15:07

标签: c namespaces

C没有名称空间,但是当编译器通过包含的名称静默覆盖某些名称时,它是否严格禁止重新声明?是否有效的想法,我可以命名我的函数,结构,类型我想要什么,并依赖重新声明编译时错误,以防名称冲突?

例如,如果我将我的结构命名为struct message或其他任何广泛的名称,我不希望依赖关系的某些依赖性因此而中断,或者相反覆盖我的名字。

4 个答案:

答案 0 :(得分:1)

  

C没有名称空间

C有一些简单的名称空间(不是C ++意义上的):

标识符的名称空间不同:结构标记名称,结构成员名称,标签名称,宏名称......

struct a;
int a = 0;  // valid, different name space

您可以拥有多个外部声明:

extern int y;
extern int y;  // valid

使用暂定定义,您可以拥有多个声明:

int z, z; // valid at file scope

您可以进行"良性重新定义"宏:

#define BLA 0
#define BLA 0  // valid

您可以重新声明不同范围内的标识符:

{
    int x;
    {
        double x;  // valid
    }
}

在同一范围内,重新标记标识符无效,至少您将获得诊断:

 int x = 0;
 double x = 0.0;  // not valid

如果在不同的翻译单元中有多个对象定义,则它是未定义的行为,但标准不需要诊断,所以要小心,因为您可能不会收到任何警告。

答案 1 :(得分:1)

与struct,union或enum标记关联的内容只能在同一范围内定义一次。此外,该标记只能在该范围内的struct,union或enum中使用,因为它们在该范围内共享相同的命名空间。

N1570 ISO/IEC 9899:201x Draft§6.7.2.3标签

答案 2 :(得分:1)

TL / DR版本 - 当同一作用域中的两个不同对象具有相同名称或尝试使用相同名称时,您只能依赖编译器捕获名称冲突枚举常量的名称和函数或对象名称,或者当您尝试对不同的structunionenum类型使用相同的标记名称时,或者当您尝试使用不同类型的相同typedef名称。否则,您的对象声明可能“影子”包含其他对象声明/定义。

编辑 - 有关保留供实施使用的标识符的说明,请参阅C 2011 standard的第7.1.3节。避免使用带有前导下划线或前导toisstr的标识符,并且您应该相当安全,至少就标准库而言。

James Michener Version - C有名称空间(参见C 2011 standard的第6.2.3节),它们不是用户可定义的:

  • 标签名称
  • structunionenum代码名称
  • 结构或工会的成员
  • 其他所有

因此,以下代码是合法的:

void foo( void )                // ordinary identifier
{
  struct foo {                  // tag name, disambiguated by presence of struct keyword 
    int foo;                    // member name, disambiguated by struct declaration syntax
  } bar;
  ...
  bar.foo = ...;                // member name, disambiguated by . operator
  goto foo;                     // label name, disambiguated by goto
  ...
  foo: ...                      // label name, disambiguated by label syntax
}

标识符可以“遮蔽”外部范围中定义的标识符:

char *i;                   // file scope identifier
void blah( void )
{
  int i;                   // block scope identifier, "shadows" file scope i
  if ( some_condition() )
  {
    double i;              // block scope identifier, "shadows" outer block scope i
    ...
  }
  ...
}

上述i中的每一个都指的是不同的对象。

相同的标识符可以在不同的范围内使用:

void foo( void )
{
  int i;  // local to foo
  ...
}

void bar( void )
{
  int i;  // local to bar
  ...
}

在上面的例子中,标识符i用于两个不同的对象。

如果标识符具有外部链接,则不同翻译单元中的标识符可以引用相同的对象:

/**
 * foo.c
 */  
void foo( void )
{
  extern int blah;  // will resolve to the object defined in bar.c
  printf( "blah = %d\n", blah );
}

/**
 * bar.c
 */
int blah = 5;

void bar( void )
{
  foo();
}

无法做的事情是在同一范围内有两个不同的对象具有相同的标识符,或者使用枚举常量作为函数或变量名等等:

void foo( void )
{
  enum bar { foo,   // illegal, clashes with identifier for function
             bar,
             bletch };
  enum bar bar;     // illegal, object name clashes with enumeration constant bar
  int i;
  double i;  // illegal, clashes with previous object definition
  ...
}

答案 3 :(得分:1)

为您提供更实用的答案:是的,您为类型,struct,函数等提供的名称很容易与其他库使用的名称冲突。并且没有自动功能可以神奇地修复它。

这是C编程语言的一个严重缺点。 C库本身甚至为自己使用保留了许多名称和前缀,并且知道可以安全使用的名称并不总是很简单。例如,如果您要包含<string.h>标题文件,那么以str开头的所有名称都将被禁止用于您自己的应用程序代码,例如strip之类的内容将不被允许你。

这就是为什么应该包含在其他代码中的软件包,通常对全局可见的所有名称使用nameprefix。例如POSIX&#39;线程使用前缀pthread_