编译过程及数据类型(一)

编译过程及数据类型(一)


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 PIprintf("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;
}

在这里插入图片描述

诚挚感谢传智播客王飞老师的详细讲解以及提供的素材。

发布了4 篇原创文章 · 获赞 0 · 访问量 79

猜你喜欢

转载自blog.csdn.net/Dylan_Cai/article/details/105034879