计算机相关 · 2025年8月30日 0

我理解的类型

学完汇编以后,对类型有了一些新的理解。

类型系统的本质是如何解读内存中的一段数据。就像同样是0xFF,它到底是白色还是256还是-127,还是某个ASCII字符或者某个小数,取决于我们如何去解读它。

一个例子可以很好的说明这一点:

#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

int main() {
	/* 机器码数组(包含可执行代码和数据) */
	unsigned char machine_code[] = {
		// 函数入口点
		0x48, 0x8d, 0x35, 0x12, 0x00, 0x00, 0x00, // lea    rsi, [rip + 0xd] 
		0xb8, 0x01, 0x00, 0x00, 0x00,             // mov    eax, 0x1          
		0xbf, 0x01, 0x00, 0x00, 0x00,             // mov    edi, 0x1          
		0xba, 0x0d, 0x00, 0x00, 0x00,             // mov    edx, 0xc          
		0x0f, 0x05,                               // syscall                  
		0xc3,                                     // ret                    

		// 内嵌字符串数据 (与代码位于同一内存区域)
		'H','e','l','l','o',' ','w','o','r','l','d','!', '\n' ,'\0'
	};

	/* 分配可执行内存 (关键步骤) */
	void *exec_mem = mmap(
		NULL,                           // 由内核选择地址
		sizeof(machine_code),           // 分配大小
		PROT_READ | PROT_WRITE | PROT_EXEC, // 可读+可写+可执行权限
		MAP_PRIVATE | MAP_ANONYMOUS,    // 匿名映射(不关联文件)
		-1,                             // 文件描述符(不使用)
		0                               // 偏移量
	);

	if (exec_mem == MAP_FAILED) {
		perror("mmap failed");
		return 1;
	}

	/* 复制机器码到可执行内存区域 */
	memcpy(exec_mem, machine_code, sizeof(machine_code));

	/* 将内存地址转换为函数指针 */
	void (*print_func)() = (void (*)())exec_mem;

	/* 执行机器码函数 */
	printf("即将执行机器码函数...\n");
	print_func();  // 调用机器码函数
	printf("已从机器码函数返回main函数\n");

	/* 清理资源 */
	munmap(exec_mem, sizeof(machine_code));
	return 0;
}

这段代码在Linux上运行,打印“Hello world!”。一个数组的内容是可执行的机器码,那么它被赋予执行权限后就能被当成函数。

因此,某种意义上,类型不重要,重要的是我们认为它是个什么类型,或者说它本身是个什么类型。“它本身是个什么类型”也只是它如果不是这个类型,语义就会出现问题,只要我们捏着鼻子认了新的语义,那重要的就是我们认为它是个啥类型了。