Это - главным образом персональная проблема стиля. Лично, пока where
пункт соответствует на одной строке, я группирую пункты.
Используя приблизительно where
с будет иметь тенденцию быть менее производительной, потому что она требует дополнительного вызова делегата для каждого элемента, который делает ее настолько далеко. Однако это, вероятно, будет незначительной проблемой и должно только быть рассмотрено, если профилировщик покажет его, чтобы быть проблемой.
Общение с Фредом Дж. Тайдманом, заместителем председателя PL22.11 (ANSI "C"), на comp.std.c пролило некоторый свет на это:
fscanf
Элемент ввода определяется как самая длинная последовательность вводимых символов [...] который является или является префиксом соответствующая входная последовательность. (7.19.6.2 P9)
Это делает "0x" самой длинной последовательностью, которая является префиксом соответствующей входной последовательности. (Даже с преобразованием % i
, поскольку шестнадцатеричный «0x» является более длинной последовательностью, чем десятичный «0».)
Первый символ, если есть, после элемент ввода остается непрочитанным. (7.19.6.2 P9)
Это заставляет fscanf
прочитать букву «z» и вернуть ее как несоответствующую (соблюдая ограничение в один символ в сноске 251)).
Если элемент ввода не соответствует последовательность, выполнение директива не выполняется: это условие сбой сопоставления. (7.19.6.2 P10)
Это приводит к тому, что "0x" не может соответствовать, т.е. fscanf
не должен назначать никакого значения, возвращать ноль (если % x
или % i
был первым спецификатором конверсии) и оставил «z» в качестве первого непрочитанного символа во входном потоке.
strtol
Определение strtol
(и strtoul
) отличается в одном решающем моменте:
Субъектная последовательность определяется как самая длинная начальная подпоследовательность строка ввода, начиная с первой символ без пробела, , имеющий ожидаемая форма . (7.20.1.4 P4, выделено мной)
Это означает, что strtol
должен искать самую длинную действительную последовательность, в данном случае «0». Он должен указывать endptr
на «x» и возвращать ноль в качестве результата.
I don't believe the parsing is allowed to produce different results. The Plaugher reference is just pointing out that the strtol()
implementation might be a different, more efficient version as it has complete access to the entire string.
Я не уверен, что понимаю вопрос, но, во-первых, scanf () должен обрабатывать EOF. scanf () и strtol () - разные чудовища. Может, стоит вместо этого сравнить strtol () и sscanf ()?
Согласно спецификации C99, семейство функций scanf ()
анализирует целые числа так же, как семейство функций strto * ()
. Например, для спецификатора преобразования x
это читается так:
Соответствует необязательно подписанному шестнадцатеричное целое число в формате то же, что и ожидалось для предмета последовательность функции
strtoul
с значение 16 для аргументаbase
.
Итак, если sscanf ()
и strtoul ()
дают разные результаты, реализация libc не соответствует.
Какими должны быть ожидаемые результаты вашего примера кода , немного неясно:
strtoul ()
принимает необязательный префикс 0x
или ] 0X
, если база
равна 16
, а в спецификации указано
Подлежащая последовательность определяется как самая длинная начальная подпоследовательность строка ввода, начиная с первой не-пробельный символ, то есть ожидаемая форма.
Для строки «0xz»
, на мой взгляд, самая длинная начальная подпоследовательность ожидаемой формы - «0»
, поэтому значение должно быть 0
и аргумент endptr
должен иметь значение x
.
mingw-gcc 4.4.0 не соглашается и не может проанализировать строку с помощью обоих strtoul ()
и sscanf ()
. Причина может заключаться в том, что самая длинная начальная подпоследовательность ожидаемой формы - это «0x»
- это недопустимый целочисленный литерал, поэтому синтаксический анализ не производится.
Я думаю, что такая интерпретация стандарта неверна: Подпоследовательность ожидаемой формы всегда должна давать допустимое целочисленное значение (если оно выходит за пределы допустимого диапазона, возвращаются значения MIN
/ MAX
, а для errno
установлено значение ERANGE
).
cygwin-gcc 3.4.4 (который использует newlib, насколько мне известно) также не будет анализировать литерал, если используется strtoul ()
, но анализирует строку в соответствии с моей интерпретацией стандарта с помощью sscanf ()
.
Помните, что моя интерпретация стандарта чревата вашей начальной проблемой, то есть что стандарт гарантирует только возможность ungetc ()
один раз. Чтобы решить, является ли 0x
частью литерала, вы должны прочитать вперед два символа: x
и следующий за ним символ. Если это не шестнадцатеричный символ, их нужно вернуть. Если есть еще токены для анализа, вы можете буферизовать их и обойти эту проблему, но если это ' s последний токен, вы должны ungetc ()
оба символа.
Я не совсем уверен, что fscanf ()
должен делать, если ungetc ()
терпит неудачу. Может просто установить индикатор ошибки потока?
Я не уверен, как реализация scanf () может быть связана с ungetc (). scanf () может использовать все байты в буфере потока. ungetc () просто помещает байт в конец буфера, и смещение также изменяется.
scanf("%d", &x);
ungetc('9', stdin);
scanf("%d", &y);
printf("%d, %d\n", x, y);
Если на входе «100», на выходе будет «100, 9». Я не понимаю, как scanf () и ungetc () могут мешать друг другу. Извините, если я добавил наивный комментарий.
Ответ устарело после переписывания вопроса. Хотя есть несколько интересных ссылок в комментариях.
Если сомневаетесь, напишите тест. - пословица
После тестирования всех комбинаций спецификаторов преобразования и вариантов ввода, которые я мог придумать, Я могу сказать, что это правильно, что два семейства функций не дают идентичных результатов . (По крайней мере, в glibc, которая у меня есть для тестирования.)
Разница появляется, когда встречаются три обстоятельства:
«% i»
или «% x»
(разрешает ввод в шестнадцатеричном формате). шестнадцатеричный префикс «0x»
. Пример кода:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char * string = "0xz";
unsigned u;
int count;
char c;
char * endptr;
sscanf( string, "%x%n%c", &i, &count, &c );
printf( "Value: %d - Consumed: %d - Next char: %c - (sscanf())\n", u, count, c );
i = strtoul( string, &endptr, 16 );
printf( "Value: %d - Consumed: %td - Next char: %c - (strtoul())\n", u, ( endptr - string ), *endptr );
return 0;
}
Вывод:
Value: 0 - Consumed: 1 - Next char: x - (sscanf())
Value: 0 - Consumed: 0 - Next char: 0 - (strtoul())
Это меня смущает. Очевидно, что sscanf ()
не срабатывает при 'x'
, иначе он не сможет проанализировать любые "0x"
шестнадцатеричные числа с префиксом . Итак, он прочитал 'z'
и обнаружил, что он не соответствует. Но он решает использовать только ведущий «0»
в качестве значения. Это означало бы отодвинуть назад 'z'
и 'x'
. (Да, я знаю, что sscanf ()
, который я использовал здесь для простого тестирования, не работает с потоком, но я твердо предполагаю, что они сделали все функции ... scanf ()
ведут себя одинаково для согласованности.)
Итак ... one-char ungetc ()
на самом деле не является причиной, здесь ...?: - /
Да, результаты расходятся . Я до сих пор не могу это объяснить как следует ...: - (
не работает с потоком, но я твердо уверен, что они заставили все функции ... scanf ()
вести себя одинаково для согласованности.)
Итак ... one-char ungetc ()
не является причиной, здесь ...?: - /
Да, результаты различаются . Я до сих пор не могу это объяснить как следует ...: - (
не работает с потоком, но я твердо уверен, что они заставили все функции ... scanf ()
вести себя одинаково для согласованности.)
Итак ... one-char ungetc ()
не является причиной, здесь ...?: - /
Да, результаты различаются . Я до сих пор не могу объяснить это как следует ...: - (
Для ввода в функции scanf () , а также для функций strtol () , в сек. . 7.20.1. 4 P7 указывает: Если предметная последовательность пуста или не имеет ожидаемой формы, преобразование не выполняется; значение nptr сохраняется в объекте, на который указывает endptr, при условии, что endptr не является нулевым указателем . Также вы должны учитывать, что правила синтаксического анализа тех токенов, которые определены в соответствии с правилами Sec. 6.4.4 Константы , правило, указанное в разд. 7.20.1.4 P5 .
Остальное поведение, такое как значение errno , должно зависеть от реализации. Например, в моем ящике FreeBSD я получил значения EINVAL и ERANGE , и в Linux происходит то же самое, где стандартные рефереры только на значение errno ERANGE .
] преобразование не выполняется; значение nptr сохраняется в объекте, на который указывает endptr, при условии, что endptr не является нулевым указателем . Также вы должны учитывать, что правила синтаксического анализа тех токенов, которые определены в соответствии с правилами Sec. 6.4.4 Константы , правило, указанное в разд. 7.20.1.4 P5 .Остальное поведение, такое как значение errno , должно зависеть от реализации. Например, в моем ящике FreeBSD я получил значения EINVAL и ERANGE , и в Linux происходит то же самое, где стандартные рефереры только на значение errno ERANGE .
] преобразование не выполняется; значение nptr сохраняется в объекте, на который указывает endptr, при условии, что endptr не является нулевым указателем . Также вы должны учитывать, что правила синтаксического анализа тех токенов, которые определены в соответствии с правилами Sec. 6.4.4 Константы , правило, указанное в разд. 7.20.1.4 P5 .Остальное поведение, такое как значение errno , должно зависеть от реализации. Например, в моем ящике FreeBSD я получил значения EINVAL и ERANGE , и в Linux происходит то же самое, где стандартные рефереры только на значение errno ERANGE .
] Также вы должны учитывать, что правила синтаксического анализа тех токенов, которые определены в соответствии с правилами Sec. 6.4.4 Константы , правило, указанное в разд. 7.20.1.4 P5 .Остальное поведение, такое как значение errno , должно зависеть от реализации. Например, в моем ящике FreeBSD я получил значения EINVAL и ERANGE , и в Linux происходит то же самое, где стандартные рефереры только на значение errno ERANGE .
] Также вы должны учитывать, что правила синтаксического анализа тех токенов, которые определены в соответствии с правилами Sec. 6.4.4 Константы , правило, указанное в разд. 7.20.1.4 P5 .Остальное поведение, такое как значение errno , должно зависеть от реализации. Например, в моем ящике FreeBSD я получил значения EINVAL и ERANGE , и в Linux происходит то же самое, где стандартные рефереры только на значение errno ERANGE .
]To summarize what should happen according to the standard when parsing numbers:
fscanf()
succeeds, the result must be identical to the one obtained via strto*()
in contrast to strto*()
, fscanf()
fails if
the longest sequence of input characters [...] which is, or is a prefix of, a matching input sequence
according to the definition of fscanf()
is not
the longest initial subsequence [...] that is of the expected form
according to the definition of strto*()
This is somewhat ugly, but a necessary consequence of the requirement that fscanf()
should be greedy, but can't push back more than one character.
Some library implementators opted for differing behaviour. In my opinion
strto*()
fail to make results consistent is stupid (bad mingw)fscanf()
accepts all values accepted by strto*()
violates the standard, but is justified (hurray for newlib if they didn't botch strto*()
:()