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 所述,仅 %ssimbolo[2] 不会拯救你,除非你确定输入文件的长度并且你 100% 信任它。如果您坚持使用 %s,还要在 %ns 中提及 n,其中 n 是您的缓冲区可以容纳的字符数(不包括空终止符)。