本文选自:https://mp.weixin.qq.com/s/D_NjVesiFtz5_TTjH9myxQ
和https://mp.weixin.qq.com/s/WdYJN4Xc4ns4xhY-lBxhzQ
1983年,美国国家标准协会(American National StandardsInstitute)开始对C语言进行标准化,并于1989年推出C语言和C标准库的标准。该标准通常被称为ANSIC。由于该标准是1989年推出的,因此也被称为C89。时隔一年,1990国际标准协会ISO参照ANSI标准,推出一模一样的C语言和C标准库标准,将其作为ISO/IEC9899:1990国际标准,由于该标准是1990年提出的,因此被称为C90标准。可见C89、C90和ANSI C是指同一个标准。
1994年,ANSI/ISO联合组织想要为ANSIC标准加入有限的改动,使C标准在国际化字符、一些明显的缺陷、数值计算上更上一层楼,推出了C99标准。2011年12月8日,ISO发布了ISO/IEC9899:2011C语言标准,俗称C11。2018年6月,ISO发布了ISO/IEC9899:2018标准,这个标准通常称为C18(有时也称C17),它是C11的bug修复版,没有任何新的特性。C18是C语言的现行标准,未来将有新的标准C2X推出。
C语言主要有两大类标准,由三个组织制定,这三个组织分别是:
自由软件基金会(Free Software Foundation,FSF):是一个致力于推广自由软件、促进计算机用户自由的美国民间非盈利性组织。它于1985年10月由理查德·斯托曼建立。其主要工作是执行GNU计划,开发更多的自由软件,完善自由软件理念。Linux和GNU就是由这个组织维护的。
美国国家标准学会(American NationalStandards Institute,ANSI):它是非赢利性质的民间标准化团体,但它实际上已成为美国国家标准化中心,美国各界标准化活动都围绕它进行。
国际标准化组织(International Organizationfor Standardization,ISO)是标准化领域中的一个国际性非政府组织,致力于制定国际标准。
美国国家标准学会制定的C语言标准简称为ANSI C,国际标准化组织制定的C语言标准简称为标准C或ISO C。大约在90年代,ANSI与ISO相互接纳吸收对方的标准,所以,当前的标准C与ANSI C标准是一样的。GNU C 是自由软件基金会制定的C语言标准,它主要应用于Linux开发,比标准C支持更多的特性,所以GNU C≠标准C=ANSI C。
由于GNU C比标准C支持更多的特性,所以使用起来更加灵活,下面举例说明。
GNU C允许定义长度为0的数组,可能很多人会问长度为0的数组有什么用呢?当我们定义一个长度为0的数组时,说明这个数组不占用内存空间,但是我们可以通过这个数组来访问数组后面的变量,比如下面这个结构体:
struct data_pra {
char name[20]; //姓名
int start[0];
int num; //学号
int math; //数学成绩
int chinese; //语文成绩
};
struct data_pra student[10];
我们可以看到结构体中的start[0]、num、math和chinese都有相同类型,可以通过student[0].start[0]访问num变量,student[0].start[1]访问math变量,student[0].start[2]访问chinese变量等等。这样处理,可以使用循环语句实现数据的输入,简化了程序。参看以下程序。
#include <stdio.h>
struct data_pra {
char name[20]; //姓名
int start[0];
int num; //学号
int math; //数学成绩
int chinese; //语文成绩
};
int main() {
struct data_pra student[10];
int i,j;
for (i=0;i<2;i++) {
printf("请输入姓名:");
scanf("%s",student[i].name);
printf("请依次输入学号 语文成绩 数学成绩: ");
for(j=0;j<3;j++) {
scanf("%d",&student[i].start[j]);
}
}
printf("\n");
for(i=0;i<2;i++) {
printf("姓名:%s ",student[i].name);
printf("学号:%d\t",student[i].num);
printf("数学成绩:%d\t",student[i].math);
printf("语文成绩:%d\n",student[i].chinese);
}
return 0;
}
程序运行结果如下图所示。
注:本博主使用Dev C++开发环境。它支持C90/C99/GNU C90/GNU C99
虽然C语言新标准不断推出,但是,国内大多数C程序设计教材还停留在C90,包括国内最著名的教材,该教材的第五版仍然讲的是C90。虽然该教材在前言中说本教材是按照C99标准进行介绍的,但在内容介绍上仍然保留C90的内容。
由于教材无法跟上时代的步伐,学生获得的C语言知识没有得到更新,使得教学严重脱离应用。举一个例子:请看下面这个程序是否有错?
#include <stdio.h>
int fsize3(int n) {
char b[n+3];
return sizeof b;
}
int main() {
int n;
n=4;
printf("\t%d\n",fsize3(n));
n=7;
printf("\t%d\n",fsize3(n));
return 0;
}
大多数同学回答该程序的第3行有错误,因为,定义数组的下标表达式中有变量,他们接受的知识是数组下标必须是常量表达式!上面所说的那本著名教材在讲这个知识点时就是这么描述的,且专门举例说明“int a[3+5];"是合法的,”int a[n];"是不合法的。
那好,我们看编译器是否能正确编译上面这个程序?
编译结果如下图所示,该程序被正确编译了。
程序运行结果如下。
看到这个结果是否颠覆了你的认知?
从C99标准开始C语言支持VLA(Variable-length array,变长数组),VLA有如下特点:
1. 其长度不是编译时计算,而是运行时计算,因此长度可以用任意整型表达式指定。如上例程序中,第一次调用时,b有7个元素,第二次调用时,b有10个元素。
2. 没有初始化式,比如我们可以对数组a进行初始化,
int a[5]={1,2,3,4,5};
但不能对上例中的b进行初始化,因为b的下标表达式中有变量,在未使用前,无法确定下标个数。
3. 一般用在main()之
外的其他函数中,每次调用长度可以不同,如上例。
但在使用变长数组时一定要注意,下标中的变量在定义数组之前一定要赋值,否则,程序运行时有可能出错,因为,变量没有赋初值,其值是一个随机数,这个数可能很大,在定义数组时,由于申请的内存过大而导致内存出错。可参看下面2个截图,第一个截图,程序正确退出,第二个截图,程序异常退出。当然,如果下标中的变量已经赋值,但下标表达式计算结果过大,仍然会造成内存异常的。