打印字符串的整数表示时 Scanf 和 strtol 的属性

Properties of Scanf and strtol when printing out an integer representation of string

我知道字符串只是具有相邻内存地址的字符数组。所以当你有一个字符数组时:

char s[5];
s[0] = '1';
s[1] = '2';
s[2] = '3';
s[3] = '4';
s[4] = '5';

并将 s[1] 处的字符数组更改为“5”,然后打印这样的数组应该 return“15345”。现在我的问题是关于 scanf 和 strtol 函数。当我使用不同大小的字符串两次使用 scanf 将值插入数组 s 时,为什么 strtol 函数不转换整个数组?

这是我的代码示例:

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

int main(){
    char bytes[5];
    printf("enter size 1: ");
    scanf("%s", bytes);

    printf("the size is: %ld\n", strtol(bytes, NULL, 10));

    printf("enter size 2: ");
    scanf("%s", bytes);

    printf("the size is: %ld\n", strtol(bytes, NULL, 10));

    return 0;

}

想象一下这些用户输入:

10000

然后程序会打印出 "the size is 10000"

然后用户输入:

100

程序然后打印 "the size is 100"

为什么它不再打印出"the size is 1000"?我只将 100 存储到字节中,第一个输入的字节的剩余数组元素不应该保持不变并且 strtol 应该正确转换数组的其余部分吗?

在我的印象中,当程序将第一个输入10000存入数组bytes时,那一刻是这样的

字节 = {1,0,0,0,0}

那么当用户输入 100 时,数组看起来是一样的,因为它只改变了前 3 个元素的值,而数组的其余部分应该保持不变:

字节 = {1,0,0,0,0}

使用那个 strtol 会将整个数组转换为 10000 对吗?

当将值存储到同一内存地址时,scanf 是否本质上 "empty" 超出了数组的其余部分?

您遗漏了 个重要的 属性 字符串。它们必须以 NUL 字节结尾,又名 '[=10=]'.

这意味着如果您将“10000”写入 5 字节数组,您就违反了规则。

scanf 函数会将 %s 的字符转换为字符串,直到遇到 space。这不是一个安全的操作。您应该使用 scanf("%4s", bytes) 之类的内容来限制转换的长度。因为 scanf 文档说:

String input conversions store a terminating null byte ('[=17=]') to mark the end of the input; the maximum field width does not include this terminator.

文档中的那一行也解释了为什么大小 2 得到“100”。因为 scanf{'1', '0', '0', '[=15=]'} 写入了 bytes 数组。

I know that strings are just an array of chars with adjacent memory addresses.

不完全是。在 C 中,字符串也是 零终止的 。也就是说,字符串以具有零值的第一个字符结尾。例如

char a[6] = { 'h', 'i',  0 , 'h', 'o', 0 }; // print(a) prints "hi"
char b[6] = { 'h', 'e', 'l', 'l', 'o', 0 }; // print(b) prints "hello"
char c[5] = { 'h', 'e', 'l', 'l', 'o' };    // print(c) will attempt to print "hello" followed by whatever characters happen to follow c[4] in memory, until it hits a zero value. But that may be reading outside the memory bounds of your application, or indeed your system, so anything can happen.

So when you have a character array: <snip>

如果您将 s 扩展为 char s[6] 并设置 s[5] = 0,您关于更改 s[1] 并打印它的假设将是正确的

Now my question is about scanf and strtol functions. When I insert values into the array s using scanf twice using different sized strings, Why is it that the strtol function does not convert the ENTIRE array?

首先是一个建议,在每 scanf("%s", bytes); 行之后插入以下内容:

printf("bytes = { %02x, %02x, %02x, %02x, %02x } (%02x)",
        bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5] );

运行 您的测试代码进行了更改,并检查该行打印的内容。 如果您看到了,您将有望看到关于 scanfstrtol.

问题的答案

我将在下面用一些注释来注释您的代码,指出 bytes 的内容,使用 ? 作为未知:

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

int main(){
    char bytes[5];
    printf("enter size 1: ");
    scanf("%s", bytes);  // 10000<return>

    // bytes {  ? ,  ? ,  ? ,  ? ,  ?  } bytes[5] = ?
    printf("the size is: %ld\n", strtol(bytes, NULL, 10));
    // bytes { '1', '0', '0', '0',' 0' } bytes[5] = 0 !!! Note overflow

    printf("enter size 2: ");
    scanf("%s", bytes);  // 100<return>
    // bytes { '1', '0', '0', 0,' 0' } Note bytes[3] changes from '0' to 0

    printf("the size is: %ld\n", strtol(bytes, NULL, 10));

    return 0;

}

简而言之,

Does scanf essentially "empty" out the rest of the array when storing values into the same memory address?

它不会清空它,但你正在读取一个字符串 (format = "%s"),因此 scanf 将在你读入的字符串末尾添加适当的终止零。

简单的回答:

scanf() 将用 [=12=] 终止您的字符数组。它不会清空数组的其余部分。

这是一个证明这一点的简单程序:

#include <stdio.h>

int main(void) {
    char str[100];

    scanf("%s", str); // Inputing 0123456789
    printf("String : %s\n", str);

    scanf("%s", str); // Inputing 01234
    printf("String 2 : %s\n", str); // str should be { '0', '1', '2', '3', '4', '[=10=]', '6', ... }

    printf("Proof : %s", str + 6); // Outputs 6789
    return 0;
}

scanf 将用它找到的内容覆盖数组并在末尾添加一个 [=12=]。因此,数组的其余部分保持完整并且仍然可以访问。

在您的情况下,这是您的数组在内存中的样子:

  • 秒前 scanf() : { '1', '0', '0', '0', '\0' } // 1000

  • 秒后 scanf() : { '1', '0', '0', '\0', '\0' } // 100