C语言 --- 内存管理&位运算&程序调试
(一)操作系统内存管理
1.内存分布

这是 32‑位程序总共 4GB 的虚拟内存分布图,地址由底部 0 向上至 4G。 最上方 1G 属于操作系统内核空间,我们用户代码不能操作。往下依次为栈区、堆区、数据区、文本区。
(1)栈区默认 8M:存放局部变量、函数参数,由系统自动申请释放;
(2)堆区:需要程序员使用malloc手动开辟、free手动释放;
(3)数据区分成三块:.data 存放初始化完毕的全局、静态变量;.bss 存放未初始化全局静态变量;字符串常量区存放双引号字符串;
(4)文本区:用来储存程序代码、字面常量。
简单总结:栈系统自动管理、堆程序员手动管控,全局变量放数据段,程序代码存放在文本段。
2.堆和栈的区别
(1)栈区:
- 栈区用来保存局部变量、函数的形参返回值、及函数调用时的保护现场
- 栈区由操作系统自动管理
- 栈区默认大小为8M, 比堆区空间小
- 栈的访问效率要比堆的访问效率高
(2)堆区:
- 堆区由开发人员手动申请和释放,使用完时及时释放,否则造成内存泄露。
- 堆区空间比栈空间大,访问效率相对较低。
1)堆区空间使用场景
-
在函数中开辟的空间,希望随着函数结束,该空间任然存在,可以去堆上开辟。
-
当需要的空间比较大时,可以去堆上开辟,栈区空间有限,默认8M。
2)堆区空间使用(动态内存分配)
void *malloc(size_t size);
功能:向堆区申请内存空间
参数:
size:申请的空间字节数
返回值:
成功:返回申请到的空间首地址
失败:NULL
void free(void *ptr);
功能:释放申请的堆区空间
参数:
ptr:申请到的堆区的空间首地址。
void *calloc(size_t nmemb, size_t size);
功能:向堆区申请数组空间,并清空成0
参数:
nmemb:申请的数组的元素个数
size:数组中的每个元素占的字节数
返回值:
成功:返回申请到的空间首地址
失败:NULL
示例:
int *p = malloc(4);
if (NULL == p)
{
printf("malloc error\n");
return 0;
}
*p = 10;
printf("*p = %d\n", *p);
free(p);
struct stu *ps = malloc(sizeof(struct stu));
if (NULL == ps)
{
printf("malloc error\n");
return 0;
}
//ps->name = "zhangsan"; 错
strcpy(ps->name, "zhangsan");
ps->age = 10;
ps->score = 100;
printf("%s %d %d\n", ps->name, ps->age, ps->score);
free(ps);
char *pstr = malloc(100);
if (NULL == pstr)
{
printf("malloc error\n");
return 0;
}
strcpy(pstr, "hello world");
printf("pstr->%s\n", pstr);
free(pstr);
3)内存泄露
- 内存泄漏:程序运行过程中,堆区申请的空间在使用完没有释放,则造成内存泄露。
- 避免内存泄露:堆区申请的空间使用完,及时使用free()进行释放。
动态数组:
int *pa = malloc(ARRAY_LEN*sizeof(int));
if (NULL == pa)
{
printf("malloc error\n");
return 0;
}
for (int i = 0; i < ARRAY_LEN; ++i)
{
pa[i] = i;
}
for (int i = 0; i < ARRAY_LEN; ++i)
{
printf("%d ", pa[i]);
}
printf("\n");
(二)位运算
1.位运算符
& 按位与 全1为1,遇0则0 (&& 逻辑与)
| 按位或 全0为0,遇1则1
^ 按位异或 同0异1
~ 按位取反 1->0,0->1 单目运算符
<< 按位左移
>> 按位右移
示例:
15 & 3 = 3
15 | 3 = 15
15 ^ 3 = 12
~ 15 = -16 (~n = -(n+1))
<< 按位左移,低位补0 (num<<n <===> num*2^n)
>> 按位右移,有符号数高位补符号位,无符号数高位补0 (num>>n <===> num/2^n)过程:
15: 0000 0000 0000 0000 0000 0000 0000 1111
3: 0000 0000 0000 0000 0000 0000 0000 0011&: 0000 0000 0000 0000 0000 0000 0000 0011
|: 0000 0000 0000 0000 0000 0000 0000 1111
^: 0000 0000 0000 0000 0000 0000 0000 1100
~15: 1111 1111 1111 1111 1111 1111 1111 0000(负数补码)只看后四位
减1:1110 1111
取反:1001 0000
9: 0000 0000 0000 0000 0000 0000 0000 1001
9<<3: 0000 0000 0000 0000 0000 0000 0100 1000 -->72==>9*2^3
9>>3: 000 0000 0000 0000 0000 0000 0000 0000 1 -->1
2.位运算的使用
1)指定第n位置1
num = num | 1 << n;
0000 0000 ---> 0000 1000如下:
0000 0001 << 3 --> 0000 1000
1010 1000 | 0000 1000 ==> 1010 1000
2)指定第n位清0
num = num & ~(1 << n);
1010 1010 ---> 1010 0010
1010 1010 & 1111 0111
1111 0111 <==> ~0000 1000 <==> ~(1 << 3)
例:
0011 1100 1010 1010 对第10、11位清0
num & ~(1<<10) & ~(1<<11)
注意:这个n是从0开始数
3)两数交换
a ^ a == 0
a ^ 0 == a
a = a ^ b;
b = a ^ b;
a = a ^ b;
过程:
b = (a ^ b) ^ b = a ^ b ^ b = a ^ (b ^ B) = a ^ 0 = a;
a = a ^ b = a ^ b ^ a = 0 ^ b = b;
中间变量:
int a,b,tmp = 0;
tmp = a;
a = b;
b = tmp;
引申:两数交换(求和方式):
a = a + b;
b = a - b; =====>a+b-b = a
a = a - b; =====>a+b-b = a+b-a
3.运算符的优先级
高--->低: ~ << >> & ^ |
(三)程序调试方法
1.打印调试
程序运行过程中打印输出:
num;
printf("num = %d\n",num);
2.gdb调试工具
gnu组织提供的Linux下的代码调试工具
- 使用gdb单步调试逻辑错误
- 使用gdb调试段错误
r全速运行,where 定位段错误的位置
- 使用流程:
(1)编译程序:-g选项:允许使用gdb进行调试
gcc main.c -g
(2)使用gdb调试程序
gdb ./可执行程序 即 gdb ./a.out
(3)gdb指令
l 列出源代码
b 行号 在指定行打断点
r 运行程序
p 变量名 查看变量的值
n 执行下一行代码
q 退出gdb quit
s 跟进一个函数
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)