7.19
结构体字节对齐
字节对齐:一次性分配多少个字节
自然对齐(32OS):
char:1字节对齐
short:2字节对齐
int:4字节对齐
long:4字节对齐
float:4字节对齐
double:4字节对齐方式,分配的时候分配两次(因为32os最大只有4字节,而double占8字节)
下面的案例没有特殊说明均采用32os
注意:按照成员中最大的那个对齐
上图中最大的是int,占4字节,所以在分配内存时按4字节大小依次分配,
结构体中每个变量需在不超过系统默认字节对齐数(最大字节)的条件下满足自身对齐的要求
(自身所占字节)(我的理解为补位),且整个结构体也需满足对齐规则。
就比如上图第二个结构体,第二次分配4字节,但是char类型占1字节,还剩3字节刚好可以满足short类型要求的2字节
关于数组的字节对齐
按double4字节分配,int占4字节,char str[29]每一个元素占1字节,字节对齐需要
4*8=32>29字节,所以char str[29]字节对齐后还剩3字节,double本身占8字节,加起来就是44字节
下图编程环境为64位ubuntu
int占8字节,short占6字节剩2字节,char占3字节(每一个元素占1字节)补位2字节,剩1字节进行字节对齐,double占16字节。加起来就是40字节。
利用位域or位段进行字节对齐(节省空间)
上图year,month,day对应数字2100,12,31.二进制位数为12,4,5.只要输入的数字小与这个数,就能满足内存要求。
字节对齐:4*5+4*1+4=28.因为1字节占8位,4字节就是32位>12+4+5=21。所以struct date整体占4字节。
注意:
使用位域进行内存分配时,不能使用标准输入给位域输入,因为标准输入找的是地址,地址的最小单位是字节。
7.21
自定义头文件,gcc编译流程,多文件编译,make工程管理器。
自定义头文件
#ifndef 标号(一般用大写表示,把.变成_)
#define 标号
- 头文件
- 宏定义
- 结构体类型的定义
- 枚举类型的定义
- 函数声明
#endif
添加的#号的作用:避免头文件重复引用
gcc编译流程
分为预处理、编译、汇编、链接四个阶段
方法1:
预处理(处理以#开头的文件):gcc -E stu.c -o stu.i
编译(生成汇编语言):gcc -S stu.i -o stu.s
汇编(将汇编语言转成及其代码):gcc -c stu.s -o stu.o
链接(将所有的机器代码汇集生成一个可执行文件):gcc stu.o -o stu
方法2:
将gcc的前四步合四为一
gcc stu.c -o stu
多文件编译
先将所有的.c文件生成对应的.o文件
再将所有的.o文件汇集生成一个可执行文件
make工程管理器
作用:
简化多文件编译操作,将多文件编译的命令写在makefile文件里,需要编译的时候只需要输入一个make命令即可。
(make会在当前路径下找一个叫做Makefile/makefile(M大小写都可以)的文件)
makefile的命名规范
目标文件1:依赖文件1
(Tab键)依赖文件1是如何生成目标文件1
目标文件2:依赖文件2
(Tab键)依赖文件2是如何生成目标文件2
举例:
gcc -c test.c -o test.o
test.o是test.c的目标文件,test.o文件是由test.c 文件生成的。
注意:
- 一个Makefile中可以有多个目标文件
- 如果两个目标之间没有依赖,系统默认只执行第一个目标
- 没有依赖文件的叫做假目标
当我们在编写依赖文件是如何生成目标文件的命令时发现需要将每一个.c文件是怎么生成.o文件的命令都需要输入一遍,使makefile变得复杂,所以需要使用变量。
预定义变量:
CC :编译器选项的名称,默认为cc
CFLAGS:编译器的选项,无默认值
RM:程序删除的名称,默认为rm -f
自定义变量:
OBJ和OBJS,一个用于存放可执行文件,一个用于存放所有的依赖文件
自动变量:
$@:目标文件
$^:所有的依赖文件
$<:第一个依赖文件
($类似指针中的*号,表示取出里面的内容)
.PHONY表示防止当前路径下存在和假目标相同命名的文件,导致无法执行假目标
7.23
该程序的功能是将形参n所指变量中,各位上为奇数的数去掉,剩余的数按原来从低位到高位的顺序组成一个新数。
unsigned long fun(unsigned long n)
{
unsigned long x = 0; int t;
while (n)
{
t = n % 10;
/********** found **********/
if (t % 2 == 0)
/********** found **********/
x = x*10 + t;
/********** found **********/
n = n/10;
}
return x;
}
首先获取最低位上的数字,判断是否使偶数,x为保存的偶数,每次扩大10倍加上个位数 ,数字n每次去掉个位数,继续循环