编译过程及数据类型(一)
文章目录
1. g++ 编译过程
1.1 预处理
-
生成的文件:由 xxx.c(源文件) 生成 xxx.i(预处理文件)。
-
工具:预处理器(包含在gcc编译集合工具中)。
-
生成命令:gcc -E xxx.c -o xxx.i 其中-E是预处理参数
-
作用:
-
头文件展开
- 展开stdio.h、stdlib.h等头文件内容,并将其具体展开内容和其它源码放在一起,存放到xxx.i文件中。(#include<stdio.h>这些预处理命令在预处理文件xxx.i中就不存在了,已经被具体内容代替了)
- 这个过程不负责检查语法错误,语法错误是在编译过程进行。所以,不管头文件里写的什么都可以,不管头文件后缀是什么格式都可以,都可以被展开和其他源码放在xxx.i中。
这一点可以测试一下:编写一个名字为111.txt的头文件,里边随便写一些什么。再写一个名字为text.c的源文件,其中使用111.txt这个头文件,即:#include<111.txt>。将头文件和源文件放在一个目录下,然后再执行gcc -E text.c -o text.i -I.这个命令,在生成的text.i文件中可以看到,这个胡乱写的头文件,也被展开了和其他剩余代码反正了一起。(这个命令最后加了一个==-I.==,分别是短横线、大写字母i、英文句号,作用是表示源文件使用的头文件在同一个目录下,否则不加这个-I.的话就找不到头文件了)。
-
宏定义替换
- 将宏名替换为宏值。
- 如:源文件中有
#define PI 3.14
,并且下边代码使用了PI,那么在转化成的xxx.i预处理文件当中,#define PI 3.14
消失不见,同时,下边代码中所有PI都变成了3.14。
-
替换注释
- 单行、多行注释在xxx.i中会被空行代替。
-
展开条件编译
- 根据条件来展开代码。
- 如:存在以下代码,
#ifdef PI
这个代码就是一个条件,如果定义了PI,就会把代码printf("PI has been defined.");
放到xxx.i当中,同时#ifdef PI
和#endif
消失不见。如果之前没有定义PI,那么#ifdef PI
和printf("PI has been defined.");
和#endif
这一整块都不会再xxx.i中显示。
#include<stdio.h> #define PI 3.14 int main(void) { #ifdef PI printf("PI has been defined."); #endif return 0; }
-
1.2 编译
- 生成的文件:由 xxx.i(预处理文件) 生成 xxx.s(汇编文件)。
- 工具:编译器(包含在gcc编译集合工具中)。
- 生成命令:gcc -S xxx.i -o xxx.s 其中-S是编译参数
- 作用:
- 逐行检查语法错误,这是gcc编译四个步骤当中最重要也是最耗时的。
- 将C程序翻译成汇编指令,得到.s汇编文件。
1.3 汇编
- 生成的文件:由 xxx.s(汇编文件) 生成 xxx.o(目标文件)。目标文件是二进制的机器语言,人类看不懂。(注意,虽然是二进制文件,但是打开之后不是101010这些,因为我们看到的101010是一系列字符串,而不是计算机识别的那个101010)。
- 工具:汇编器(包含在gcc编译集合工具中)。
- 生成命令:gcc -c xxx.s -o xxx.o 其中-c是汇编参数
- 作用:
- 翻译:将汇编指令翻译成对应的二进制指令。
1.4 链接
- 生成的文件:由 xxx.o(目标文件) 生成 xxx.exe(可执行文件)。可执行文件是二进制的机器语言,人类看不懂。
- 工具:链接器(包含在gcc编译集合工具中)。
- 生成命令:gcc xxx.o -o xxx.exe 无链接参数
- 作用:
- 库引入:引入函数库,如:printf、system、scanf等函数的函数库。
- 合并多目标文件
- 合并启动例程(例程是调用main函数的)
1.5 小结
- 以上过程可以参照下图:
- 以上过程不必须一步一步来,可以直接从任一个前边的通过指令转化成任一个后边的,如:
- 可以由xxx.c变成xxx.i(指令为gcc -E xxx.c -o xxx.i),
- 也可以从xxx.c变成xxx.s(指令为gcc -S xxx.c -o xxx.s),
- 也可以xxx.c变成xxx.o(指令为gcc -c xxx.c -o xxx.o),
- 也可以xxx.c变成xxx.exe(指令为gcc xxx.c -o xxx.exe),
- 也可以从xxx.i变成xxx.s(指令为gcc -S xxx.i -o xxx.s),
- 也可以xxx.i变成xxx.o(指令为gcc -c xxx.i -o xxx.o),
- 也可以xxx.i变成xxx.exe(指令为gcc xxx.i -o xxx.exe),
- 以此类推(下图列举部分)…
2. main函数
2.1 main函数两种标准形式
-
无参:
int main(void) {return 0;}
-
有参:
int main (int argc, char *argv[]) {return 0;}
(其中*argv[]可以写成**argv[])
2.2 main函数其他形式
-
int main()
-
void main()
-
int main(int c)
-
main()
-
main(int c)
-
等等其他…这些虽然不是标准形式,但是编译器可以接受。
3. 程序调试
-
程序调试是用来查找程序中出现的逻辑错误,并不是针对语法错误!无法错误是编辑器的事。
-
核心思想:让程序一行一行的执行。
-
行号:工具 —— 选项 —— 文本编辑器 —— c/c++ —— 行号(勾选)。
-
程序调试流程:
-
添加断点:一个或者多个
1.方法一:用鼠标左键单击想要添加断点这一行最左侧的灰色区域即可,再次点击则取消。
2.方法二:鼠标光标停止在想要添加断点这一行的任意位置,按F9可以添加,再次按F9消失。
-
调试:必须是在Debug模式下进行调试,不可以是Release模式。
-
F5启动调试(不同软件快捷键可能不同,可以在上方工具栏中查看)。
-
断点之前的程序可以执行,断点之后的程序先不执行。
-
调试方式:
1.**逐语句执行。**快捷键F11,逐条语句执行下一行,遇见函数进入到函数当中(指自定义函数),函数中继续逐条语句执行,函数中执行完毕之后跳出函数继续逐条跟踪执行。
2.**逐过程执行。**快捷键F10,逐个过程执行下一行,遇见函数,不进入函数内部,继续逐条跟踪执行。
3.逐断点执行。代码中有多个断点时,点击上方工具栏中的“继续”直接跳转到下一个断点,没有快捷键。
4.跳出函数:shift+10,可跳出当前断点所在的函数。(将该函数中未执行部分一次性执行)
-
4. 变量
4.1 变量的三要素
- 变量类型:整型、实型、字符型等等,用来开辟该变量的内存空间大小。
- 变量名:用于在程序中使用。
- 变量值:该变量存储的实际数据。
4.2 变量定义
- 定义语法:类型名 变量名 = 变量值。如:
int a = 10;
- 定义变量的时候,内存中会开辟出相应的空间给变量,但是变量声明中不会开辟内存空间。
4.3 变量声明
- 语法:
- 方法一:
int a;
没有变量值的变量定义,即为变量声明。 - 方法二:
extern int a;
使用关键字extern进行声明。
- 方法一:
- 特性:
- 如果想使用变量,必须先对他进行定义。
- 编译器在使用变量之前,会向前查找定义,如果没有定义,编译器会自动找到该变量的声明,把他提升为定义。
- 上一条中,如果是用extern进行声明的,不能提升。
5. 常量
常量是指不会变化,不会被修改的数据。大体可以有以下三种:
- “hello”是字符串常量,'a’是字符常量,25是整型常量,63.335是实型常量。
- 使用define进行宏定义(最常用),语法:
#define 宏名 宏值
。比如:#define PI 3.14
- 注意宏名和宏值中间没有等号,句尾也没有任何符号
- 使用关键字const,语法:const 类型名 变量名 = 变量值 比如:
const int a = 1;
- a本来是变量,但是经过const这个关键字修饰之后,就变成了只读变量。
6. 标识符
- 变量名和常量名的总称。
6.1 硬性要求
- 标识符由字母、数字、下划线(_)组成。
- 标识符不可以是关键字和函数名,如main、system、return、float等均不可以。
- 标识符不可以由数字开头,可以是字母和下划线开头。
- 大小写区分:
- 通常使用大写字母来表示常量,如:
#define MAX 200
- 通常使用小写字母来表示变量,如:
int a = 10;
- 通常使用大写字母来表示常量,如:
6.2 命名规范
-
大驼峰法:int HelloWorldHahaHohoHehe = 10;
- 多个单词组成变量名,每个单词首字母大写。
-
小驼峰法:int helloWorldHahaHohoHehe = 10;
- 多个单词组成变量名,首个单词的首字母小写,其余每个单词首字母大写。
-
小写+下划线:int hello_world_haha_hoho_hehe = 10;
- C 语言专用!
7. sizeof关键字
- sizeof不是函数,他是关键字。
- 用来求取数据类型、变量所用内存空间的大小。
- 使用方法:
- sizeof(变量名) 返回变量的大小,单位是字节。
- sizeof(数据类型) 返回数据类型所占内存空间的大小,单位是字节。
- sizeof 变量名 C语言的语法支持该写法,不常用。
8. printf函数
- printf是格式化输出函数。
- 一般形式为:
printf("格式匹配符", 输出表列);
8.1 格式匹配符d
常用形势有 %d %md %-md %ld %lld %hd
%d
表示按照整型数据的实际位数输出。%md
表示输出数据位数,如果m小于数据的实际位数,则按照数据实际位数输出,如果m大于数据的实际位数,则输出m位,结果右对齐,左侧空格补齐。%-md
表示当m大于数据实际位数时,输出m位,结果左对齐,右侧空格补齐。%ld
中字母l表示长整型。可以加入m、n、负号等修饰,规则同上。%lld
中字母ll表示long long整型。可以加入m、n、负号等修饰,规则同上。%hd
中字母h表示短整型。可以加入m、n、负号等修饰,规则同上。
#include <stdio.h>
int main(void)
{
int a = 222, m = 555;
short int b = 3;
long int c = 4;
long long int d = 5;
printf("%d\n",a);
printf("%4d,%2d\n",a,a);
printf("%4d,%-4d\n",a,a);
printf("%04d,%4d\n",a,a);
printf("%d + %d = %d\n",a, m, a+m);
printf("%hd",b);
printf("%ld",c);
printf("%lld",d);
return 0;
}
8.2 格式匹配符f
- 常用的形式由:
%f %m.nf %-m.nf %mf %.nf
%f
是按照实际长度输出实型数据。%-m.nf
中m是正整数,表示数据的宽度,包括小数部分、小数点和整数部分;n是正整数,表示小数位数。
#include <stdio.h>
int main(void)
{
float a = 3.1425;
printf("%f\n",a);
printf("%5.2f\n",a);
printf("%.2f\n",a);
printf("%-5.2f\n",a);
printf("%7.3f\n",a);
printf("%f\n",a);
printf("%4.8f\n",a);
return 0;
}
8.3 字符型格式匹配符
- 常用形式%c,输出一个字符。
#include<stdio.h>
int main(void)
{
char c = 'A';
printf("%c",c);
return 0;
}
8.4 字符串格式匹配符
- 常用形式
%s和%m.ns
。m表示允许输出的字符串长度,n表示对字符串截取的字符个数。
#include<stdio.h>
int main(void)
{
printf("%s\n","ABCDEFGHI");
printf("%3.2s\n","ABCDEFGHI");
printf("%-3s,%3s\n","ABCDEFGHI","ABCDEFGHI");
printf("%-.2s\n","ABCDEFGHI");
printf("%12.2s,%-12.2s\n","ABCDEFGHI","ABCDEFGHI");
printf("%12s,%-12s\n","ABCDEFGHI","ABCDEFGHI");
return 0;
}
9. 整型
获取数据类型的最大值最小值的时候,需要使用头文件#include<limits.h>
9.1 有符号整型
名称 | 形式 | 格式匹配符 | 占用空间大小 | 最小值 | 最大值 |
---|---|---|---|---|---|
短整型 | short (int) | %hd | 2字节 | -32768 | 32767 |
整型 | int | %d | 4字节 | -2147483648 | 2147483647 |
长整型 | long (int) | %ld | windows系统中32位和64位都是4个字节;Linux系统中,32位是4个字节,64位是8个字节 | -2147483648 | 2147483647 |
长 长整型 | long long (int) | %lld | 8个字节 | -9223372036854775808 | 9223372036854775807 |
#include <stdio.h>
#include <limits.h>
int main(void)
{
printf("short的大小为:%d个字节\n",sizeof(short));
printf("short的最小值是:%hd,最大值是:%hd\n",SHRT_MIN,SHRT_MAX);
printf("int的大小为:%d个字\n",sizeof(int));
printf("int的最小值是:%d,最大值是:%d\n",INT_MIX,INT_MAX);
printf("long的大小为:%d个字节\n",sizeof(long));
printf("long的最小值是:%ld,最大值是:%ld\n",LONG_MIN,LONG_MAX);
printf("long long的大小为:%d个字节\n",sizeof(long long));
printf("long long的最小值是:%lld,最大值是:%lld",LLONG_MIN,LLONG_MAX);
return 0;
}
9.2 无符号整型
名称 | 形式 | 格式匹配符 | 占用空间大小 | 最小值 | 最大值 |
---|---|---|---|---|---|
短整型 | unsigned short (int) | %hu | 2字节 | 0 | 65535 |
整型 | unsigned int | %u | 4字节 | 0 | 4294967295 |
长整型 | unsigned long (int) | %lu | windows系统中32位和64位都是4个字节;Linux系统中,32位是4个字节,64位是8个字节 | 0 | 4294967295 |
长 长整型 | unsigned long long (int) | %llu | 8个字节 | 0 | 18446744073709551615 |
#include <stdio.h>
#include <limits.h>
int main(void)
{
printf("unsigned short的大小为:%d个字节\n",sizeof(unsigned short));
printf("unsigned short的最大值是:%hu\n",USHRT_MAX);
printf("unsigned int的大小为:%d个字\n",sizeof(unsigned int));
printf("unsigned int的最大值是:%u\n",UINT_MAX);
printf("unsigned long的大小为:%d个字节\n",sizeof(unsigned long));
printf("long的最大值是:%lu\n",ULONG_MAX);
printf("unsigned long long的大小为:%d个字节\n",sizeof(unsigned long long));
printf("long long的最大值是:%llu",ULLONG_MAX);
return 0;
}
诚挚感谢传智播客王飞老师的详细讲解以及提供的素材。