c - 内存字长异常的 "char*"(Knuth 的 MIX 架构)

标签 c language-lawyer bit memory-address knuth

原文MIX architecture具有 6 位字节和内存寻址为 31 位字(5 个字节和一个符号位)。作为一个思考练习,我想知道 C 语言如何在这种环境中运行,给定:

  • char 至少有 8 位(C99 规范的附件 E)
  • C99 规范第 6.3.2.3 节(“指针”)第 8 段说“当指向对象的指针转换为指向字符类型的指针时,结果指向对象的最低地址字节。连续递增结果,直到对象的大小,产生指向对象剩余字节的指针。”我对此要求的解释是,它支持“memcpy(&dst_obj, &src_obj, sizeof(src_obj))”。

我能想到的方法:

  1. 使 char 成为 31 位,因此通过“char*”的间接寻址是简单的内存访问。但这会使字符串变得浪费(并且意味着它不符合 POSIX 标准,因为它显然需要 8 位字符)
  2. 将三个 8 位字符打包成一个字,忽略 7 位:“char*”可能由字地址和其中的字符索引组成。然而,这似乎违反了 6.3.2.3,即 memcpy() 必然会跳过忽略的位(这可能对真实对象类型有意义)
  3. 将字符完全打包成单词,例如第四个 8 位字符在字 0 中有 7 位,在字 1 中有一位。然而,这似乎要求所有对象的大小都为 8 位字符,例如无法声明“uint31_t”以匹配字长,因为这又存在 memcpy() 问题。

所以这似乎留下了使用 31 位字符的第一个(浪费)选项,所有对象的大小都是字符的倍数——我这样读是正确的吗?

最佳答案

我同意 MIX 架构上的 C 可能很难实现,尽管我本人不是语言律师,但在我看来,您指出您的方法 1. 是唯一符合标准的方法是正确的。

无论如何,字符串的空间浪费是您的问题中最少的:您可以通过求助于比 C 本身更早的解决方案来规避它:使每个 char 代表多个字母。例如,对于 MIX 架构,您可以设计一个 7 位编码并将 4 个字母打包到每个字符中:

char hi[4];
hi[0] = 'hell';
hi[1] = 'o, w';
hi[2] = 'orld';
hi[3] = '\0';

printf("%s", hi);

// Whoops, we forgot the exclamation mark
putchar('!\n');

这个实现看起来很奇怪,但是根据维基百科,it was used in the first "Hello world" program ever .我看了一下标准,发现没有什么可以阻止它,即使在 C11 中也是如此。特别是 § 6.4.4.4 允许以特定于实现的方式对文字字符和字符串进行编码。

编辑:

这无助于克服其他困难,主要的困难是您无法使用机器的大部分可能指令,因为您无法使用 native C 类型处理单个字节。但是,您可以通过这种方式使用位域:

typedef struct _bytes {
    unsigned int sign  : 1;
    unsigned int byte1 : 6; // EDIT: bitfields must be 
    unsigned int byte2 : 6; // declared as ints in standard C
    unsigned int byte3 : 6;
    unsigned int byte4 : 6;
    unsigned int byte5 : 6;
} bytes;

typedef union _native_type {
    char as_word;
    int as_int; // int = char; useful for standard library functions, etc.
    bytes as_bytes;
} native_type;

请注意,在 C++ 中,由于严格的别名规则中的一个子句,您必须小心始终在访问 int 和访问 int 之间访问 char 成员bytes,因为这个片段:

native_type a, b;
a.as_int = 0xC11BABE;
b.as_bytes.byte4 = a.as_bytes.byte4; // Whoops

会产生未定义的行为:参见 here了解详情。

关于c - 内存字长异常的 "char*"(Knuth 的 MIX 架构),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28851800/

相关文章:

c++ - 对标准库分配器指针类型的要求

c++ - move 的 vector 总是空的吗?

java - 为什么我的Key中的 '1'位越多,放到HashMap中的时间就越长?

c# - 将位数组转换为 uint 或类似的打包值

c - I/O系统调用

c - 我想扫描一些单词,并将它们放入字符串数组中,然后打印它。

c++ - 标准是否要求流构造函数不访问流缓冲区?

c - Mcrt1.o和Scrt1.o有什么用?

c - 这是做什么的?

c - C 指针地址的细微差别