文章目录
1. 指针
1.1 交换两个数
int *p = &a;
将指针 p 指向 a;
#include <stdio.h>
void change(int *a, int *b){ // 使用指针交换数值
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a = 5;
int b = 3;
change(&a, &b);
printf("a = %d\nb = %d\n", a, b);
return 0;
}
2. gdb 工具的使用
2.1 安装 gdb 调试工具工具:
$ sudo apt-get install gdb
编译生成支持调试的 .out 文件
$ gcc -g main.c -o main.out # 在编译的时候,产生调试信息
2.2 gdb 的使用
使用命令:gdb main.out
或 gdb ./main.out
进行调试
$ gdb ./main2.out # 调试main2.out
(gdb) list # 查看源码
(gdb) l # 查看源码
(gdb) start # 开始调试 会列出当前所在行号
(gdb) p a # print the value of variable a
(gdb) n # next 单步执行(执行上一条命令,显示下一条命令)
(gdb) s # 到某个函数处使用 s 进入这个函数
(gdb) bt # backtrace 查看函数堆栈
#0 change (a=5, b=3) at main2.c:5
#1 0x000000000040116d in main (argv=1, argc=0x7fffffffcc48) at main2.c:13
(gdb) f 1 # 切换函数栈 1
(gdb) frame 0 # 切换到栈 0
(gdb) info stack # 查看堆栈的使用情况
(gdb) q # 退出调试
当使用指针进行调试时,进入函数后会出现地址:
![](https://i-blog.csdnimg.cn/blog_migrate/4526229873a8bb9052d7aa8bf993383f.png)
此时传入的是地址,两个地址 相差4个字节
(gdb) p a # 打印地址 a
(gdb) p *a # 打印 a 的内容
3. 内存
Q:32bits 的系统最大内存为4G?
A:32bits 的系统地址总线为32位,可使用的内存为 2 32 2^{32} 232 bits,最大内存为4G
Q:内存是如何分配的?
A:由操作系统统一来管理内存,操作系统调用函数
Q:为什么64位的系统中 gdb 打印的地址都是48位的?
A:受硬件处理器的限制,目前X86_64处理器的地址总线只有48位,一般默认64位地址的高16位为0,因此地址的实际范围为:0x00 00 00 00 00 00
到 0x 7f ff ff ff ff ff
OS 功能
-
对内存编号
-
对内存进行规划(如64位操作系统里面,将地址的低48位分配给用户使用,剩下的作为操作系统内核)
4. 操作系统对内存的管理
对下面代码进行调试:
#include <stdio.h>
int global = 0;
int rect(int a, int b){
static int count = 0;
count++;
global++;
int s = a * b;
return s;
}
int quadrate(int a){
static int count = 0;
count++;
global++;
int s = rect(a, a);
return s;
}
int main()
{
int a = 5;
int b = 3;
int* pa = &a;
int* pb = &b;
int *pglobal = &global;
int (*pquadrate)(int a) = &quadrate;
int s = quadrate(a);
return 0;
}
4.1 查看变量的地址:
(gdb) start
(gdb) p &a
(int *) 0x7fffffffcb28 # 4 bits;a 的首地址为 0x7fffffffcb28 (字节作单位)
(gdb) p &b
(int *) 0x7fffffffcb24 # 4 bits
(gdb) p &s
(int *) 0x7fffffffcb2c # 编译后进行了优化 (地址顺序和代码不一定一致)
(gdb) p &pquadrate
(int (**)(int)) 0x7fffffffcb30
(gdb) p &pa
(int **) 0x7fffffffcb48 # 64 bits
(gdb) p &pb
(int **) 0x7fffffffcb40 # 64 bits
(gdb) p &global
(int **) 0x7fffffffcb38
(gdb) p &rect
(int (*)(int, int)) 0x401102 <rect> # 先加载 (0x401139-0x401102=rect编译后的总长度)
(gdb) p &quadrate
(int (*)(int)) 0x401139 <quadrate> # 后加载
(gdb) p &global
(int *) 0x40402c <global> # 0x400402c 比代码段地址大
4.2 函数的调用:
// 当 main() 函数执行到:int s = quadrate(a);
输入 s 进入函数 quadrate():
// 当 quadrate() 函数执行到: int s = rect(a, a);
输入 s 进入函数 rect():
此时打印栈的使用情况:
得到下面的结果:
函数调用时,栈从上往下增长,main 函数在最外层,地址更高,内层的函数地址更低,最内层的函数最先返回 FILO
4.3 内存的分配
(gdb) bt
#0 rect (a=5, b=5) at storage.c:7
#1 0x0000000000401171 in quadrate (a=5) at storage.c:17
#2 0x00000000004011b9 in main () at storage.c:29
(gdb) f 0
(gdb) p &count
$8 = (int *) 0x404030 <count> # rect 中的静态变量 更小
(gdb) f 1
(gdb) p &count
$9 = (int *) 0x404034 <count> # quadrate 中的静态变量(quadrate 调用 rect)
内存的示意图如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/014460ac0e0864edda4099810d1034c6.png)
内存 | 存放内容 | 特点 |
---|---|---|
代码段 | 代码编译后生成的机器二进制码存放处(指令) | 地址向上增长 |
数据段 | 全局变量、常量、静态变量 | 向上增长 |
栈 | 存放变量、函数调用 | 地址接近 0x7fffffffffffffff,地址向下增长 |
内核 | 操作系统相关的内容,用户不得修改 | 地址高于 0x7fffffffffffffff |
在 gdb 中查看内存:
x/3d 0x7fffffffd114 # 从给定地址开始显示3个数,用 decimal 显示
5. 指针和数组
5.1 指针的运算
int array[5];
int* p = &array[0];
p += 3; // p 前进3格 指向 array[3]
p = array;
p[2] = 5; // *(p+2) = 5 即 array[2] = 5
5.2 字符数组
#include <stdio.h>
int main()
{
char str1[] = "hello"; // 栈内存分配时分配在
char *str2 = "world"; // "world" 存放在代码段,指针str2指向 "world"
char str3[10];
printf("input the value: ");
scanf("%s", str3);
printf("str1 is %s\n", str1);
printf("str2 is %s\n", str2);
printf("str3 is %s\n", str3); // str3是数组,也是地址
return 0;
}
调试上述代码:
$ gcc -g test.c -o test.out
$ gdb ./test.out
(gdb) p str1 # 数值
$1 = "hello"
(gdb) p str2 # 指针 存储在代码段
$2 = 0x402004 "world"
尝试 scanf("%s", str2)
回报错,原因是 str2 位于代码段,不能修改。
在 char str1[] = "hello";
之后加上一句 str1[3] = '\0'
后再打印str1,只得到了 “hel”
参考内容: