003

003.空间处理

一、如何描述一个空间

如何访问一个空间?

空间访问分为有名访问无名访问

有名访问:

通过定义变量,以变量名为名称进行访问(int a; char b; struct buffer data;)

变量定义在内存上,内存为了让CPU访问到,必须要编址

通过名字访问时,对CPU来说,变量名只是地址的一个代号

无名访问:

空间的本质:用一个地址进行编址,使CPU可以访问,与有名访问的区别只是没有通过名称去关联地址。

如何保存地址的值?

数字概念:足够存储数字的大小

物理含义:区别于数字,地址应该具有一定的物理含义

C语言如何用地址来描述一个空间?

一个地址要描述空间,需要满足以下几要素:

  1. 具备存储地址大小的容量,如32位系统(地址4 bytes = 32 bits)和64位系统(地址8 bytes = 64 bits)
  2. 地址从一个单位到下一个单位,该如何访问

C语言编译器如何识别地址的二要素?

int *p1;

char *p2;

存储空间大小由系统决定,32位系统4字节,64位系统8字节

对于int* p1,地址会以4个bits为单位移动,对于char* p2,地址会以1个bit为单位移动

image-20251002150845611

代码验证

#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"复制到这块区域中,栈内的数据是局部变量,可以修改,所以程序正常运行。

image-20251002175345973

static段

不同的变量,默认定义在不同的段内

函数代码:代码段(.text段) r

字符串、常量值:只读数据段(.rodata段) r

全局的变量:数据段(.data段) rw

堆段:malloc(c++中为new)申请的,必须通过free(c++中为delete)释放 rw

栈段:函数的局部变量,函数返回后,出站释放 rw

static的变量:静态数据段(.data段) rw

static数据实际上就是全局数据,在任何地方都可以使用,但是要注意其生成位置,在static数据生成之前不可以使用此数据。

空间权限之边界访问

空间访问权限要考虑哪些?

  1. 读写权限
  2. 边界标志

下面是一段越界演示代码

#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的改变

image-20251002184055915

即使我们将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;
}

posted on 2025-10-04 14:17  Khalilll  阅读(21)  评论(0)    收藏  举报