为啥你可以在 C 中以 $ 开头的变量名?

Posted

技术标签:

【中文标题】为啥你可以在 C 中以 $ 开头的变量名?【英文标题】:Why can you start a variable name with $ in C?为什么你可以在 C 中以 $ 开头的变量名? 【发布时间】:2019-09-16 01:03:18 【问题描述】:

我的印象是你只能以字母和_开头的变量名,但是在测试时,我还发现你可以用$开始变量名,如下所示:

代码

#include <stdio.h>

int main() 
    int myvar=13;
    int $var=42;
    printf("%d\n", myvar);
    printf("%d\n", $var);

输出

13
42

根据this resource,它说你不能在 C 中以 $ 开头变量名,这是错误的(至少在使用我的 gcc 版本、Apple LLVM 版本 10.0.1 (clang-1001.0.46.4) 编译时) )。我在网上找到的其他资源似乎也表明变量不能以 $ 开头,这就是我感到困惑的原因。

这些文章是否只是没有提及这种细微差别,如果是,为什么这是 C 的一个特性?

【问题讨论】:

你试过其他符号或编译器吗? $ in variable name?的可能重复 @JL2210 这个问题似乎专门针对 C++,而这个问题专门针对 C。 @uneven_mark 不,同样适用。 @JL2210:不是重复的。其他问题要么只有错误答案,要么只是针对 C18 进行了更改,我不会潜水检查。 【参考方案1】:

在 C 2018 标准中,第 6.4.2 条第 1 段允许实现在标识符中允许附加字符。

它将identifier定义为一个identifier-nondigit字符后跟任意数量的identifier-nondigitdigit em> 个字符。它将digit定义为“0”到“9”,并将identifier-nondigit字符定义为:

一个非数字,它是下划线、“a”到“z”或“A”到“Z”之一, 一个通用字符名称,或 其他实现定义的字符。

因此,实现可以定义标识符中允许的其他字符。

universal-character-name 中包含的字符是 C 标准附录 D 中列出的字符。

resource you link to 有几个地方有误:

C 中的变量名由字母(大写和小写)和数字组成。

这是错误的;标识符可以包括下划线和每个符合实现中的上述通用字符以及允许它们的实现中的其他字符。

$ 不允许——只有字母和 _

这是不正确的。 C 标准不要求实现允许“$”,但它并没有禁止实现允许它。某些实现允许使用“$”,而其他实现则不允许。可以说它不是严格符合 C 程序的一部分,但它可能是符合 C 程序的一部分。

【讨论】:

嘿;在访问 C18 草案时,我想我发现标准和 gcc 之间存在差异。如果表示为 unicode 代码点,则前导 $ 可能会起作用。查看我最近的编辑。【参考方案2】:

This 回答您的问题:

在 GNU C 中,您通常可以在标识符名称中使用美元符号。这是因为许多传统的 C 实现允许这样的标识符。但是,一些目标机器不支持标识符中的美元符号,通常是因为目标汇编器不允许它们。

【讨论】:

嗯,为什么目标汇编器会关心?为什么前端编译器不能自己转换所有标识符? @Dai 因为某些标识符最终会出现在输出中,例如共享库中的函数名。 @Dai:因为外部链接。局部变量不会在意,因为它们的名称已被删除。 @Dai clang 在 x86-64 上处理得很好,但 gcc 没有:gcc.godbolt.org/z/VFzmId。我认为它已被报告为错误。【参考方案3】:

这在 GCC 和 LLVM 中是允许的,因为许多传统的 C 实现都允许这样的标识符。

其中一个原因是 VMS 通常使用这些,其中许多系统库例程的名称类似于 SYS$SOMETHING

这是描述此内容的 GCC 文档的链接:

https://gcc.gnu.org/onlinedocs/gcc/Dollar-Signs.html

【讨论】:

【参考方案4】:

取决于 C 的方言和选择的选项。历史上,当 C 是新的时,一些 C 支持 $ 以与现有库兼容。如果严格遵守 C 对您有价值,您可能需要使用命令行选项来启用 $ 或其他选项。

历史上的一个点:在我早年进入足够多的大型机房间时,我知道 $ 是 IBM 大型机所说的 $、# 和 @ 的“国家字符”之一,它可能出现在编程语言的标识符中,例如PL/1 和大型机组装程序。这可以归结为一些大型机的衍生产品,例如 IBM 1130。在我看来,它就像早期的冲击式打印机,它使用一些形状的块进行打印,而 CRT 终端可以更换这些字符以满足外国客户的国内需求. IBM 1403 打印机有许多“打印链”可供选择,用于不同的人类语言和技术用途。

一些非 IBM 标识符至少出现在其中一些字符上。 GNU C、VMS 和 javascript 保留“$”。在大多数语言中,“$”是唯一一个似乎幸存到今天的 old 字符,即使作为一个选项。奇怪的是,在 IBM 早期,下划线对标识符名称无效。

【讨论】:

【参考方案5】:

TL;DR:它是汇编器而不是编译器

好的,所以我对此进行了一些研究。这并不是真正允许的,但是在装配过程中将其排除在外。尝试执行以下操作失败:

#include <stdio.h>

extern int $func();

int main() 
    int myvar=13;
    int $var=42;
    printf("%d\n", myvar);
    printf("%d\n", $var);
    $func();

joshua@nova:/tmp$ gcc -c test.c
/tmp/ccg7zLVB.s: Assembler messages:
/tmp/ccg7zLVB.s:31: Error: operand type mismatch for `call'
joshua@nova:/tmp$

我将 K&R C 版本 2(这涵盖 ANSI C)从我的书架上撤下,它说“标识符是字母和数字的序列。第一个字符必须是字母;下划线 _ 字符算作字母。大写字母和小写字母不同。标识符可以有任意长度……[省略过时的措辞]。”

此参考明显陈旧;几乎每个人都接受高 unicode 作为字母。正在发生的事情是后端汇编器按字节查看符号,并且每个具有高位集的字节都算作一个字母。如果您足够疯狂地在字符串文字之外使用 shift-jis,那么就会出现混乱;但除此之外,这往往效果很好。

我访问了一个draft of C18,上面写着identifier-nondigit: nondigit ; nondigit ; universal-character-name other-implementation-defined-characters。因此,允许实现允许附加字符。

对于universal-character-name,我们有一个限制:“通用字符名称不得指定短标识符小于 00A0 的字符 除了 0024 ( $ )、0040 ( @ ) 或 0060 (‘) 之外,也不是 D800 到 DFFF 范围内的任何一个。”

以下代码仍按预期在装配过程中阻塞:

#include <stdio.h>

extern int \U00000024func();

int main()

    return \U00000024func();

以下代码构建:

#include <stdio.h>

extern int func\U00000024();

int main()

    return func\U00000024();

【讨论】:

您可以免费查看草稿:pdf-archive.com/2014/10/02/ansi-iso-9899-1990-1/… C11 (n1570.pdf) 中的措辞完全相同。但是您似乎没有看过附录 D(这是规范性的); U+0024 在标识符中的通用字符名称中有效的代码值列表中不是。因此,C 标准不允许您在标识符中使用 \u0024,但它不会阻止实现允许 $ 作为“其他实现定义的字符”的一部分(这可能扩展到实现定义的使用\u0024 也一样)。

以上是关于为啥你可以在 C 中以 $ 开头的变量名?的主要内容,如果未能解决你的问题,请参考以下文章

C# 中变量名中 @ 字符的用途/含义是啥?

以“_”开头的变量是啥类型变量?

SPSS13.变量名包含非法的首字符 为啥

什么双下划线的意思是在C语言中的变量名

C语言变量命名规则

【变量】关于单下划线、双下划线开头变量的含义