Day10
共用体(Union)
也称为联合体,和结构体类似,也是由多个相同或不同类型的数据成员构成的集合。但和结构体不同的是它的所有数据成员都是共享同一块内存空间,并没有独立的内存空间
共用体的用法和结构体是一样的,只是使用的是 union 关键字
共用体的总大小就是其数据成员
一定要弄清楚共用体的内存布局(即内存存储形态)
#include <stdio.h>
// 声明共用体类型 demo,类型名是 demo
union demo
{
int i;
char c;
short s;
long l;
char a[5];
};
typedef union
{
float f;
int i;
short s;
} money;
int main()
{
// 使用上面声明的共用体类型定义变量或常量
union demo d1;
money m1;
printf("%lu\n",sizeof(d1));
printf("%lu\n",sizeof(m1));
d1.i = 6513249;
printf("%c\n",d1.c); // a
printf("%hd\n",d1.s); // 25185
printf("%s\n",d1.a); // abc
d1.a[1] = 0;
printf("%c\n",d1.c); // a
printf("%hd\n",d1.s); // 97
printf("%s\n",d1.a); // a
printf("%d\n",d1.i); // 6488161
d1.l = 0x10064636261;
printf("%c\n",d1.c); // a
printf("%hd\n",d1.s); // 25185
printf("%s\n",d1.a); // abcd
printf("%x\n",d1.i); // 64636261
union demo* p1 = &d1;
printf("%hd\n",p1->s);
return 0;
}
枚举(Enum)
用来表示取值个数有限的数据,比如性别、学历等。
枚举类型其实就是整数类型,只不过使用一些更有意义的名字来代表整数值
枚举也是一种自定义数据类型,需要先声明再使用,使用 enum 关键字
枚举元素就是一个整数常量,在所有枚举类型中都不能同名
如果不显示指定枚举元素的具体值,第一个枚举元素的值就是 0,后面枚举元素的值就是它前面的枚举元素的值加一后的值,也支持我们显式指定枚举
enum sex_t
{
// 枚举元素
female,
male,
unknow
};
typedef enum
{
// 枚举元素
dazhuan,
benke,
yanjiusheng,
boshi
} degree;
int main()
{
// 使用上面声明的枚举类型定义变量或常量
enum sex_t s1 = male;
s1 = female;
s1 = unknow;
degree d1 = boshi;
d1 = benke;
if(s1 == male)
{
printf("男\n");
}
else if(s1 ==female)
{
printf("女\n");
}
else
{
printf("未知\n");
}
printf("%lu\n",sizeof(s1));
printf("%lu\n",sizeof(d1));
printf("%d\n",s1);
printf("%d\n",d1);
printf("%d\n",male);
printf("%d\n",boshi);
}
内存管理(Memory Management)
每个进程都拥有自己独立的私有地址空间,每个进程的地址空间都分为下面的区域:
- 栈区(Stack):可读可写,存放形参变量,非静态局部变量,函数返回地址等,系统自动管理分配和释放,我们无法干预。容量很小(通常只有若干 MB),不适合处理大量数据。
- 静态存储区:可读可写,存放静态局部变量、全局变量等,容量大,系统自动管理分配和释放,我们无法干预。
- 常量区:只读,存放常量数据,容量大,系统自动管理分配和释放,我们无法干预。
- 堆区:可读可写,容量大,由我们自己管理分配和释放,很灵活,可以实现动态内存管理。
- 代码段:只读,存放程序指令(即代码),系统自动管理,我们无法干预。
进程(Process):正在运行的程序
程序(Program):可执行文件,存放在硬盘、U 盘面等外存设备上
动态内存管理相关的标准 C 库函数:
- malloc:在堆区申请一块指定大小的内存空间,未初始化
- calloc:在堆区申请一块指定大小的内存空间,全部清零初始化
- realloc:调整一块已分配的堆区空间的大小(会保留原来的数据,调大时会尽可能进行原地扩充内存,如果尾部没有足够的空闲空间,就会直接申请一块新的空间,将旧空间的所有数据都拷贝过去,然后释放掉旧空间)
- free:释放一块已分配的堆区空间(PS:释放后就不要再访问那块堆区空间了)
malloc 函数如果执行成功,返回值为分配的堆区空间的首地址,否则返回 NULL。
calloc 函数内部首先调用 malloc 函数申请指定大小的空间
在各个进程频繁进行申请和释放后,堆区肯定会产生很多内存碎片
关于 main 函数返回值的意义,规范的做法是程序执行成功(正常结束)返回 0,执行失败(异常结束)返回非零值,操作系统或其他程序可以通过获取我们的程序的返回值判断是否执行成功
进程结束后,系统会释放它占用的所有资源,包括未释放的堆区空间等
#include <stdio.h>
#include <stdlib.h> // standard library
#include <string.h>
struct book
{
int isbn;
char name[51];
float price;
};
int main()
{
/*
// 栈溢出(Stack Overflow)崩溃
double nums[10000000] = {1,2,3,4};;
nums[1000] = 3.14;
printf("%g\n",nums[1000]);
*/
// 在堆区中申请空间存放10000000个浮点数
double* nums= malloc(10000000 * sizeof(double));
if(nums == NULL)
{
printf("malloc fail\n");
return 1;
}
int* i = malloc(sizeof(int));
if(i == NULL)
{
printf("malloc fail\n");
return 1;
}
*i = 3;
(*i)++;
printf("%d\n",*i);
free(i);
nums[1000] = 3.14;
printf("%g\n",nums[1000]);
free(nums); // 释放 nums 指向的堆区空间
/*
// 堆区释放了就不要再访问了
nums[3] = 5.8;
printf("%g\n",nums[3]);
*/
// 单个堆区结构体
struct book* b1 = malloc(sizeof(struct book));
if(b1 == NULL)
{
printf("malloc fail\n");
return 1;
}
b1->isbn = 10001;
strcpy(b1->name,"操作系统");
b1->price = 35.5;
//(*b1).price = 35.5;
printf("%d %s %g\n",b1->isbn,b1->name,b1->price);
free(b1);
// 堆区结构体数组
struct book* bs = malloc(1000 * sizeof(struct book));
if(bs == NULL)
{
printf("malloc fail\n");
return 1;
}
bs[3].isbn = 10001;
strcpy(bs[3].name,"操作系统");
(bs + 3)->price = 35.5;
printf("%d %s %g\n",bs[3].isbn,bs[3].name,(bs + 3)->price);
free(bs);
// calloc 的用法
// calloc 函数内部实现
/*
int* datas = malloc(100 * sizeof(int));
// 全部清零
char* p = (char*)datas;
for(int i = 0;i < 100 * sizeof(int);i++)
{
p[i] = 0;
}
*/
int* datas = calloc(100,sizeof(int));
if(datas == NULL)
{
;
}
printf("%d\n",datas[90]);
datas[3] = 100;
printf("%d\n",datas[3]);
free(datas);
// realloc 用法
char* s = malloc(5);
if(s == NULL)
{
;
}
//s = "qzp"; // 会导致内存泄漏
strcpy(s,"qzp");
s = realloc(s, 10);
printf("%s\n", s);
strcat(s,"666");
printf("%s\n",s);
free(s); // 段错误
return 0;
}
常用内存操作相关的标准 C 库函数(所有内存区域都可以使用):
- memset:memory set,将一块内存空间中的每个字节都设置为指定的值
- memcpy:memory copy,内存空间拷贝,源内存空间和目标内存空间不能存在重叠
- memmove:memory move,内存空间拷贝,源内存空间和目标内存空间可以存在重叠
- memcmp:memory compare,比较两块内存空间中的数据是否相同
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int i;
memset(&i, 0, sizeof(int));
printf("%d\n",i);
memset(&i, 1, sizeof(int));
printf("%d\n",i);
memset(&i, 0xFF, sizeof(int));
printf("%d\n",i);
// 栈区数组
int nums1[100] = {5,4,3,1,2};
//memset(nums1, 0, sizeof(nums1)); // 数组清零
// 堆区数组
int* nums2 = malloc(100 * sizeof(int));
memcpy(nums2,nums1,100 * sizeof(int));
for(int i = 0;i < 5;i++)
{
printf("%d ",nums2[i]);
}
printf("\n");
free(nums2);
char s[] = "hello,qzp";
memmove(s,s + 2,7); // llo,qzpzp
// memmove(s + 2,s,7); // hehello,q
printf("%s\n",s);
return 0;
}