【问题标题】:What happens when I write `char str[80];`?当我写 `char str[80];` 时会发生什么?
【发布时间】:2017-02-04 18:13:39
【问题描述】:

当我写:char str[80]; 时,幕后发生了什么?

我注意到我现在可以立即设置 str = "hello";str = "hello world";。第一次strlen(str)5,第二次是11

但是为什么呢?我认为在str = "hello"; 之后,索引5 处的字符变为空(str[5] 变为'\0')。这不是说str的大小现在是6,我应该不能设置为"hello world"

如果不是,那么strlensizeof 每次如何计算正确的值?

【问题讨论】:

  • strlen 不包括空字符。在任何情况下,将数据写入str 数组,不会影响您声明此数组的大小 (80)。
  • 是的,你做到了:“这不是说 str 的大小现在是 6 吗?”。
  • 我指的是尺寸,而不是长度
  • 嗯,简而言之(正如我在第一条评论中已经提到的)——将数据写入变量确实会影响该变量的大小。例如:int x = 5 不会将x 的大小从sizeof int 更改为sizeof char,尽管5 可以放入char
  • 你不能做str = "hello";。您不能分配给数组。

标签: c arrays string memory


【解决方案1】:

我认为您对两个不同的概念感到困惑:数组的分配长度(可用的总空间量)和数组的逻辑长度字符串(使用了多少空间)。

当您编写 char str[80] 时,您将获得 80 个字符的存储空间。您最终可能不会使用所有这些空间,但无论您尝试在其中存储什么字符串,您总会有 80 个可以放置字符的插槽。

如果将字符串"hello"存储到str中,那么str的前六个字符将被设置为hello和一个空终止字符。不过,这不会改变 分配的长度 - 您仍然可以使用 74 个其他插槽。如果您随后将其更改为"hello, world",您将使用额外的七个字符,这很合适,因为您很容易有足够的分配空间 来容纳东西。您刚刚更改了逻辑长度,即有多少空间用于有意义的数据,而不是分配的长度,即有多少可用空间。 p>

这样想。当您说char str[80] 时,您购买的就是一块80 英亩的土地。如果您随后将"hello" 放入其中,那么您将使用可用的 80 英亩中的 6 英亩。其余的土地仍然是你的——你可以在那里建造任何你想要的东西——所以如果你决定拆除所有东西并建造一个更长的绳子来占用更多英亩的土地,那很好。没有人会反对。

strlen 函数返回字符串的逻辑长度 - 您正在存储的字符串中有多少个字符。它的工作原理是对字符进行计数,直到找到一个指示字符串 logical 结尾的空终止符。 sizeof 运算符返回数组的分配长度,即你有多少个槽。它在编译时工作,并不关心数组内容是什么。

【讨论】:

  • 实际上strlen()返回字符串的终止NUL字符的索引值。请记住,C 中数组的索引从 0 开始,因此 strlen() 返回的值是尾随 NUL 字符之前使用的字节数
  • @user3629249 哦,当然。我在“存储的逻辑字符串中的字符数”和“用于对该字符串进行编码的字符数”之间进行了区分,尽管也许我没有做得最好。
【解决方案2】:

当您将变量声明为 char str[80] 时,会在堆栈上分配 80 个字符数组的空间。当该特定堆栈帧超出范围时,该内存将自动释放。

当您将它分配给字符串文字“hello”时,它会将每个字符复制到数组中,然后在字符串末尾放置一个空终止符(str[5] == '\0')。字符串长度和数组大小是两个不同的东西,这就是为什么你可以将它重新分配给“hello world”。字符串长度只是在空终止符之前有多少个连续字符。如果您改为将 str 声明为 char str[5],那么当您尝试将其重新分配给“hello world”时,确实会导致崩溃。查看 strlen 的简单实现可能会有所帮助:

size_t strlen(const char *str)
{
    size_t return_val = 0;
    while (str[return_val] != '\0') return_val++;
    return return_val;
}

当然,如果没有空终止符,上面的幼稚实现就会崩溃。

【讨论】:

  • “天真的”实现实际上是strlen 的正常实现方式——将非空终止字符串传递给它是未定义的行为。
  • 正如 Govind 所说,这是 C 语言的不安全部分之一。确保字符串正确终止是程序员的工作。
【解决方案3】:

我假设你在 C 中工作。当你编译“char str[80];”时基本上为您分配了一个 80 字符长的空间。 sizeof(str) 应该总是告诉你它是一个 80 字节长的内存块。 strlen(str) 将计算从 str[0] 开始的非零字符。这就是为什么“Hello”是 5 而“Hello world”。

我建议你学习使用 strnlen、strncpy、strncmp、snprintf 等函数,这样可以防止读取/写入超出数组末尾,例如:strnlen(str,sizeof(str)) .

还可以开始学习在线教程并找到一本介绍性的 C/C++ 书籍来学习。

【讨论】:

    【解决方案4】:

    当您声明像char str[80]; 80 chars 之类的数组时,堆栈上会为您保留空间,但它们并未初始化——它们会获取当时已在内存中的任何内容。作为程序员,初始化数组是你的工作。

    strlen 遵循以下原则:

    int strlen(char *s)
    {
         int len = 0;
         while(*s++) len++;
         return len;
    }
    

    换句话说,它返回一个以空字符结尾的字符串的长度in一个字符数组,即使长度小于整个数组的大小。

    sizeof 返回类型或表达式的大小。如果您的数组是 80 chars 长,而 char 是一个字节长,即使数组中的任何值都没有被初始化,它也会返回 80。如果您有一个包含 5 个 ints 的数组,而 int 的长度为 4 个字节,则 sizeof 将产生 20 个。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-17
      • 1970-01-01
      • 2014-03-31
      • 1970-01-01
      • 1970-01-01
      • 2016-11-11
      • 2010-09-15
      • 1970-01-01
      相关资源
      最近更新 更多