“printf”可以用作变量名吗?

问题描述

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 初始化为变量名时,您不能再使用 printf 命令,这与 scanf 相同。最初 printf 命令指向代码以获取输入,但现在您已将其分配给整数。

,

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

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

至于使用 printfscanf 作为变量名,只有在您尚未将它们声明为函数时才能这样做 - 换句话说,您如果您已经包含 stdio.h,则无法执行此操作。

C 有四个命名空间用于标识符(或者,更准确地说,四类命名空间);如果它们属于不同的名称空间,则只能对同一范围内的两个不同事物使用相同的标识符。命名空间是:

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

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

,

您可以在块作用域中或在结构、联合或枚举的标记的命名空间中,或在结构和联合的成员的命名空间中或在一个函数原型作用域。

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

这是一个演示程序。

scanf

它的输出是

printf

这是另一个演示程序,它展示了使用名称 #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

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

对于以下划线开头的名称,则根据 C 标准(7.1.3 保留标识符)

1 每个头文件都声明或定义了在它的列表中列出的所有标识符 相关的子条款,并可选地声明或定义标识符 列在其相关的未来库方向子条款中和 始终保留用于任何用途或用作的标识符 文件范围标识符。

——所有以下划线开头的标识符和 大写字母或其他下划线总是保留给任何 使用。

因此,无论是在文件作用域还是在块作用域中,您都不能使用此声明中的标识符

#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

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

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

int __;

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

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

 int _;

——所有以下划线开头的标识符都是保留的 用作普通和标签中具有文件范围的标识符 名称空间。

,
  • 变量的第一个字母应该是字母或下划线。

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

    所有以下划线开头的标识符以及大写字母或另一个下划线始终保留供任何使用。

  • 变量名(标识符)可以有多长没有规则

    不正确,列出了关于编译器跟踪多少字母的限制。在当前的标准中,外部标识符有 31 个字母,内部标识符有 63 个字母。在称为 C90 的旧版 C 中,这些限制对于外部标识符只有 6 个字母,这简直是疯了。如果您倾向于使用过时的编译器,则需要非常小心。 (在这种情况下,Dev C++ 已经过时但有些安全。)

可以将 printf 或 scanf 用作变量吗?

理论上,您可以在本地范围内执行此操作。 7.1.3 进一步说明:

在以下任何子条款(包括未来的库方向)中列出的具有文件范围的每个标识符都保留用作宏名称,如果包含任何关联的标头,则保留用作同一名称空间中具有文件范围的标识符.

这意味着您不能在文件范围内声明名为 printf 的变量或函数,但您可以在本地范围内这样做。当然,现在人们确实出于“函数模拟”的目的一直声明名为 printf 的宏,因此实际上它是无害的。

但一般的经验法则是:如果你做了非常奇怪的事情,那么期待非常奇怪的结果。众所周知,C 广泛使用 karma,给那些编写晦涩代码的人带来麻烦。