003.空间处理
一、如何描述一个空间
如何访问一个空间?
空间访问分为有名访问和无名访问
有名访问:
通过定义变量,以变量名为名称进行访问(int a; char b; struct buffer data;)
变量定义在内存上,内存为了让CPU访问到,必须要编址
通过名字访问时,对CPU来说,变量名只是地址的一个代号
无名访问:
空间的本质:用一个地址进行编址,使CPU可以访问,与有名访问的区别只是没有通过名称去关联地址。
如何保存地址的值?
数字概念:足够存储数字的大小
物理含义:区别于数字,地址应该具有一定的物理含义
C语言如何用地址来描述一个空间?
一个地址要描述空间,需要满足以下几要素:
- 具备存储地址大小的容量,如32位系统(地址4 bytes = 32 bits)和64位系统(地址8 bytes = 64 bits)
- 地址从一个单位到下一个单位,该如何访问
C语言编译器如何识别地址的二要素?
int *p1;
char *p2;
存储空间大小由系统决定,32位系统4字节,64位系统8字节
对于int* p1,地址会以4个bits为单位移动,对于char* p2,地址会以1个bit为单位移动
代码验证
#include <stdio.h>
int main() {
char *p1;
int *p2;
printf("%d : %d\n", sizeof(p1), sizeof(p2));
printf("%p : %p\n", p1 + 1, p2 + 1);
return 0;
}
%p代表打印对应指针指向的地址,同时 p1 + 1 和 p2 + 1 并不是简单地代表地址加一,而是加一单位
例如p1对应char*,则加1字节,p2对应int*,则加4字节
二、C语言编译器如何识别变量的属性?
定位变量名、先右看、再左看
int a1;
int a2[5];
int *a3;
int *a4[5];//指针数组
int (*a5)[5];//数组指针
int a6[3][4];
int *a7[3][4];
int (*a8)[3][4];
多维空间存储
//设计一个指针,可以存储二维空间或三维空间首地址
#include <stdio.h>
int main() {
int a[5][3][4];
int (*k1)[3][4];
k1 = a;
printf("a = %p, a+1 = %p\n", a, a+1);
printf("k = %p, k+1 = %p\n", k1, k1+1);
return 0;
}
三、函数地址的保存
函数是空间,函数名就是这个空间地址的常量值的代号
如何用一个变量保存这个代号
#include <stdio.h>
int main() {
//int printf (const char *__format, ...)
int (*myshow)(const char *__format, ...);
myshow = printf;
myshow("hello world!\n");
return 0;
}
代码-事务执行
#include <stdio.h>
void do_music() {
printf("Music!\n");
}
void do_game() {
printf("Game!\n");
}
void do_book() {
printf("Book!\n");
}
int main() {
//定义一个数组空间,保存了key,每把钥匙都是函数行为
void (*events[3])(void);
//设置每天做的事情
events[0] = do_game;
events[1] = do_book;
events[2] = do_music;
//循环执行每天的事情
for(int i = 0; i < 3; i++) {
events[i]();
}
}
四、空间的属性
空间可以任意访问吗?
下面是一段演示:
#include <stdio.h>
int main() {
//char *s = "Aello";//Wrong
char s[] = "Aello";//Right
s[0] = 'H';
printf("the s is %s\n", s);
return 0;
}
这里的错误原因是"Aello"是字符串常量,被存储在内存的rodata区域,在程序运行时不允许修改。而s指向了这段字符串,并且试图修改,程序就会崩溃。
下面这一个正确的原因是,s[ ]在栈内申请了一块区域,并将"Aello"复制到这块区域中,栈内的数据是局部变量,可以修改,所以程序正常运行。
static段
不同的变量,默认定义在不同的段内
函数代码:代码段(.text段) r
字符串、常量值:只读数据段(.rodata段) r
全局的变量:数据段(.data段) rw
堆段:malloc(c++中为new)申请的,必须通过free(c++中为delete)释放 rw
栈段:函数的局部变量,函数返回后,出站释放 rw
static的变量:静态数据段(.data段) rw
static数据实际上就是全局数据,在任何地方都可以使用,但是要注意其生成位置,在static数据生成之前不可以使用此数据。
空间权限之边界访问
空间访问权限要考虑哪些?
- 读写权限
- 边界标志
下面是一段越界演示代码
#include <stdio.h>
int main() {
char buf[4];
int a = 0x12345678;
buf[0] = 0x11;
buf[1] = 0x22;
buf[2] = 0x33;
buf[3] = 0x44;
buf[4] = 0x99;//越界
printf("the a is %x\n", a);
return 0;
}
最后输出的a为12345699
我们惊讶地发现,buf的越界访问,竟然导致了a的改变
即使我们将a的类型改为const int,面对越界,a仍然会被改变
如何去定义边界呢?我们可以通过数量或特殊结束标志来定义边界,默认的结束标志:数字0 == '\0'
字符空间
字符串的结尾都会隐藏一个'\0',所以sizeof("12345") = 6,
字符串数组的初始化方法:char s[ ] = "xxxxxxxxxx"
字符空间的遍历写法:
#include <stdio.h>
#include <string.h>
int main() {
char *s1 = "12345";
char *temp;
temp = s1;
int i = 0;
int cnt = 0;
while (temp[i] != 0) {
cnt++;
i++;
}
printf("the cnt is %d\n", cnt);
return 0;
}
浙公网安备 33010602011771号