2016-02-09 3 views
-1

В C++, если я прочитал целое число из строки, кажется, что на самом деле не имеет значения, использую ли я u или d как спецификатор преобразования, так как оба принимают даже отрицательные целые числа.Разница между% u и% d в scanf()

#include <cstdio> 
using namespace std; 

int main() { 
    int u, d; 
    sscanf("-2", "%u", &u); 
    sscanf("-2", "%d", &d); 
    puts(u == d ? "u == d" : "u != d"); 
    printf("u: %u %d\n", u, u); 
    printf("d: %u %d\n", d, d); 
    return 0; 
} 

Ideone.com

я копал глубже, чтобы найти, если есть какая-то разница. Я обнаружил, что

int u, d; 
sscanf("-2", "%u", &u); 
sscanf("-2", "%d", &d); 

эквивалентно

int u, d; 
u = strtoul("-2", NULL, 10); 
d = strtol("-2", NULL, 10); 

согласно cppreference.com.

Есть ли разница вообще между u и d при использовании этих спецификаторов преобразования для синтаксического анализа, то есть в формате передается scanf функций -типа? Что это?

Ответ тот же для C и C++, правильно? Если нет, меня интересует и то, и другое.

+0

'% u' для неподписанных и'% i' подписано. – user3813674

+1

Я не совсем понял вопрос. Не соответствует ли ответ в документации, которую вы связали? –

+0

'scanf' is C. Если вы используете C++, используйте соответствующий механизм C++! Это разные языки. Сказал, что: RTFM и включить предупреждения компилятора и обратить внимание на них. – Olaf

ответ

0

Каждый conversion specifier имеет соответствующий тип результирующего аргумента, определенной в C спецификации. директивы Конверсионные %u и %d действительно принимают одни и те же входы, как вы наблюдали, но аргумент, соответствующий %u должен быть типа unsigned int*, не int*. То есть ваш пример должен быть скорректирован как:

unsigned int u; 
int d; 
sscanf("-2", "%u", &u); 
sscanf("-2", "%d", &d); 

Если вы включили предупреждения, вы получите их при составлении оригинального примера.И rightfully so:

Если подавление назначение не было обозначено *, то результат преобразования помещается в объект, на который указывает первый аргумент следующий формат аргумента, который еще не получил результат преобразования. Если этот объект не имеет соответствующего типа или если результат преобразования не может быть представлен в объекте, поведение не определено.

Акцент мой.

Таким образом, вы были вызовом undefined behavior (см часть Что Неопределенное поведение). Когда вы вызываете неопределенное поведение, вы одиноки и неприятны.


модификаторы преобразования определяются в C99 (последнем общественном проекте, N1256; official PDF). Определение такое же, как в C11 (последний публичный черновик, N1570; official PDF). most recent C++ draft (по состоянию на 2015-02-10, N4567), связанного с list of C++ standard documents под другой вопрос о переполнении стека принимает определение cstdio заголовка from C99 и does not modify it (кроме placing the functions into the std namespace и незначительные изменения, указанные в § 27.9.2).

+0

Спасибо [Olaf] (http://stackoverflow.com/users/4774918/olaf) за то, что он предложил мне использовать spec вместо cppreference.com и ссылку на его версию HTML. – Palec

8

%d: Сканировать целое число как десятичное signed int. Аналогичный спецификатор преобразования, %i, интерпретирует число как шестнадцатеричное, если ему предшествует 0x, и как восьмеричный, когда ему предшествует 0. В противном случае он идентичен.

%u: Сканировать целое число как десятичное unsigned int.

1

Технически, вы призываете неопределенное поведение при попытке прочитать отрицательное число в int с использованием %u спецификатора формата. Вы делаете sscanf обрабатываете указатель на целое число со знаком в качестве указателя на целое число без знака, и эти типы несовместимы. Он работает только потому, что как беззнаковое, так и подписанное ints имеют аналогичное представление битов, а целые числа со знаком используют представление с двумя дополнениями.

TL/DR: Вы не гарантированно получить -2 от sscanf("-2", "%u", &u);

+0

Это то, где поведение может отличаться между C и C++ (не уверен). В C это фактически легально, учитывая, что 'signed int' и' unsigned int' имеют равные гарантии для выравнивания, ср. ISO 9899: 2011 §6.3.2.3 ¶7. – fuz

+0

В контексте функций типа «scanf» использование неподписанного спецификатора преобразования для подписанной переменной может по-прежнему приводить к [неопределенному поведению в C11] (http://port70.net/~nsz/c/c11/n1570.html#7.21 .6.2p10), @FUZxxl. [C11 §6.3.2.3 ¶7] (http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p7) определяет только требования для указателей каста взад и вперед. У разыменования есть дополнительные ограничения ([C11 §6.5 ¶7] (http://port70.net/~nsz/c/c11/n1570.html#6.5p7)), но подписанное/unsigned aliasing в порядке. Таким образом, пример 'sscanf' в моем вопросе не совсем эквивалентен' strtoul'/'strtol'. – Palec

Смежные вопросы