Я изучаю язык Си. Это мой код, работающий в Linux.
#include <stdio.h>
int main(int argc, char *argv[]){
char string[] = "Rudolph is 12 years old";
char str[20] = {0}
char i;
sscanf(string, "%s %*s %d", str, &i);
printf("%s -> %d\n", str, i);
return 0;
}
Мой ожидаемый результат:
Rudolph -> 12
Но я получил результат:
-> 12
Я хочу знать, что произошло и почему? Я попробовал заменить char i
на int i
и получил ожидаемый результат.
Но я не знаю, почему с char i
я не получаю ожидаемого результата.
Вероятно, у вас есть предупреждение для %d
и char i
. Почему ты игнорируешь это?
Соврите компилятору (вы обещали дать sscanf адрес int
(%d
), но вместо этого отправили адрес char
(&i
)) и он отомстит!
Это не ваша текущая ошибка, но: НИКОГДА не игнорируйте возвращаемое значение любой из функций семейства scanf()
. Это единственный способ узнать, удалось ли преобразование или нет. Кроме того, НИКОГДА не используйте преобразование %s
без спецификатора ширины в реальном коде — это просто запрос на переполнение буфера.
«sscanf дал мне неожиданный результат» кажется хорошим названием для мемуаров.
Голосуем за то, чтобы закрыть это как простую опечатку, поскольку это не представляет никакой ценности для будущих читателей, и у нас на сайте уже есть тысячи вопросов о несоответствии строк формата printf/scanf.
%d
в функциях scanf
предназначен для int
, а не char
. При вызове неопределенного поведения три дополнительных байта, вероятно, перезапишут что-то еще. Дезинфицирующее средство адресов находит проблему, и компилятор предупреждает о ней: https://godbolt.org/z/YPKv111r9
%hhd
для signed char
, %hhu
для unsigned char
, когда вы хотите прочитать число, а не символ.
Вам следует использовать %hhd вместо %d, поскольку sscanf рассматривает %d как ожидающее 4-байтовое целое число. При использовании %d sscanf предполагает, что предоставленная область памяти достаточно велика для хранения целого числа (обычно 4 байта). Это может привести к неопределенному поведению, поскольку sscanf может перезаписать соседнюю память. В вашем конкретном случае эта соседняя память, скорее всего, является частью массива str, что приводит к потенциальному повреждению его содержимого. Использование %hhd гарантирует, что sscanf правильно интерпретирует местоположение целевой памяти как символ (1 байт).
Вот фрагмент кода с исправлением:
#include <stdio.h>
int main(int argc, char* argv[]) {
char string[] = "Rudolph is 12 years old";
char str[20] = {0};
char i;
sscanf(string, "%s %*s %hhd", str,&i);
printf("%s -> %d\n", str, i);
return 0;
}
Это изменение позволит правильно проанализировать входную строку и сохранить проанализированное значение в переменной char.
«Использование %hhd гарантирует правильность sscanf» --> имеет смысл, когда char
подписано, но не тогда, когда char
не подписано, где ожидается "%hhu"
.
Итак, вы уже знаете ответ?
char
— это один байт, аint
, вероятно, 4. Итак, когдаsscanf
сохраняет значение, куда идут 3 дополнительных байта?! Может ли это перезаписать какое-то другое значение?