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

时间:2015-10-04 18:16:21

标签: c string scanf strtol

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

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两次将值插入数组时,为什么strtol函数不转换ENTIRE数组?

这是我的代码示例:

#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的第一个输入存储到数组字节中时,它在那一刻看起来像这样

bytes = {1,0,0,0,0}

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

bytes = {1,0,0,0,0}

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

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

3 个答案:

答案 0 :(得分: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>

如果您将s延伸至char s[6]并设置s[5] = 0,您对更改s[1]并打印它的假设将是正确的

  

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

首先建议,在每个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;

}

简而言之,

  

scanf本质上是&#34;空的&#34;将值存储到相同的内存地址时,将数组的其余部分输出?

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

答案 1 :(得分:1)

您遗漏了字符串的重要属性。它们必须以NUL字节结尾,即'\0'

这意味着如果将“10000”写入5字节数组,则会破坏规则。

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

  

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

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

答案 2 :(得分:1)

简单回答:

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

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

#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。因此阵列的其余部分保持不变并且仍可访问。

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

  • 在第二个scanf()之前:{&#39; 1&#39;,&#39; 0&#39;,&#39; 0&#39;,&#39; 0&#39; ,&#39; \ 0&#39; } // 1000

  • 在第二个scanf()之后:{&#39; 1&#39;,&#39; 0&#39;,&#39; 0&#39;,&#39; \ 0&#39 ;,&#39; \ 0&#39; } // 100