fscanf 没有在 c 中正确赋值
fscanf doesn't assign values correctly in c
我有一个这样的 txt 文件:
milk l 300
oil l 200
..
我的功能如下(一些变量是全局变量,一些单词是意大利语):
void Calcola() {
FILE *fp;
fp = fopen("ingredients_out.txt", "r");
float somma = 0.0;
int i;
char ingredient[15]; //this is the problem
char simbolo[1];
float quantita;
while(fscanf(fp, "%s%s%f", ingredient, simbolo, &quantita) == 3){
printf("%s\n", ingredient); //I think the problem is here
if(simbolo[0] != 'g') {
for(i = 0; i < 4; i++) {
if(strcmp(V[i].ingrediente, ingredient) == 0) {
somma += quantita * V[i].peso;
}
}
}
}
fclose(fp);
printf("Somma pesi: %d\n", somma);
}
问题是“ingredient”变量总是空的,它是一连串的空格..为什么?
这个其实挺有意思的。所以原因实际上是因为 simbolo[1]
没有足够的存储空间来容纳 C 字符串(即以 [=11=]
结尾的字符串)。但这与 ingredient
为空有什么关系?
嗯,如果你仔细看,在第一次读取(即读取“milk”)时 ingredient
的第一个字符是空终止符,然后是 i
,然后是 l
,然后 k
。等待! [=11=]
是如何到达 m
应该到达的位置的?
答案?缓冲区溢出。
fscanf
首先将“牛奶”放入 ingredient
,这是应该的。然后它 尝试 将 "l"
放入 simbolo
(注意 "l"
实际上是 l
和 [=11=]
)并且它这样做是对的。但是等等,simbolo
只能容纳一个字符。所以它持有 l
,[=11=]
到底发生了什么?
你猜对了。由于现代架构上的堆栈对齐,ingredient
内存在 simbolo
结束后立即开始。所以 [=11=]
实际上被放入了 ingredient[0]
并且你刚刚破坏了那段记忆。
这是与缓冲区溢出相关的未定义行为的漂亮教科书定义。不要指望每台机器都会发生这种情况。但我很确定这就是你的机器上发生的事情。
所以,是的,如果您希望 l
作为字符串,解决方法是只执行 char simbolo[2]
。或者,如果您确定它始终是单数字符,只需执行 char simbolo
并在 fscanf
上使用 %c
编辑:同样如@Barmar 所述,仅 %s
和 simbolo[2]
不会拯救你,除非你确定输入文件的长度并且你 100% 信任它。如果您坚持使用 %s
,还要在 %ns
中提及 n
,其中 n
是您的缓冲区可以容纳的字符数(不包括空终止符)。
我有一个这样的 txt 文件:
milk l 300
oil l 200
..
我的功能如下(一些变量是全局变量,一些单词是意大利语):
void Calcola() {
FILE *fp;
fp = fopen("ingredients_out.txt", "r");
float somma = 0.0;
int i;
char ingredient[15]; //this is the problem
char simbolo[1];
float quantita;
while(fscanf(fp, "%s%s%f", ingredient, simbolo, &quantita) == 3){
printf("%s\n", ingredient); //I think the problem is here
if(simbolo[0] != 'g') {
for(i = 0; i < 4; i++) {
if(strcmp(V[i].ingrediente, ingredient) == 0) {
somma += quantita * V[i].peso;
}
}
}
}
fclose(fp);
printf("Somma pesi: %d\n", somma);
}
问题是“ingredient”变量总是空的,它是一连串的空格..为什么?
这个其实挺有意思的。所以原因实际上是因为 simbolo[1]
没有足够的存储空间来容纳 C 字符串(即以 [=11=]
结尾的字符串)。但这与 ingredient
为空有什么关系?
嗯,如果你仔细看,在第一次读取(即读取“milk”)时 ingredient
的第一个字符是空终止符,然后是 i
,然后是 l
,然后 k
。等待! [=11=]
是如何到达 m
应该到达的位置的?
答案?缓冲区溢出。
fscanf
首先将“牛奶”放入 ingredient
,这是应该的。然后它 尝试 将 "l"
放入 simbolo
(注意 "l"
实际上是 l
和 [=11=]
)并且它这样做是对的。但是等等,simbolo
只能容纳一个字符。所以它持有 l
,[=11=]
到底发生了什么?
你猜对了。由于现代架构上的堆栈对齐,ingredient
内存在 simbolo
结束后立即开始。所以 [=11=]
实际上被放入了 ingredient[0]
并且你刚刚破坏了那段记忆。
这是与缓冲区溢出相关的未定义行为的漂亮教科书定义。不要指望每台机器都会发生这种情况。但我很确定这就是你的机器上发生的事情。
所以,是的,如果您希望 l
作为字符串,解决方法是只执行 char simbolo[2]
。或者,如果您确定它始终是单数字符,只需执行 char simbolo
并在 fscanf
%c
编辑:同样如@Barmar 所述,仅 %s
和 simbolo[2]
不会拯救你,除非你确定输入文件的长度并且你 100% 信任它。如果您坚持使用 %s
,还要在 %ns
中提及 n
,其中 n
是您的缓冲区可以容纳的字符数(不包括空终止符)。