是读 《c与指针》 的笔记
依据个人理解水平而写,详略若不得当还请见谅
结构和联合
结构和联合
结构体
结构基础知识
- 聚合结构类型:能够同时存储超过一个的单独数据
- C语言提供两种聚合结构类型:数组,结构
- 结构是一些值的集合,这些值是它的成员
- 结构变量属于标量变量
- 结构声明
- struct TAG {
}varible-list;
- typedef struct{
}Sample;
- typedef可以创建一种新的类型,Sample是类型名
- 可以将typedef形式的声明放到一个头文件中,方便后续使用
- 结构成员
- 一个结构的成员的名字可以和其他结构的成员的名字相同
- 结构成员的直接访问和间接访问
- (*sb).a = sb -> a;
- 结构体的自引用
struct self{
int a;
struct self b;
}
是非法的
struct self{
int a;
struct self *b;
}
是合法的
- 区别在于b在第一个里面是结构,会无限套娃;而在第二个里面是指针,可以自引用
- 自引用在链表等很常见
- 不完整的声明
- 声明一个相互依赖的结构
struct B;
struct A {
struct B *partner;
......
}
struct B{
struct A *partner;
......
}
- 结构体的初始化
- 类似多维数组,剩余的结构使用缺省值进行初始化
结构,指针和成员
- 访问指针
EX x = {};
EX *px = &x;
- px是一个指针变量
- *px+1 非法: *px是一个结构,C语言并没有规定整型与结构相加的情况
- *(px+1) 非法 px不是标量,如果是数组x就可以,因为x是标量
结构的存储分配
- 字节对齐
- 并不是永远要字节对齐:例如数据较少时为了可读性
- sizeof(结构体)读的是结构的整个长度,包含被跳过的字节
- 若想确定结构某个成员具体位置,可使用offsetof宏(定义于stedef.h)
- offsetof(struct SB,b);
作为函数参数的结构
- 把结构作为参数传入合法但低效
- 传一个指向结构的指针就很高效,对栈温柔一点
- void print_receipt(register Transaction const *trans)
- 寄存器
- 防修改
- 传指针
- 齐活了…
位段
- 位段声明必须为
int
signed
unsigned int
类型
-
缺:位段可移植性差,若考虑可移植则不要使用位段
-
优:
- 能够把长度为奇数的数据包装在一起,节省存储空间【例硬盘中应用】
- 源代码如果需要访问一个值内部的任意一些位,则使用位段比较方便
联合
- 联合基础知识
- 联合中所有成员引用的是内存中相同的位置
union {
float f;
int i;
}fi;
- 联合的长度是其最长成员的长度
- 变体记录
- 联合可以存指向不同变量的指针可以避免浪费,在使用时使用动态内存分配去初始化空间即可
- 联合的初始化
- 联合可以被初始化,但这个初始值必须是联合的第一个成员的类型
动态内存分配
有助于消除程序内部存在的限制
malloc() & free()
C库函数提供函数进行动态内存分配和释放
最基础:
#include<stdlib.h>
void *malloc(size_t size);
void free(void *pointer);
-
malloc()
- malloc从内存池中提取一块合适的内存,并向程序返回一个指向这块内存的指针
- 这块内存没有被初始化
- malloc分配的是连续的字节
- 分配失败则返回NULL
- malloc返回的指针类型是 void*,可以自己动手进行强制类型转换
- malloc返回的值总是符合字节对齐
-
free()
- free的参数要么是NULL,要么是一个从malloc、calloc、realloc返回的值
plus:calloc()、realloc()
void *calloc(size_t num_elements,size_t element_size);
void realloc(void *ptr,size_t new_size);
-
calloc()
- 在返回指向内存的指针前会把内存全部初始化为0
- 合理使用会很方便,但切记不要画蛇添足
- calloc的参数包括所需元素数量和每个元素的字节数
-
realloc()
- 用于修改一个原先已经分配的内存块的大小
- 可以扩,也可以减
- 扩:原内存块保留,新内存块被添加到原内存块后面,且未被初始化
- 减:内存块尾部的内存被拿掉,剩余部分保留
- 如果原内存块无法被修改,那么realloc会分配一块正确大小的内存并把原内容复制过来
- 所以,在使用完realloc后,就不能再使用指向旧内存的指针,而是应该改用realloc返回的新指针
- 如果realloc的第一个参数为NULL,则其行为等同于malloc
使用动态分配的内存及其常见错误
-
使用俺基本会
-
常见错误:
- 对NULL进行解引用
- 对分配的内存操作时越界
- 释放并非动态分配的内存
- 释放动态分配的内存的一部分(可以另辟蹊径使用realloc完成【虽然只能释放后面的内存】)
- 内存在被释放后继续被使用
-
分配完后一定要写一个检查释放分配成功的函数
char *new_string;
new_string = (char*)malloc(sizeof(char) * 100);
if(new_string == NULL){
printf("动态内存分配内存失败");
exit(1);
}
也可以使用宏定义实现一个不易发生错误的内存分配器(现在还不会,只懂皮毛,等会了发博客)
内存泄漏
- 只要不还,榨干可用内存,memory leak
- 是一个很严重的问题
- 记得free!!!