C语言基础要点

C语言基础

C程序编译过程

C程序编译步骤

  • 预处理:宏定义展开、头文件展开、条件编译等,同时将代码中的注释忽略,此处不检验语法
  • 编译:检验语法,将处理后的文件生成汇编文件
  • 汇编:将汇编文件生成目标文件(二进制文件)
  • 链接:C语言编写的程序需要依赖各种库,所以编译之后还需要把库链接到最终的可执行程序中

例子(gcc编译过程):

  • 分步编译

    预处理:gcc -E hello.c  -o hello.i
    编译:  gcc -S hello.i  -o hello.s
    汇编:  gcc -c hello.s  -o hello.o
    链接:  gcc    hello.o  -o hello
    
    选项 功能
    -E 只进行预处理
    -S 只进行预处理和编译
    -c 只进行预处理、编译和汇编
    -o <filename> 指定生成的输出文件名<filename>
  • 简化编译

      gcc hello.c -o hello
    

    不带选项,直接源文件编译为可执行文件(二进制文件)

汇编语言

一些汇编语言的操作符

操作符 作用
mov 移动
add 添加
push 压栈
pop 出栈
call 调用

eax 32位寄存器

32关键字

  • 数据类型关键字(12)

    • unsigned, signed
    • short, int, long, float, double, char
    • struct, union, enum
    • void
  • 存储类型关键字(5)

    • auto, static, register, extern, const
  • 控制语句关键字(12)

    • if, else, switch, case, default
    • for, while, do
    • break, continue
    • goto, return
  • 其他关键字(3)

    • sizeof
    • typedef
    • volatile

数据类型

  • 基本类型

    • 整型:short, int, long
    • 字符型:char
    • 实型【浮点型】:float,double
  • 构造类型

    • 数组类型
    • 结构类型:struct
    • 联合类型:union
    • 枚举类型:enum
  • 指针类型

常量

常量创建的两种方法

  • define宏定义

      #define 标识符 替换列表
    

    define定义的宏在编译后就不存在了【进行了宏展开】,它不占用内存,因为它不是变量

  • const修饰变量

    • const 是 constant 的缩写,意思是“恒定不变的”
    • 用 const 定义的变量的值是不允许改变的,即不允许给它重新赋值,即使是赋相同的值也不可以

    本质上是变量,只不过值是不允许改变的,所以叫常变量

      const 数据类型 变量名;
    

    用 const 修饰的变量,无论是全局变量还是局部变量,生存周期都是程序运行的整个过程

const VS define

  1. define是预编译指令,而const是普通变量的定义

    • define定义的宏是在预处理阶段展开的
    • const定义的只读变量是在编译运行阶段使用的
  2. const定义的是变量,而define定义的是常量

    • define定义的宏在编译后就不存在了,它不占用内存,因为它不是变量,系统只会给变量分配内存
    • const定义的常变量本质上仍然是一个变量,具有变量的基本属性,有类型、占用存储单元
    • 可以说,常变量是有名字的不变量,而常量是没有名字的。有名字就便于在程序中被引用,所以从使用的角度看
    • 除了不能作为数组的长度,用const定义的常变量具有宏的优点,而且使用更方便

因为const定义的对象有数据类型,而宏定义的对象没有数据类型。所以编译器可以对前者进行类型安全检查,而对后者只是机械地进行字符替换,没有类型安全检查。这样就很容易出问题,即“边际问题”或者说是“括号问题”

结论:所以编程时在使用const和define都可以的情况下尽量使用常变量来取代宏

补充:const 可以通过指针变相修改【C可以,C++不行】

const int num = 520;
int *p = &num;
p = 1314;

size程序

类型限定

extern 声明变量,在变量未赋值前不占用空间

const 定义常变量【存储值不能修改的变量】

volatile 防止编译器优化代码

register 定义寄存器变量

goto语句

goto语句原则供参考

  • 使用goto语句只能goto到同一函数

  • goto的始与终

    • 应是函数内一段小功能的结束处

    • 函数内另外一段小功能的开始处  

  • 不能从一段复杂的执行状态中的位置goto到另外一个位置

  • 应该避免像两个方向跳转

指针

  • 野指针

    指针变量指向一个未知的空间【无法确定其内容的空间】

    操作系统将0~255作为系统占用空间,不允许访问操作

  • 空指针

    空指针是指内存空间编号为0的空间

  • 万能指针

    万能指针可以接收任意类型变量的内存地址

    注意:如果需要改变变量值,需要转换对应类型

指针和字符串

char str[] = "hello world"; // 栈区字符串
char* ptr = "hello world";  // 数据区常量区字符串

// ch[2] = 'm'; // 通过
// p[2] = 'm';  // err
// *(p + 2) = 'm';  // err

作用域

变量类型 作用域 生命周期 存储位置
局部变量 函数内部 到函数结束 栈区
全局变量 项目中所有文件 到程序销毁 数据区
静态局部变量 函数内部 到程序销毁 数据区
静态全局变量 定义所在文件中 到程序销毁 数据区

函数

全局函数和静态函数

  • 在C语言中默认全部是全局函数,使用关键字static可以将函数声明为静态
  • 声明为静态意味着只能在创建函数的文件中使用

内存

  • 代码区【text】

    • 可执行文件的二进制代码(函数)

    共享,只读

  • 数据区【全局区静态区】

    • 未初始化数据区【bss】
    • 初始化数据区【data】
    • 文字常量区【data】
  • 栈区【stack】

    系统为每一个程序分配一个临时空间

    • 局部变量,函数信息,函数参数,数组...

    栈区大小为:1M

    • 可扩展【windows 10M】【Linux 16M】
  • 堆区【heap】

    存储大型数据【图片,音乐,视频】

    • 操作需求:

      • 手动开辟 malloc

      • 手动释放 free

    用于动态内存分配

进程内存结构

高地址 ↓

  • 栈区【向下增长】
  • 堆区【向上增长】
  • 未初始化全局变量【BSS区,用零初始化】
  • 已初始化全局变量、静态变量、常量数据【数据区】
  • 可执行代码【代码区】

低地址 ↑

可执行文件结构

  • 未初始化全局变量【BSS区,用零初始化】
  • 已初始化全局变量、静态变量、常量数据【数据区】
  • 可执行代码【代码区】

内存处理常见问题

  • 数组下标越界

  • 堆空间开辟野指针

    • int* p = (int*)malloc(0);
    • int* p = (int*)malloc(10); // 与整型不符,10/4
  • 堆空间不允许多次释放【空指针可以】

  • 开辟所用指针迷失,释放出错

联合体

  • 共用体占用的内存应足够存储共用体中最大的成员
  • 同一时间只使用一个变量【以最后一次赋值为基准】

文件指针

typedef struct {

    short           level;      // 缓冲区“满”或者“空”的程度
    unsigned        flags;      // 文件状态标志
    char            fd;         // 文件描述符
    unsigned char   hold;       // 如无缓冲区不读取字符
    short           bsize;      // 缓冲区的大小
    unsigned char   *buffer;    // 数据缓冲区的位置
    unsigned        ar;         // 指针,当前指向
    unsigned        istemp;     // 临时文件,指示器
    short           token;      // 用于有效性的检查

} FILE;
posted @ 2022-05-05 14:52  sha0dow  阅读(73)  评论(0编辑  收藏  举报