"printf" 可以用作变量名吗?

Can "printf" be used as a variable name?

C中变量名规则如下:

  1. 变量名只能包含字母(大小写字母)、数字和下划线。
  2. 变量首字母应为字母或下划线。
  3. 变量名(标识符)的长度没有规定。但是,如果变量名超过 31 个字符,您可能 运行 在某些编译器中遇到问题。 (来源:[https://www.programiz.com/c-programming/c-variables-constants])

我想知道理论上是否可以将 single underbar(_)double underbar(__) 用作变量? printfscanf 可以用作变量吗?

在使用 c 编译器 (Dev C++) 和 linux Ubuntu Vi 时,即使我用上面的作为变量名,没有任何错误或警告。

我使用的代码如下:

#include <stdio.h>
int main(void){
   int scanf;
   int printf;
   int _;
   int __;
   return 0;
}

是的,它们可以使用。见:

代码:

~> cat demo.c
#include <stdio.h>

void show(int i) {
   printf("Just for show: %i\n", i);
}

int main(int argc, char **args) {
   int printf = 42;
   int _ = 11;
   int __ = 22;

   (void)argc;
   (void)args;

   show(printf);
   show(_);
   show(__);

   return 0;
}

没有错误的编译过程:

~>  gcc -std=c99 -Wall -Wextra -pedantic -o demo demo.c

输出:

~>  ./demo
Just for show: 42
Just for show: 11
Just for show: 22

当您将 printf 初始化为变量名时,您将无法再使用与 scanf 相同的 printf 命令。最初 printf 命令指向接受输入的代码,但现在您已将其分配给整数。

您可以在块作用域或结构、联合或枚举的标签名称 space 中或在名称 space 结构和联合的成员或在函数原型范围内。

在块作用域中声明的 scanfprintf 等名称将隐藏在文件作用域中声明的相应函数名称。但是您不能在文件范围内重新声明这些名称。

这是一个演示程序。

#include <stdio.h>

int main(void) 
{
    int printf = 10;

    {
        int *scanf = &printf;
        extern int printf(const char * restrict format, ...);
    
        printf( "printf = %d\n", *scanf );
    }       
    
    return 0;
}

它的输出是

printf = 10

这是另一个演示程序,展示了使用名称的其他方式 printf

#include <stdio.h>

struct printf
{
    int printf;
};

void f( int printf );

void f( int x )
{
    printf( "x = %d\n", x );
}

int main(void) 
{
    struct printf printf = { .printf = 10 };
    
    f( printf.printf );
    
    return 0;
}

程序输出为

x = 10

自然地,这样使用名称 printf 只会让代码的读者感到困惑。

下划线开头的名称按照C标准(7.1.3保留标识符)

1 Each header declares or defines all identifiers listed in its associated subclause, and optionally declares or defines identifiers listed in its associated future library directions subclause and identifiers which are always reserved either for any use or for use as file scope identifiers.

— All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.

因此您不能在文件范围或块范围内使用此声明中的标识符

int __;

该名称由实现保留。

至于您代码段中的这个声明

 int _;

然后因为它没有文件作用域(它有块作用域)然后在声明中使用标识符(使用此标识符的声明)是有效的。

但是如果你将在文件范围内声明名称,如

 int _;

 int main( vpid )
 {
     //...
 }

那么这样的标识符声明将是无效的。

此外,您不得在文件范围内使用此类标识符作为标记名称。例如

struct _
{
    int x;
};

int main( void )
{
    //...
}

— All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces.

  • The first letter of a variable should be either a letter or an underscore.

    使用下划线是不好的做法,因为这样的名称可能会与编译器库中的内部结构发生冲突 headers。 C 标准 7.1.3 规定:

    All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.

  • There is no rule on how long a variable name (identifier) can be

    不正确,列出了编译器跟踪的字母数量的限制。在此当前标准中,外部标识符有 31 个字母,内部标识符有 63 个字母。在 C 的旧版本 C90 中,这些限制只是外部标识符的 6 个字母,这简直是疯狂。如果您容易使用过时的编译器,则需要非常小心。 (Dev C++ 已过时但在这种情况下比较安全。)

and can printf or scanf be used as a variable?

理论上您可以在本地范围内这样做。 7.1.3 进一步指出:

Each identifier with file scope listed in any of the following subclauses (including the future library directions) is reserved for use as a macro name and as an identifier with file scope in the same name space if any of its associated headers is included.

意味着您不能在文件范围内声明名为 printf 的变量或函数,但您可以在本地范围内这样做。当然,现在人们确实总是为了“函数模拟”的目的而声明名为 printf 的宏,所以在实践中它是无害的。

但一般的经验法则是:如果你做的事情真的很奇怪,那么结果就会很奇怪。众所周知,C 广泛使用业力,给那些因编写晦涩难懂的代码而应得的人带来麻烦。

可以,这并不意味着你应该

是的,___ 是有效的标识符,因为它们满足标识符规则。它们可能导致代码难以阅读,并且可能与实现中已使用的名称冲突,因此您不应该以这种方式单独使用它们。

至于使用 printfscanf 作为变量名,只有在您还没有将它们声明为函数时才能这样做 - 换句话说,如果你已经包含了stdio.h.

,你就不能这样做了

C 有四个 name spaces 作为标识符(或者更恰当地说,四个 类 of name spaces);如果它们属于不同的名称 space,则只能对同一范围内的两个不同事物使用相同的标识符。 space 的名字是:

  • 所有标签(通过 goto 关键字或结尾的“:”消除歧义);
  • 所有标签名称(通过 structenumunion 关键字消除歧义);
  • structunion 成员名称(由 .-> 运算符消除歧义;标识符可用于多个 structunion 类型)
  • 所有其他名称(变量名、函数名、枚举常量等)

如果您包含了 stdio.h,那么 printfscanf 已经被声明为函数名称,因此您不能在该范围内也将它们用作变量名称因为函数名和变量名属于同一个名字space。但是,您可以将它们用作标记名称、成员名称或标签,但我强烈建议您不要这样做。