我想知道违反我在下面列出的假设的架构。
我看到 Stephen C 提到了 PERQ 机器,MSalters 提到了 68000 和 PIC。
令我失望的是,没有其他人通过命名具有不符合某些毫无根据的假设的符合标准的 C 编译器的任何奇怪而美妙的架构来真正回答这个问题。
sizeof(int *) == sizeof(char *) == sizeof(void *) == sizeof(func_ptr *) ?
不必要。一些例子:
大多数哈佛架构 8 位处理器的编译器——PIC 和 8051 和 M8C——使 sizeof(int *) == sizeof(char *),但不同于 sizeof(func_ptr *)。
这些系列中的一些非常小的芯片具有 256 字节的 RAM(或更少)但有几千字节的 PROGMEM(闪存或 ROM),因此编译器通常使 sizeof(int *) == sizeof(char *) 等于 1(a单个 8 位字节),但 sizeof(func_ptr *) 等于 2(两个 8 位字节)。
对于那些拥有几千字节 RAM 和大约 128 千字节 PROGMEM 的系列中的许多较大芯片的编译器,使 sizeof(int *) == sizeof(char *) 等于 2(两个 8 位字节),但 sizeof( func_ptr *) 等于 3(三个 8 位字节)。
一些哈佛架构芯片可以完全存储 2^16(“64KByte”)的 PROGMEM(闪存或 ROM),以及另外 2^16(“64KByte”)的 RAM + 内存映射 I/O。这种芯片的编译器使 sizeof(func_ptr *) 始终为 2(两个字节);但通常有办法将其他类型的指针 sizeof(int *) == sizeof(char *) == sizeof(void *) 变成一个“long ptr”的 3 字节通用指针它有一个额外的魔法位,指示该指针是指向 RAM 还是 PROGMEM。(当您从许多不同的子例程调用该函数时,您需要将这种指针传递给“print_text_to_the_LCD()”函数,有时使用缓冲区中可能位于 RAM 中任何位置的变量字符串的地址,有时使用一个可以在 PROGMEM 中的任何位置的许多常量字符串)。此类编译器通常具有特殊的关键字(“short”或“near”、“long”或“far”),以让程序员在同一程序中专门指示三种不同类型的 char 指针——常量字符串只需要 2 个字节来指示在哪里在 PROGMEM 中,它们位于非常量字符串,只需要 2 个字节来指示它们在 RAM 中的位置,
1950 年代和 1960 年代建造的大多数计算机使用36 位字长或18 位字长,以及 18 位(或更少)地址总线。我听说此类计算机的 C 编译器通常使用9 位字节,其中 sizeof(int *) == sizeof(func_ptr *) = 2 给出 18 位,因为所有整数和函数都必须是字对齐的;但是 sizeof(char *) == sizeof(void *) == 4 以利用特殊的 PDP-10 指令将此类指针存储在完整的 36 位字中。完整的 36 位字包括一个 18 位字地址,以及其他 18 位中的更多位(除其他外)指示该字中指向字符的位位置。
无论指向的数据类型如何,给定架构的所有指针的内存表示都是相同的?
不必要。一些例子:
在我上面提到的任何一种架构中,指针都有不同的大小。那么他们怎么可能有“相同的”表示呢?
某些系统上的某些编译器使用“描述符”来实现字符指针和其他类型的指针。这样的描述符对于指向 " " 中的第一个 "char" 的指针与指向 " " 中的第一个 "char" 的指针是不同的,它们可以说是不同的数据类型,即使小数组恰好开始在之前被大数组占用的内存中完全相同的位置。描述符允许此类机器捕获和捕获在其他机器上导致此类问题的缓冲区溢出。char big_array[4000]
char small_array[10]
SAFElite 和类似的“软处理器”中使用的“Low-Fat Pointers”具有关于指针指向的缓冲区大小的类似“额外信息”。Low-Fat 指针具有捕获和捕获缓冲区溢出的相同优势。
指针的内存表示是否与架构相同位长的整数相同?
不必要。一些例子:
在“标记架构”机器中,内存的每个字都有一些位来指示该字是整数、指针还是其他东西。对于这样的机器,查看标记位会告诉您该单词是整数还是指针。
我听说 Nova 小型机在每个单词中都有一个“间接位”,它激发了“间接线程代码”。听起来像存储整数会清除该位,而存储指针会设置该位。
只有编译器禁止指针数据类型的乘法和除法。注意:是的,我知道这是荒谬的。我的意思是 - 是否有硬件支持来禁止这种不正确的使用?
是的,某些硬件不直接支持此类操作。
正如其他人已经提到的,68000 和 6809 中的“乘法”指令仅适用于(某些)“数据寄存器”;它们不能直接应用于“地址寄存器”中的值。(编译器很容易绕过这些限制——将这些值从地址寄存器移动到适当的数据寄存器,然后使用 MUL)。
所有指针值都可以转换为单一数据类型吗?
是的。
为了让memcpy() 正常工作,C 标准要求每种类型的每个指针值都可以转换为 void 指针(“void *”)。
需要编译器来完成这项工作,即使对于仍然使用段和偏移量的架构也是如此。
所有指针值都可以转换为单个整数吗?换句话说,哪些架构仍然使用段和偏移量?
我不知道。
我怀疑所有指针值都可以转换为“”中定义的“size_t”和“ptrdiff_t”整数数据类型<stddef.h>
。
递增指针相当于将 sizeof(指向的数据类型)添加到指针存储的内存地址。如果 p 是 int32*,则 p+1 等于 p 之后 4 个字节的内存地址。
目前还不清楚你在这里问什么。
问:如果我有一个某种结构或原始数据类型的数组(例如,“ #include <stdint.h> ... int32_t example_array[1000]; ...
”),并且我增加了一个指向该数组的指针(例如,“int32_t p = &example_array[99]; .. . p++; ..."),指针现在是否指向该数组的下一个连续成员,即 sizeof(指向的数据类型) 字节在内存中更远的位置?
答:是的,编译器必须使指针在递增一次后,指向数组中下一个独立连续的 int32_t,sizeof(指向的数据类型)字节在内存中更远,以符合标准。
问:那么,如果 p 是 int32* ,那么 p+1 等于 p 之后 4 个字节的内存地址?
答:当 sizeof(int32_t) 实际上等于 4 时,是的。否则,例如对于某些字可寻址机器,包括一些现代 DSP,其中 sizeof(int32_t) 可能等于 2 甚至 1,则 p+1 等于内存地址 2 甚至 p 之后的 1 个“C 字节”。
问:因此,如果我将指针转换为“int”...
A:“全世界都是 VAX 异端”的一种。
问:......然后将“int”转换回指针......
A:另一种类型的“全世界都是 VAX 异端”。
问:因此,如果我将指针 p(指向 int32_t 的指针)转换为某个足够大以包含指针的整数类型,然后添加sizeof( int32_t )
到该整数类型,然后再将该整数类型转换回变成一个指针——当我做所有这些时,结果指针等于 p+1?
不必要。
许多 DSP 和其他一些现代芯片具有面向字的寻址,而不是 8 位芯片使用的面向字节的处理。
一些用于此类芯片的 C 编译器将 2 个字符塞入每个单词,但需要 2 个这样的单词来保存 int32_t ——所以他们报告说sizeof( int32_t )
是 4。(我听说有一个24 位的 C 编译器这样做的摩托罗拉 56000)。
编译器需要安排一些事情,以便使用指向 int32_t 的指针执行“p++”会使指向下一个 int32_t 值的指针递增。编译器有几种方法可以做到这一点。
一种符合标准的方法是将每个指向 int32_t 的指针存储为“本机字地址”。因为保存一个 int32_t 值需要 2 个字,所以 C 编译器将“ int32_t * p; ... p++
”编译成某种汇编语言,该语言将该指针值增加 2。另一方面,如果该编译器使用“ int32_t * p; ... int x = (int)p; x += sizeof( int32_t ); p = (int32_t *)x;
”,则 56000 的 C 编译器很可能将其编译为将指针值增加 4 的汇编语言。
我最习惯于在连续的虚拟内存空间中使用指针。
一些 PIC 和 8086 以及其他系统具有不连续的 RAM——地址上的一些 RAM 块“使硬件更简单”。内存映射 I/O 或根本没有附加到这些块之间的地址空间间隙。
这比听起来更尴尬。
在某些情况下——例如使用位绑定硬件来避免由读-修改-写引起的问题——可以使用 2 个或更多不同的地址读取或写入 RAM 中完全相同的位。