【问题标题】:C Arbitrary length stringC 任意长度字符串
【发布时间】:2020-05-05 13:26:43
【问题描述】:

我怀疑数组的长度是如何分配的

#include <stdio.h>
#include <string.h>

int main()
{
    char str[] = "s";
    long unsigned a = strlen(str);
    scanf("%s", str);
    printf("%s\n%lu\n", str, a);
    return 0;
}

在上面的程序中,我将字符串"s" 分配给char 数组。 我认为str[] 的长度是1。所以我们不能存储超过数组的长度。但它的行为不同。如果我使用scanf 读取一个字符串,它将存储在str[] 中,没有任何错误。数组str 的长度是多少?

示例 I/O:

Hello

Hello 1

【问题讨论】:

  • 这是未定义的行为,可能会导致难以追踪的错误,所以不要越界。
  • 今天可能没有错误,但明天会有。如果你打破了速度限制,你不一定会造成事故。只有当你撞到东西时。在这里,你的越界字符串没有命中任何东西。
  • 如果您有未定义的行为,则崩溃是第二好的情况行为。最好的情况是编译器错误/警告,但在许多情况下,C 编译器不会为此类事情提供任何诊断。如果您在缓冲区末尾之外写入可能会发生更糟糕的事情:任意代码执行,即代码中存在严重的安全漏洞,数据完整性受损,数据机密性......
  • 关于:scanf("%s", str); 1) 始终检查返回值(不是参数值)以确保操作成功。注意:scanf() 系列函数返回成功的input format conversion 说明符(或 EOF)的数量 在当前场景中,任何返回值不是 1 都表示发生了错误。 2) (注意缓冲区str[] 的长度为 2)当使用说明符时:%s 和/或%[...] 总是包含一个比输入缓冲区长度小 1 的 MAX CHARACTERS 修饰符,因为这些说明符总是附加一个 NUL 字节。 (续)
  • (cont) 这也避免了缓冲区溢出的任何可能性以及由此产生的未定义行为

标签: c arrays string


【解决方案1】:

你的str 是一个char 的数组,用"s" 初始化,也就是说,它的大小为2,长度为1。大小比长度大一,因为在末尾添加了一个 NUL 字符串终止符 (\0)。

您的str 数组可以最多容纳两个char。尝试写入更多内容会导致您的程序访问超出数组末尾的内存,这是未定义的行为。

但实际发生的情况是,由于str 数组存储在内存中的某个位置(在堆栈上),并且该内存区域远大于 2 个字节,因此您实际上可以写到末尾而不会导致碰撞。这确实不意味着您应该这样做。它仍然是未定义的行为。

由于您的数组大小为2,它只能容纳长度为1 的字符串,加上它的终止符。要使用 scanf() 并正确避免写入超出数组末尾,您可以使用字段宽度说明符:% 之后和 s 之前的数值,如下所示:

scanf("%1s", str);

【讨论】:

    【解决方案2】:

    当数组的大小由使用的初始化器确定时,声明数组时未指定其大小。

    在这个数组的声明中

    char str[] = "s";
    

    使用字符串文字作为初始值设定项。字符串文字是由包含的零终止字符终止的字符序列。那就是字符串文字"s"有两个字符{ 's', '\0' }。

    它的字符用于顺序初始化数组str的元素。

    所以如果你会写

    printf( "sizeof( str ) = %zu\n", sizeof( str ) );
    

    那么输出将是2。字符串的长度确定为终止零字符之前的字符数。所以如果你会写

    #include <string.h>
    //...
    printf( "strlen( str ) = %zu\n", strlen( str ) );
    

    那么输出将是1。

    如果您尝试在数组外部写入数据,那么您将获得未定义的行为,因为不属于该数组的内存将被覆盖。在某些情况下,您可以获得预期的结果。在其他情况下,程序可能会异常完成。那就是程序的行为是未定义的。

    【讨论】:

      【解决方案3】:

      数组str 的大小为2:1 个字节用于字符's',1 个字节用于终止空字节。你正在做的是写到数组的末尾。这样做会调用undefined behavior。

      当您的代码有未定义的行为时,它可能会崩溃,它可能会输出奇怪的结果,或者它可能(如本例所示)似乎可以正常工作。此外,进行看似无关的更改,例如为调试而调用 printf 或未使用的局部变量,可能会改变未定义行为的表现方式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-22
        • 2012-02-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多