再学C语言,复习&笔记。


系列顺序:
Linux C语言编程基本原理与实践
Linux C语言指针与内存
Linux C语言结构体

更多Linux知识:
一天一点linux


gdb调试工具的使用

gdb -help 查看gdb用法
gcc -g main.c -o main.out 编译命令
gdb ./main.out 调试命令
l (list)列出代码(按回车显示全部)
break + 行号 打断点
start 开始单步调试
n 执行下一行
p a 打印a变量在内存中的情况
s 进入调用函数内部
bt 查看函数堆栈
f 1 表示切换函数栈(切换到编号为1的函数)
q 退出调试

demo

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

void change(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}

int main()
{
int a = 5;
int b = 3;
change(&a, &b);
printf("a = %d\n, b = %d\n", a, b);
return 0;
}

main2.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

void change(int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}

int main()
{
int a = 5;
int b = 3;
change(a, b);
printf("a = %d\n, b = %d\n", a, b);
return 0;
}

调试main2.c:
https://github.com/hubojing/BlogImages/blob/master/Linux%20C%E8%AF%AD%E8%A8%80%E6%8C%87%E9%92%88%E4%B8%8E%E5%86%85%E5%AD%98%E2%80%94%E2%80%94%E8%B0%83%E8%AF%95main2.c.png?raw=true

注意change函数传入的是a=5,b=3这两个数值。

调试main.c:
https://github.com/hubojing/BlogImages/blob/master/Linux%20C%E8%AF%AD%E8%A8%80%E6%8C%87%E9%92%88%E4%B8%8E%E5%86%85%E5%AD%98%E2%80%94%E2%80%94%E8%B0%83%E8%AF%95main.c.png?raw=true

注意change函数传入的是两个地址。

指针与内存

32位主机——32位总线(2^32个字节)最多只能放4G内存条。
2^10*2^10*2^10^2^2
=1024*1024*1024*4字节
=1024*1024*4K
=1024*4M
=4G

2^30=G
2^40=T


内存分配

系统内核(48位以上)
栈(暂时存储首先执行的程序状态)
自由可分配内存(可动态分配内存)

数据段(声明一些全局变量或者声明一些常量)
代码段(程序源代码编译后存放在此)


C语言语法是不允许直接操作代码段的。
函数调用信息保存在栈(stack)中。

变量的本质是什么?
变量名它只是一个代号,变量的本质就是内存。
指针保存的是什么?
指针保存的就是内存地址。

int *pa=&a
指将a的地址传给pa。
p pa看到的是a的地址(&a)。
p &pa才能看到pa本身的地址。

GCC会对内存变量分配进行优化:同一种变量类型会放到一块儿。
静态变量、常量、全局变量存于内存的数据段(数据段位置高于代码段)。
栈区域(最接近系统控制的内存空间)保存的是函数的运行状态:函数执行到哪一个命令?每一个变量保存的什么值?等等。

地址分配:代码,数据段是从下往上分配(先低地址,后高地址),即:先声明的函数地址小,后声明的函数地址大;栈是从上往下分配(先高地址,后低地址)

函数中静态变量、局部变量区别:
局部变量在栈(相对数据段而言的高地址)中,而静态变量在数据段(低地址)中。
所以在多次调用函数时,静态变量不会被重新初始化。或者这么说,静态变量的生存周期和数据段相同,局部变量生存时间受调用函数时,所属函数进栈出栈的影响而会重新初始化。
全局变量和静态变量都在数据段中,但静态变量是某个函数特有的。

函数指针

函数指针格式:
返回值类型 (*变量名)(形参列表);
p pa指找到数据的内存地址(a的地址);
p *pa指找到pa中的数据;
p &pa指找到pa本身的地址。
quadrate在代码段时,
p quadrate 函数的内存地址(quadrate )
p *quadrate 在地址前加*,表示通过地址取值
int quadrate(int a);是一个函数
int (*pquadrate)(int a)=&quadrate;则是指向这个函数的指针
int s=(*pquadrate)(a)可以调用函数

字符串与数组

x/3d 地址 gdb调试打印内存:x代表打印,3代表打印长度,d代表十进制输出
例: x/3d 0x7fffffffde14
x/6cb c:字符形式 :b byte为单位

数组是指针常量,指针是指针变量。
数组当作内存地址输入的话,可以直接当作参数传进去,不用加&取址符。

栈内存中,gcc编译会作内存优化:

  1. 使同一数据结构的变量在一起;
  2. 先定义的变量内存地址会小于后定义的;
  3. 只有堆内存和栈内存可以写入数据,代码段的内存编译以后无法修改。
    例:
    char *str=”world”;
    scanf(“%s”, str);
    将报错。因为”world”是一个常量,在代码段中,不可更改。
    char str[]=”hello”;
    scanf(“%s”, str);
    正确。str在栈或堆中,可修改。