fscanf неправильно присваивает значения в c

#c #scanf

#c #сканф #scanf

Вопрос:

У меня есть текстовый файл, подобный этому:

 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, amp;quantita) == 3){   
    printf("%sn", 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: %dn", somma);
}
  

Проблема в том, что переменная «ингредиент» всегда пуста, это серия пробелов .. Почему?

Комментарии:

1. char simbolo[1]; как C-строка, она может содержать только нулевой терминатор, так что она в принципе бесполезна. Может быть, изменить это на char simbolo; и использовать %c в fscanf .

2. или лучше написать char simbolo[2] , чтобы иметь пустую позицию для завершающего значения null…

3. @SergeBallesta Вам нужно будет использовать %1s , чтобы он не записывал дальше массива

Ответ №1:

На самом деле это довольно интересно. Таким образом, причина на самом деле в том, что simbolo[1] у него недостаточно памяти для хранения строки C (т.е. Строки, заканчивающиеся на a ). Но какое это имеет отношение к ingredient пустому значению?

Ну, если вы посмотрите внимательно, самый первый символ ingredient при первом чтении (то есть чтение «молоко») является нулевым терминатором, тогда i , тогда l , тогда k . ПОДОЖДИТЕ! Как это получилось, где m должно было быть?

Ответ? Переполнение буфера.

fscanf сначала помещает «молоко» ingredient , как и должно быть. Затем он попытался вставить "l" в simbolo (примечание "l" на самом деле l и ), и он делает это по праву. Но подождите, simbolo может содержать только один символ. Итак, он содержит l , что, черт возьми, случилось с ?

Вы уже догадались. Из-за выравнивания стека на современных архитектурах ingredient память начинается сразу после окончания simbolo . На самом деле So вводится, ingredient[0] и boom вы только что уничтожили эту память.

Это довольно хрестоматийное определение неопределенного поведения, связанного с переполнением буфера. Не ожидайте, что это произойдет на каждой отдельной машине. Но я почти уверен, что это то, что происходит на вашем компьютере.

Так что да, исправление заключается в том, чтобы просто сделать char simbolo[2] , если вы хотите, чтобы это l было как строка. Или, если вы уверены, что это всегда единственный символ, просто выполните char simbolo и используйте %c на fscanf

Редактировать: также, как упоминалось @Barmar, %s with simbolo[2] alone не спасет вас, если вы не уверены в длине входного файла и не доверяете ему на 100%. Если вы настаиваете на использовании %s , также укажите a n в %ns где n — количество символов (исключая нулевой ограничитель), которое может содержать ваш буфер.

Комментарии:

1. Если вы не уверены, что это всегда один символ, [2] он также не будет достаточно большим. Это должно быть 1 максимально возможное значение.

2. И при использовании %c перед ними должен быть пробел. В противном случае он поместит пробел в simbolo .

3. О да! Большое спасибо, друг! Вы были действительно точны!

4. @Barmar очень хорошие замечания! %s в одиночку определенно небезопасно

5. Вы можете использовать %1s для предотвращения переполнения, но если слово длиннее 1 символа, оно не будет читать следующее float .