fortran fortran中文资料网
手气不错
Article

013.contextualKeywordsAndIdentifiersRules_关键字与命名规则及历史沿革

2026年5月16日入门级

Fortran关键字

在fortran中有许多关键字并非是保留关键字(reservedWords)存在,即一个词是否被作为关键字并起到设定作用,取决于其出现的位置,由编译器以语境区分来判断。

多数语言不允许直接使用保留字作为普通标识符,也有语言提供转义机制。

本文代码

#include <stdio.h>
int main(){
    int do = 3;
    printf("do=%d",do);
    return 0;
}
program do
integer :: program = 3
print *,"program=",program
end program do

编译运行

(base) hong@hongdeMacBook-Pro 013.contextualKeywordsAndIdentifiers % gfortran test.f90 
(base) hong@hongdeMacBook-Pro 013.contextualKeywordsAndIdentifiers % ./a.out 
 program=           3
(base) hong@hongdeMacBook-Pro 013.contextualKeywordsAndIdentifiers % gcc test.c 
test.c:3:9: error: expected identifier or '('
    3 |     int do = 3;
      |         ^
test.c:4:20: error: expected expression
    4 |     printf("do=%d",do);
      |                    ^
2 errors generated.
(base) hong@hongdeMacBook-Pro 013.contextualKeywordsAndIdentifiers %  

结果分析

  • fortran代码可以顺利编译运行,
    • 尽管使用fortran关键字do作为主程序标识符,但依旧被编译器允许。在program do中,program是关键字用以声明主程序开始,do作为标识符并非循环体开始的关键字。
    • 甚至把program声明为整数型变量,在integer :: program = 3中,integer是关键字用以声明整数型变量,program作为变量名。
  • c代码不可以通过编译
    • 因为do作为c语言的保留关键字严禁作为变量名。
    • 编译器会看到do,会优先按语法关键字解析,而不是变量名所以报expected identifier

类似do、program、subroutine、这种词作为标识符尽管是合法的,但因其本身已经承担了语法角色,会使得阅读体验下降,因此不适应被用来作标识符、变量名。

Fortran为什么会这么设计?

主要是因为早期Fortran66/77的“历史包袱”,关键字不严格保留、空格也不重要、是编译器几乎靠猜的设计,可以看一段“反人类”的代码,其中因历史局限我不作过多评价。

早期代码样例

      program test
      DO10I = 1,10
      print *, "正常循环"
10    continue

      DO10E = 1.10
      print *, "DO10E=?", DO10E
      end

编译运行

(base) hong@hongdeMacBook-Pro 013.contextualKeywordsAndIdentifiers % gfortran fortran6677temp.for -o fortran6677temp
(base) hong@hongdeMacBook-Pro 013.contextualKeywordsAndIdentifiers % ./fortran6677temp                              
 正常循环
 正常循环
 正常循环
 正常循环
 正常循环
 正常循环
 正常循环
 正常循环
 正常循环
 正常循环
 DO10E=?   1.10000002    
(base) hong@hongdeMacBook-Pro 013.contextualKeywordsAndIdentifiers % 

运行结果

  • 输出了10次“正常循环”对应的语句是DO10I = 1,10,其中DO10I就是DO 10 I = 1,10,循环十次。
  • 而DO10E=? 1.10000002是因为我故意写错把1,10写成了1.10,该表达式实际是将1.10赋值给了DO10E,这简直是灾难,此后的编程语言越来越倾向于使用强保留关键字与规范格式语法。所以应处处需要注意代码的可读性、可维护性、可复用性。

命名规则

标识符(identifier):该词的概念很宽泛,几乎所有需要编者自行命名的都可称之为标识符,用来给主程序、变量、数组、函数、子程序、模块、程序、常量等结构体命名。

基本规则

1. 标识符必须以字母开头,如

count
velocity
matrix3

以数字、下划线开头是非法的

3dSolve
_version2

2.后续字符可以是_,0-9,A-Z,a-z,

0-9  数字
A-Z,a-z  字母
_ 下划线

3.其他的所有字符都是非法字符,包括空格。

user-name
a+b
a value

大小写不敏感规则

以下都是同一个名字

count
Count
COUNT
CoUnT

fortran关键字大部分并非严格意义的保留关键字

非严格意义的保留关键字不等于任何位置都能随便用,语法上下文仍会限制解析。以下代码都是合法的,当读到program就会把do当成主程序名,而不是把do当成循环语句。但这是个危险的写法,不建议在学习中使用。在工程规范上多数也是严禁使用的。简写如:

program do
integer :: program

传统命名习惯

用I J K L M N作为整数循环变量是因为隐式声明规则。在不显示声明类型的情况下如I开头就是integer,X开头就是real。

implicit none禁止隐式推导类型

几乎是现代fortran项目的“必备”,我极少有见到2010年以后的项目还使用隐式声明的代码。

fortran标识符长度

我尝试过在gfortran14版本下使用63位长度的变量名,可以顺利通过编译,但长度达到64位以上会报错。一般也写不到这个长度,只是我个人好奇并在此刻记录。

一则趣事:fortran II(1958年发布)不支持大于6字符的标识符,不同实现可能有差异,或许也支持但只保留前6位。如X123456和X123457是同一个变量,如今的编译器再也无法复现这种奇葩问题。

      x123456=12345
x123457=98765
print *,"x123456= ?",x123456
print *,"x123457= ?",x123457
! 照我的理解,由于变量x123456只取x12345,而在语句x123457=98765中x12345被重新赋值为98765。最终都会输出98765。

现代流行的命名法

蛇形命名法snake_case,使用下划线作间隔。

velocity_x
pressure_value
matrix_size
如module fluid_solver

小驼峰命名法lowerCamelCase,在c语言中,小驼峰常用于变量名,大驼峰常用于类名。

velocityX ! 小驼峰 
PressureValue ! 大驼峰
matrixSize ! 小驼峰
如module fluidSolver

匈牙利命名法Hungarian notation,是把变量类型的缩写加上比如数组大小的前缀写在变量名的里,我记得古早basic、c语言教材中还要将该变量更具体的特征写进变量名从而长到离谱,在此不再展开。我在下面代码中只用一个字符表示类型来演示该命名法。

i_count  ! 在阅读编写距声明区很远的代码中也一眼看出该表达式中的变量是integer类型
rValue  ! real 匈牙利+驼峰
c_format_Name  ! character 匈牙利+蛇形+小驼峰
lDone  ! 逻辑

都有可取之处,本无优劣之分。我本不太愿讲自己的主观,但这里我简单提一句:我通常使用的是驼峰,但越来越觉得蛇形更“优雅”。例如:

subroutine compute_stress_tensor(strain_tensor, stress_tensor)
module mesh_utils
    integer :: max_iteration
    integer :: current_step
    real    :: poisson_ratio
    logical :: is_converged

    max_iteration = 100
    current_step = 0
    poisson_ratio = 0.3
    is_converged = .false.

其他的例如短横线命名法。因fortran不允许将写进标识符所以不再赘述。

不使用意义不明的词或单字符标识符

使用意义不明的词易造成误解,使用单字标识符更是“折磨”。简写如

real :: aPairOf
logical :: A
integer,parameter :: r

a pair of是什么变量? A大致是什么逻辑?常量r指密度rho还是半径radius又或是比值ratio?编译器只需要读通语法没问题即可,但读者/维护者恐怕很难接受这样的写法。

大小写不敏感到ASR-33、ibm穿孔卡、fortran历史沿革与传承

本文我已写了很久,但总觉意犹未尽,有些事没说清楚,索性再讲讲fortran与计算机发展的一些渊源。

早期的fortran写在穿孔卡上,穿孔卡由打孔、不打孔两种状态,比较近似今天二进制。有孔=1,没孔=0(严格来说更接近“机械字符的矩阵编码”而不是现代CPU纯bitstream)。最知名的产品莫过于IBM 80col punch card,早期的一行代码就是一张卡,该卡的作用类似今天的硬盘,是离线程序存储介质。想像这样一种情景:在纸盒一样的“存储设备”中有大量卡片,即使有稳固机械结构,也难免会有打乱、掉落,所以必须要对卡片进行编号,用以还原正确的顺序。这也直接影响了早期fortran的固定格式:

  • 1-5列:语句标签
  • 第6列:续行标记
  • 7-72列:真正代码
  • 73-80列:卡片编号

很多fortran编译器对固定格式里的“72列代码限制”,本质上并非语言特性,而是IBM 80列穿孔卡的物理遗产,73-80列是为了卡片意外乱掉后还可重新排序恢复程序,在某些现代项目的代码中还看得到后八位编号的存在。而且fortran的大小写不敏感并不是孤立设计,它和早期输入输出设备、字符集、穿孔卡生态有关。

由于使用电传打印机(teletype),而大量设备都不支持大小写区分,主要是设计/修理难度与经济性的原因。比如最著名的ASR-33 TELETYPE(该型号设备只能输出大写字符,在此我用全大写致敬)。该型号也深刻影响微软的诞生与早期发展,比如在Altair basic时代很多用户就是通过ASR-33与Altair8800交互,甚至今天微软仍在沿用的 CR(carriageReturn) 回车 / LF(lineFeed) 换行,本质上也是机械打印机时代传承下来的物理动作:CR是打印头回行首,LF是纸张向前滚一行。

因为ASR-33内存小处理慢打印速度低(约8-10字符/秒),启发了后来Unix基础设计哲学——“小工具+高效文本+管道”,比如:cat file | grep hello | sort。核心需求就是”让简单程序通过文本流自由组合”。这又深刻影响到今天的linux、bsd、macos、git、docker、kubernetes、ssh等。另外电传打印机teletypewriter简称的tty,今天在linux终端中的tty其实就是传承其名,例如:/dev/tty /dev/pts/0本质上也都是“teletype抽象接口”的历史遗产。

至于在类unix中普遍使用ctrl+c来终止程序,还是来源于ASR-33的ascii控制字符体系中的ETX(end of text)退出码ascii3。按顺序ctrl+@是ascii0,ctrl+a是ascii1,ctrl+b是ascii2。ctrl+c并非我起先想当然理解的control+cancel,而是按ascii码排的操作。在ASR-33上的ctrl键不是现代GUI里的快捷键,而是“生成控制字符”。后来unixTTY子系统(准确说是line discipline)将ETX解释为sigint并发送给前台进程组,于是ctrl+c逐渐成为终止程序的标准方式。打从unix所以到今天的linux、macos、bsd也依旧沿用了这套terminal signal机制。

话说回来,a==A必须成为fortran语言设计的一部分。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注