一个char数组是如何存储的?

3

我发现了一些奇怪的事情:

当我有一个三个元素的char* s,并将其赋值为“21”时,

  1. s的打印short int值似乎为12594,即二进制的0010001 0010010,分别为49和50个字符。但根据ASCII表,“2”的值为50,“1”的值为49。

  2. 当我将char向右移动时,*(short*)s >>= 8,结果与(1.)一致,即为‘1’或49。但是,在我将char *s = '1'赋值后,s的打印字符串也显示为“1”,这让我之前认为它会变成“11”感到困惑。

我对char中存储的位数感到困惑,希望有人能解释一下。

以下是我使用的代码:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  printf("%lu,%lu\n",sizeof(char), sizeof(short));
  char* s = malloc(sizeof(char)*3);
  *s = '2', *(s+1) = '1', *(s+2) = '\0';
  printf("%s\n",s);
  printf("%d\n",*(short int*)s);
  *(short*)s >>= 8;
  printf("%s\n",s);
  printf("%d\n",*(short int*)s);
  *s = '1';
  printf("%s\n",s);
  return 0;
}

输出结果为:

1,2
21
12594
1
49
1

这个程序是在macOS上使用gcc编译的。


1
你是否了解“字节序”? - Yunnosch
不,什么是“字节序”?@Yunnosch - Yi Lin Liu
2
或者是“小端”和“大端”? - Yunnosch
1
什么是严格别名规则? - Lundin
1
是的。定义你想要检测的字节序类型。研究它如何可见并进行测试。你可以在一个函数中完成,并返回一个布尔值或枚举类型“little”或“big”。你所展示的代码实际上就是这样一个测试。如果得到了期望的结果,就返回一个字节序;否则返回另一个。然而,这样做而不冒着未定义行为的风险更具挑战性。也许社区有一些想法。但你应该先提出一个单独的问题,并在此之前搜索重复内容。 - Yunnosch
显示剩余5条评论
2个回答

4
你需要了解"字节序"的概念,即数值可以表示为"小端序"和"大端序"。我将略过关于合法性的讨论以及涉及未定义行为的问题。
以下是相关链接:What is the strict aliasing rule?
假设有一对内存中的字节,低地址的字节包含50,高地址的字节包含49:
`50 49`
你可以通过显式设置低字节和高字节(通过char类型)来引入它们。
然后,你读取它们,强制编译器将其视为short类型,在你的系统上,short是两个字节大小的类型。
编译器和硬件可以采用不同的方式来表示连续两个字节中的两个字节值。这就是所谓的"字节序"。
两个标准符合的编译器可能会像这样处理:
要返回的short:
- 取自较低地址的值,乘以256,加上较高地址的值
- 取自较高地址的值,乘以256,加上较低地址的值
它们没有真正这样做,这是硬件上实现的更有效的机制,但重点是即使在硬件上的实现也会隐式地执行这样或那样的操作。

1
您正在通过别名类型重新解释表示方式,这是标准不允许的:您可以将short值处理为char数组,但不能反过来。这样做可能会导致奇怪的错误,优化编译器可能会假设该值从未被初始化,或者可能会优化掉包含未定义行为的完整代码分支。
然后,您问题的答案称为字节序。在大端表示法中,最高有效字节具有最低地址(258或0x102将按照顺序0x01、0x02表示为2个字节),而在小端表示法中,最低有效字节具有最低地址(0x102按照顺序0x02、0x01表示)。
您的系统恰好是小端序。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接