C语言拾遗|基础类型的进阶理解

举报
debugzhang 发表于 2021/04/30 18:05:07 2021/04/30
【摘要】 深入的理解一些 C 语言的进阶知识点。

数据模型

数据模型:LP32,ILP32,LP64,LLP64,ILP64

32 位环境涉及 ILP32 数据模型,包括 32 位的 int、long 和指针。

64 位环境涉及 LP64 数据模型,包括 64 位的 long 和指针。

目前的 64 位系统中:

  • 类 Unix 系统使用 LP64 数据类型;
  • Windows 系统 使用 LLP64 数据类型。

表达式

编译器取词采用的是贪心算法,总是尽量多地取得合法的词汇。

类型自动转换

所有 float 类型都会现转换成 double 类型,再进行运算,哪怕只有一个 float。

在表达式中,如果 char 类型和 short 类型的值进行运算,无论 char 和 short 是否有符号,运算结果都会自动转换成 int。

__LINE__:当前代码行数

__FILE__:当前源文件名

__DATE__:当前日期

__TIME__:当前时间

多次运算引发的问题:尽量不使用宏函数,而使用普通函数进行处理。除非有其他原因,比如性能问题。

宏的缺点:

  • 宏在符号表中不存在,所以宏函数无法打热补丁,在调试过程中,无法引用;
  • 宏的使用会增大程序的代码空间;
  • 宏函数在单步调试时无法被进入;
  • 宏常量没有数据类型,编译器不能在编译代码的时候进行深入的类型检查。

枚举

枚举的大小和编译器、编译选项有关,具体大小无法统一确定,建议:

  • 不要使用枚举的大小;
  • 在结构体中使用枚举字段要谨慎;
  • 枚举间尽量不要强制转换(无法检查);
  • 枚举的值不要超过 32 位。

枚举的值如果没有指定,那么它的值就是其上一个值加一。

同一个枚举下面的值可以是相同的。

结构体对齐

4 字节对齐的运行速度要比 1 字节对齐的运行速度更快。

对于部分 CPU,结构体赋值语句对应的汇编指令是块拷贝指令(LDM 和 STM),而块拷贝指令要求基址必须是 4 字节对齐,否则就会导致数据异常复位,或者读取的数据与期望数据不一致的情况。

  • 是否会出错与具体的 CPU 和编译器强相关。

解决方法:

  • 自然对齐
    • 按照 8、4、2、1 字节的顺序调整成员声明的次序;
    • 不够对齐的字段用保留字段填充。
  • 用预编译选项 #pragma pack(n) 对齐
  • 不进行类型转换,不使用非同类型指针访问不对齐的结构
  • 尽量不对结构体直接赋值,而是使用 memcpy 拷贝整个结构体

基本概念:

  • 数据类型自身的对齐值:char 型数据自身对齐值为 1 字节,short 型为 2 字节,int/float 型为 4 字节,double 型为 8 字节。
  • 结构体或类的自身对齐值:其成员中自身对齐值最大的那个值。
  • 指定对齐值:#pragma pack (value) 时的指定对齐值 value。
  • 数据成员、结构体和类的有效对齐值:自身对齐值和指定对齐值中较小的那个值。

对齐原则:

  • 每个成员分别按照有效对齐值进行对齐,起始地址 % 有效对齐值 = 0。
    • 编译器在给结构体开辟空间时,首先找到结构体中最宽的基本数据类型,然后寻找内存地址能被该基本数据类型所整除的位置,作为结构体的首地址。
  • 结构体的默认对齐方式是它最大的成员的对齐方式,起始地址 % 最大成员有效对齐值 = 0。
  • 结构体对齐后的长度必须是其成员的有效对齐值的整数倍。
    • 为结构体的一个成员开辟空间之前,编译器首先检查预开辟空间的首地址相对于结构体首地址的偏移是否是本成员大小的整数倍,若是,则存放本成员,反之,则在本成员和上一个成员之间填充一定的字节,以达到整数倍的要求,也就是将预开辟空间的首地址后移几个字节。

inline

inline 函数没有调用开销,运行效率较高。同时它是一个真正的函数,所以编译器会检查它的参数类型,消除上述提到的宏函数带来的隐患。

但是,如果内联函数太复杂,或者函数调用点过多,就会导致展开后的代码膨胀,其带来的空间恶化可能大于效率提升带来的好处。

inline 对于编译器来说只是建议,所以即使对函数加上 inline 头,编译器也可能选择忽略这个建议。

在调用内联函数时,需要保证内联函数的定义能被编译器发现,所以内联函数要定义在头文件中,这种做法与通常情况下的函数定义不一样。

二维数组

如何定义一个返回二维数组的函数,其第二维的大小为 10?

int (*function())[10];

这个函数实际上返回了一个指针,该指针指向一个包含 10 个 intetger 的数组。

      function          - function
      function()        -1 个函数
     *function()        - 返回 1 个指针
    (*function())[10]   - 指向 1 个包含 10 个元素的数组
int (*function())[10]   - 元素的类型为 int

相关信息

Emai: debugzhang@163.com

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。