打印字符串的整数表示时 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" 超出了数组的其余部分?
您遗漏了 c 个重要的 属性 字符串。它们必须以 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] );
运行 您的测试代码进行了更改,并检查该行打印的内容。
如果您看到了,您将有望看到关于 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;
}
简而言之,
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
我知道字符串只是具有相邻内存地址的字符数组。所以当你有一个字符数组时:
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" 超出了数组的其余部分?
您遗漏了 c 个重要的 属性 字符串。它们必须以 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] );
运行 您的测试代码进行了更改,并检查该行打印的内容。
如果您看到了,您将有望看到关于 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;
}
简而言之,
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