0

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

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

并将 s[1] 处的字符数组更改为“5”,然后打印这样的数组应返回“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

然后程序将打印出“大小为 10000”

然后用户输入:

100

然后程序打印“大小为 100”

为什么它不再打印出“大小为1000”?我只将 100 存储为字节,第一个输入中字节的剩余数组元素不应该保持不变, strtol 应该转换数组的其余部分吗?

在我看来,当程序将第一个输入 10000 存储到数组字节中时,那一刻看起来像这样

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

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

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

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

将值存储到同一内存地址时,scanf 是否本质上“清空”了数组的其余部分?

4

3 回答 3

2

我知道字符串只是一个具有相邻内存地址的字符数组。

不完全的。在 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.

所以当你有一个字符数组时:<snip>

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

现在我的问题是关于 scanf 和 strtol 函数。当我使用 scanf 两次使用不同大小的字符串将值插入数组 s 时,为什么 strtol 函数不转换整个数组?

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

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

使用该更改运行您的测试代码,并检查该行打印的内容。scanf如果您看到这一点,您将有望看到有关和的问题的答案strtol

我将在下面使用一些注释来注释您的代码,指示 的内容bytes,使用?as unknown :

#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;

}

所以简而言之,

将值存储到同一内存地址时,scanf 是否本质上“清空”了数组的其余部分?

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

于 2015-10-04T20:22:25.643 回答
1

字符串的一个重要属性。它们必须以 NUL 字节结束,也就是'\0'.

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

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

字符串输入转换存储一个终止空字节('\0')来标记输入的结束;最大字段宽度不包括此终止符。

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

于 2015-10-04T20:18:49.120 回答
1

简单的回答:

scanf()将用 . 终止您的 char 数组\0。它不会清空数组的其余部分。

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

#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', '\0', '6', ... }

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

scanf将用它找到的内容覆盖数组并\0在最后添加 a 。因此,阵列的其余部分保持完整并且仍然可以访问。

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

  • 在第二个之前scanf():{ '1', '0', '0', '0', '\0' }// 1000

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

于 2015-10-04T20:24:45.100 回答