我知道字符串只是一个具有相邻内存地址的字符数组。所以当你有一个字符数组:
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是否基本上“清空”了数组的其余部分?
答案 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] );
使用该更改运行您的测试代码,并检查该行打印的内容。
如果您看到这一点,那么您希望看到有关scanf
和strtol
的问题的答案。
我会在下面添加一些评论来注释您的代码,指出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)
您遗漏了c字符串的重要属性。它们必须以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