GCC 编译命令初探
编写测试代码 hello.c
1 #include <stdio.h>
2
3 int main(void){
4 printf("hello world!n");
5 }
在代码目录下打开cmd命令行或打开VSCode中的命令行,VS Code快捷键是【Ctrl】 【~】
输入编译命令进行编译
代码语言:javascript复制1 gcc hello.c
然后输入dir
查看,可以看到生成了个叫a.exe
的可执行文件,在命令行输入a
即可运行它,看到打印了hello world!
以上就是最简单的gcc
编译命令,它每次都会默认生成一个a.exe
程序,如果我们想指定生成的程序名字,只需要加上一个参数-o
指定即可,o
表示out
,用于指定生成的程序名,如下
1 gcc hello.c -o hello
这次再查看,生成的就是hello.exe
,输入hello
即可运行它。
有时候我们写的程序有语法错误,我们希望编译的时候编译器能给出详细的提示信息,这时候就可以加上另一个参数-Wall
,让编译器在编译器时输出更多更详细的的信息,建议每次编译都加上这个参数,这样有什么错误也好查
1 gcc hello.c -Wall -o hello
以上就是我们本篇学习的编译命令,总结一下就是两个参数
-o
指定生成的文件名-Wall
让编译器工作时输出更多详细信息
到这里肯定有人要说了,每次都手敲这些命令好麻烦,直接使用VS Code
中的run
编译不就好了?如果直接使用VS Code
去run
那就脱离了我们学习GCC以及GNU工具链的本意了,对我们去理解C语言没有任何好处,初学时,多多手敲这些命令,熟悉他们,对我们来说是有很大好处的,否则你根本不知道编译器C程序时,IDE到底干了些什么事。
数据类型
以上表中,long
类型有两种情况,使用32位编译器编译,则long
为4字节,占32位,如果使用64位编译器,则long
为8字节,占64位二进制。当然这个也不是绝对的,仍然与系统环境有关。以上就是标准C中的基础数据类型,要记住,C中没有long long
类型,很多人将C语言与C 语言搞混,切记!
补充说明: 在C99新标准中,对C语言进行了扩展,其中提供了几种新的类型
- 新增复数类型(
_Complex
)和虚数类型(_Imaginary
) - 新增布尔类型(
_Bool
) - 新增整数类型
long long int
,该类型用于表示64位整数,共8字节,请注意与C 中的long long
区分
在C99 版本以前,C语言中是没有这些类型的,然而,C99并不是一个被广泛支持的C语言版本,例如微软旗下的VC编译器就坚决不支持C99,这些阻碍导致C99无法被普及,使得C语言新特性被割裂。
总结一句话就是这些新特性目前并没什么用,使用新特性语法将导致C语言难以被移植到其他平台,比如Windows,最后提一句,我们使用的GCC编译器是支持C99标准的,它不仅支持C99,它还支持目前C语言最新的标准C11,可见GCC编译器是非常厉害的。
修饰数值类型
除了直接使用这些类型,通常还会使用一个关键字unsigned
来修饰,它表达的意思是无符号,例如:
1 unsigned int len = 10;
我们以前讲过,计算机中,最高位是符号位,例如32位,只有31位是有效位,因为最高位要用来表示符号,为0表示正数,1则表示负数。这样一来能用于表示的实际范围就变小了,有时候我们根本不需要使用负数,这时候就可以使用unsigned
关键字来提升表示的范围,例如用unsigned
修饰int
后,就能将32位都有效的用于表示范围,则变量len
能表示的范围变成了0 ~ (
,如果不加关键字unsigned
,则默认被signed
修饰,即int len = 10;
等价于signed int len = 10;
,通常我们当然是用简洁写法,什么都不写啦。
基本数据类型的打印
在第一个示例中,我们已经使用printf
函数打印了hello world
,这里print是打印的意思,那么f
是什么意思呢?其实这里的f
是缩写,是format
的意思,代表格式化打印。既然是格式化打印,那一定会有格式占位符了,例如我要打印“”He is 18 years old"
这句话,显然18是一个变量,他今年18,明年就是19,所以在这句话中需要把一个变量拼进去,这个时候就可以使用占位符,占位符的作用就是先把位置占着,到合适的时候在替换,这就像拿个水杯到图书馆占座一样。根据输入输出的变量的类型不同,占位符也不同,这里介绍最常用的几个
%d
有符号十进制整数%f
浮点数%s
字符串%c
单个字符%x
十六进制整数
1 #include<stdio.h>
2
3 int main(void){
4 printf("He is %d years old",18);
5 printf("He is %s years old","18");
6 }
获取数据类型的长度
前面已经说过,在不同的硬键平台上,不同的编译器下,这些类型的实际长度可能都有出入,那么如何才能确切的指定当前某个数据类型的实际长度到底是多少呢?答案就是使用sizeof
运算符
1 #include<stdio.h>
2
3 int main(void){
4 printf("int size = %dn",sizeof(char));
5 printf("short size = %dn",sizeof(short));
6 printf("int size = %dn",sizeof(int));
7 printf("long size = %dn",sizeof(long));
8 printf("float size = %dn",sizeof(float));
9 printf("double size = %dn",sizeof(double));
10 }
输出:
代码语言:javascript复制1 int size = 1
2 short size = 2
3 int size = 4
4 long size = 4
5 float size = 4
6 double size = 8
可以看到,long
类型仍然是4字节,然而在我的Mac OS电脑上测试,64位GCC编译出来的long
输出的是8字节,在其他类Unix操作系统上一致,可见Windows系统上的C存在很多奇怪的特殊现象。所以再次建议尽量使用类Unix系统学习C语言,如Mac OS、Ubuntu系统等。
变量与常量
变量
在C语言中,变量必须先声明后使用,没有先声明的变量无法访问
代码语言:javascript复制 1 #include<stdio.h>
2
3 int main(void){
4 /*
5 声明变量
6 */
7 int width;
8 int height;
9 float price;
10
11 // 初始化变量
12 width = 100;
13 height = 200;
14
15 /*
16 对于浮点数,其字面量应带后缀f
17 这不是必须的,但应具备这样的编码规范
18 */
19 price = 15.6f;
20 }
注意,声明变量后没有初始化就使用,会造成一些不可预知的结果,因为未初始化的变量可能会具有一些随机值,而且这不是良好的C语言编程习惯,应当被批判。声明的变量没有预初始化为零值,这是C语言的一个缺陷!推荐的良好的编程范式,是在声明的同时对变量进行零值初始化。
代码语言:javascript复制1 int main(void){
2 /*
3 声明的同时进行零值初始化
4 不同类型的变量,其零值不同
5 */
6 int width = 0;
7 int height = 0;
8 float price = 0.0f;
9}
C语言还有一种在一行声明多个变量并初始化的方式,请警惕这其中的陷进
代码语言:javascript复制1 int a, b, c=10;
以上代码中,只有变量c
在声明的同时进行了初始化,而a
、b
均未初始化,在后续中可能会导致未对其初始化就使用了。建议在声明时都进行零值初始化
1 int a = 0, b = 0, c = 10;
为什么在大量的C教材中,都存在先声明,后初始化的代码范例呢?这是因为在最早的C语言版本中,不能在声明变量的同时初始化,换句话说,就是变量声明和初始化必须分开两行写,并且在最早的C语言经典教材《K&R》得到体现,那个时候的C语言学习者没有那么多资料,几乎全部都是根据这个教程入门学习C语言,他们成长起来后,传播并编写的资料仍然会沿用这种范式,正所谓祖传代码,薪火相传,一代一代人传下来,就造成了这种局面。实际上国外很早就有人指出C语言的很多缺陷和编码问题,后续出版的很多教程都冠以现代C语言,或C语言现代编程风格等字眼,只是据我观察,国内基本上没有好的C语言教材,大多陈旧落后,错漏百出,国内C语言教材销量基本靠高校强迫式的垄断销售,基本跟中小学里面老师强迫指定参考书一个路数。
常量
C语言中使用const
关键字修饰的就是常量,常量是不能修改的。C语言中,约定使用变量名大写来表示常量,多个单词则使用下划线分隔,例如MD_MARK
,这只是一种编码风格,不是必须的,但是建议遵守它,否则你可能会受到同行的鄙视。这样的好处是看的人一眼能识别出这是一个常量,而且能避免一些命名冲突。
1 const int PI = 3.14;
在C语言中,还有一种方式来定义常量
代码语言:javascript复制1 #include<stdio.h>
2
3 // 定义一个宏 PI
4 #define PI 3.14
5
6 int main(void){
7 printf("%f",PI);
8 }
注意,以上实际上是定义了一个宏,它并不是我们所说的那种真正意义上的常量,但它的效果等同于常量,而且在某种时候这种方式比使用const
关键字定义常量性能更好,这也是为什么许多C语言高手都喜欢使用宏的一个原因。定义宏时,是没有等号的,请留意。