字符串和位如何在 C 中工作?

How do strings and bits work in C?

我有一个分为两部分的问题:

  1. 了解 sizeof
  2. 的输出
  3. 了解字符串如何存储在变量中(例如位和 ram)

问题 1

我正在尝试理解以下 C 代码的输出。

printf("a: %ld\n", sizeof("a")); // 2
printf("abc: %ld\n", sizeof("abc")); // 4

它似乎总是比实际指定的字符数大一。

文档建议返回值表示对象(在本例中为字符串)的大小(以字节为单位)。因此,如果 a 的大小返回 2 字节,那么我很好奇 a 是如何表示 16 位信息的。

如果我查看 ASCII 字符 a 的二进制表示,我可以看到它是 01100001。但这只显示了 1 个字节中的 3 位被使用。

问题 2

此外,大字符串如何存储到 C 中的变量中?我认为它们必须存储在数组中是否正确,如下所示:

char my_string[5] = "hello";

有趣的是,当我有一些代码时:

char my_string = "hello";
printf("my_string: %s\n", my_string);

我遇到两个编译器错误:

- incompatible pointer to integer conversion initializing 'char' with an expression of type 'char [6]'
- format specifies type 'char *' but the argument has type 'char'

...我不明白。首先,它声明当只有 5 个字符时,类型被假定为 [6] 的大小。其次,这里提到指针对我来说似乎很奇怪?为什么 printf 需要一个指针,为什么不指定 variable/array 的长度会导致指针指向整数错误?

顺便说一下,我似乎可以将 variable/array 的长度设置为 5 而不是 6,它会像我期望的那样工作 char my_string[5] = "hello";

我可能只是遗漏了一些非常 basic/fundamental 关于位和字符串在 C 中如何工作的东西。

如果能帮助理解这一点,我们将不胜感激。

问题的第一部分是由于字符串在C中的存储方式。C中的字符串只不过是一系列字符(char)加上一个[=11=]结束,这就是您在执行 sizeof 时看到 +1 的原因。请注意,在您的第二部分中,如果您要说 char my_string[4] = "hello";,您还会收到一个编译器错误,指出此字符串的大小不足。这也跟这个有关。

现在进入第二部分,字符串本身就是一系列字符。但是,您不会将每个字符单独存储在变量中。相反,您有一个指向这些字符系列的 指针 ,这将允许您从内存的某个部分访问它们。有关 C 中的指针和字符串的更多信息,请参见此处:Pointer to a String in C

在 C 中,字符串 是一个字符值序列,后跟一个零值终止符。例如,字符串 "hello" 是字符值序列 {'h', 'e', 'l', 'l', 'o', 0 }1。字符串(包括字符串文字) 存储 作为 char 的数组(或 wchar_t 用于宽字符串)。为了考虑终止符,数组的大小必须始终比字符串中的字符数大一:

char greeting[6] = "hello";

greeting 的存储空间类似于

          +---+
greeting: |'h'| greeting[0]
          +---+
          |'e'| greeting[1]
          +---+
          |'l'| greeting[2]
          +---+
          |'l'| greeting[3]
          +---+
          |'o'| greeting[4]
          +---+
          | 0 | greeting[5]
          +---+

字符串文字的存储基本相同2:

          +---+
 "hello": |'h'| "hello"[0]
          +---+
          |'e'| "hello"[1]
          +---+
          |'l'| "hello"[2]
          +---+
          |'l'| "hello"[3]
          +---+
          |'o'| "hello"[4]
          +---+
          | 0 | "hello"[5]
          +---+

是的,您可以将下标运算符 [] 应用于字符串文字,就像任何其他数组表达式一样。

除非它是 sizeof 或一元 & 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,表达式 类型的“N-元素 T” 将被转换 ("decay") 为类型 "pointer to T" 的表达式,表达式的值将是数组第一个元素的地址。因此,字符串文字 "hello" 是类型为“char 的 6 元素数组”的表达式。如果我将该文字作为参数传递给

这样的函数
printf( "%s\n", "hello" );

那么字符串文字 表达式 "%s""hello" 都是从“char 的 4 元素数组”转换而来的3 和“char 的 6 元素数组”到 "pointer to char",所以 printf 接收的是指针值,而不是数组值。

您已经看到转换规则的两个例外情况。当您使用 sizeof 运算符并获得比预期多 1 的值时,您会在代码中看到它。 sizeof 计算出存储操作数所需的字节数。由于零终止符,存储N个字符的字符串需要N+1个字节。

第二个例外是上面greeting数组的声明;因为我使用字符串文字来初始化数组,所以文字不会首先转换为指针值。请注意,您可以将该声明写为

char greeting[] = "hello"; 

在这种情况下,数组的大小取自初始化程序的大小。

第三个异常发生在数组表达式是一元&运算符的操作数时。表达式 &greeting 的计算结果不是指向指向 char (char **) 的指针,而是计算类型 "pointer to 6-element array of char" 或 char (*)[6].

字符串的长度是到零终止符之前的字符数。所有处理字符串的标准库函数都希望看到该终止符。存储该字符串的数组的大小 必须至少 比您要存储的字符串的最大长度大一倍。


  1. 有时您会看到人们写 '[=43=]' 而不是裸体的 0 来表示字符串终止符;他们的意思是一样的。
  2. 字符串文字的存储在程序启动时分配并一直保留到程序终止。字符串文字可以存储在只读内存段中;尝试修改字符串文字的内容会导致未定义的行为。
  3. '\n' 算作一个字符。