【问题标题】:How do strings and bits work in C?字符串和位如何在 C 中工作?
【发布时间】:2016-11-20 12:54:28
【问题描述】:

我有一个两部分的问题:

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

问题 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 需要一个指针,为什么不指定变量/数组的长度会导致指针指向整数错误?

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

我可能只是遗漏了一些关于位和字符串如何在 C 中工作的非常基本的/基础知识。

任何理解这一点的帮助将不胜感激。

【问题讨论】:

  • sizeof 返回一个 size_t,也就是 uintmax_t。阅读this 了解好标志。 C 中的字符串以 '\0' 结尾,所以 "a" == {'a', '\0'} 所以两个字符。 char my_string[5] = "hello"; 无效,您没有空间用于 '\0'。
  • 发布两个单独的问题。
  • @Stargateur:size_t 不是 uintmax_t 的同义词。例如,在许多 32 位平台上,size_t 是 32 位的,但 uintmax_t 可能表示 64 位整数 (unsigned long long)。
  • @dreamlax 不,uintmax_t 是实现的最大整数提供程序。所以在 32 位中,uintmax_t => uint32_t。 doc。 size_t 的目的是处理一个大小,因此是一个无符号整数。 uintmax_t 完美地代表了这一点。
  • char my_string[5] = "hello"; 可能会导致缓冲区溢出。正如@JohnBode 指出他的answer,让编译器弄清楚数组的大小应该是多少。

标签: c arrays string sizeof bits


【解决方案1】:

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

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

【讨论】:

  • 所以为了清楚起见,即使是字符串char x[2] = "a" 中的单个字符仍然会创建一个变量,该变量分配了一个指向底层数组的指针(x 分配了一个指向["a", "\0"] 的指针)?
  • 实际上在那里回答了我自己的问题(doh),当我意识到由于空终止符而需要一个数组时
【解决方案2】:

在 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 或一元& 运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则表达式 类型为“@987654332 @-element of T" 将被转换 ("decay") 为类型为 "pointer to T" 的表达式,表达式的值将是数组第一个元素的地址。因此,字符串文字 "hello" 是“char 的 6 元素数组”类型的表达式。如果我将该文字作为参数传递给像

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

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

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

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

char greeting[] = "hello"; 

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

当数组表达式是一元& 运算符的操作数时,会发生第三个异常。表达式&greeting 不是计算指向char (char **) 指针的指针,而是计算类型“指向char6 元素数组 的指针”或@987654353 @。

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


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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-05
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    • 2016-07-12
    • 2016-12-12
    • 2020-02-22
    • 2012-04-20
    相关资源
    最近更新 更多