Стандарт определяет, что шестнадцатеричные константы, такие как 0x8000 (больше, чем помещается в целое число со знаком), являются беззнаковыми (точно так же, как восьмеричные константы), тогда как десятичные константы, такие как 32768, имеют длинный знак. (Точные типы предполагают 16-битное целое число и 32-битное длинное.) Однако в обычных средах C оба будут иметь одинаковое представление в двоичном формате 1000 0000 0000 0000
. Возможна ли ситуация, когда это различие действительно приводит к другому результату? Другими словами, возможна ли ситуация, когда эта разница вообще имеет значение?
Может ли разница типов между константами 32768 и 0x8000 иметь значение?
Ответы (5)
Да, это может иметь значение. Если ваш процессор имеет 16-битный int
и 32-битный long
тип, 32768 имеет тип long
(поскольку 32767 является наибольшим положительным значением, подходящим для 16-битного int
со знаком), тогда как 0x8000 (поскольку он также рассматривается для unsigned int
) по-прежнему умещается в 16-битном unsigned int
.
Теперь рассмотрим следующую программу:
int main(int argc, char *argv[])
{
volatile long long_dec = ((long)~32768);
volatile long long_hex = ((long)~0x8000);
return 0;
}
Когда 32768 считается long
, отрицание инвертирует 32 бита, в результате получается представление 0xFFFF7FFF с типом long
; гипс лишний. Когда 0x8000 считается unsigned int
, отрицание инвертирует 16 бит, в результате получается представление 0x7FFF с типом unsigned int
; затем приведение к нулю расширится до long
значения 0x00007FFF. Взгляните на H & S5, раздел 2.7.1, стр. 24ff.
Лучше всего дополнять константы U
, UL
или L
в зависимости от ситуации.
void main
? Ты должно быть шутишь.
- person ; 09.11.2011
void
, поскольку нет среды, в которую можно было бы вернуть int
.
- person Johan Bezem; 09.11.2011
void main
допустимо только в том случае, если реализация явно поддерживает и документирует его; в противном случае это делает поведение программы неопределенным. Для автономных реализаций точка входа определяется реализацией; опять же, реализация должна решить, действительно ли void main
. Но int main(void)
всегда действителен для размещенных реализаций, и нет разумной причины не использовать его или int main(int argc, char *argv)
или аналогичные.
- person Keith Thompson; 09.11.2011
char * * argv
;-)
- person Johan Bezem; 09.11.2011
char *argv[]
(что в качестве параметра эквивалентно char **argv
).
- person Keith Thompson; 09.11.2011
void main
, когда кто-то использовал его в каком-то случайном фрагменте кода, как в этом случае!
- person Oliver Charlesworth; 10.11.2011
void main
- очень распространенная ошибка - и да, в большинстве случаев это ошибка. Обычно это появляется в вопросах новичков, и новички обычно используют размещенные реализации. Указывать на него - это хорошо. (Обычно это означает, что программист учился на плохой книге, написанной автором, который должен был знать лучше.)
- person Keith Thompson; 10.11.2011
int
, а не к integer
. int
- один из нескольких целочисленных типов.
- person Keith Thompson; 21.11.2011
char
тип - да и его long
тип, если на то пошло, - 16-битный ... А вы?
- person Johan Bezem; 21.11.2011
На 32-битной платформе с 64-битной long
, a
и b
в следующем коде будут иметь разные значения:
int x = 2;
long a = x * 0x80000000; /* multiplication done in unsigned -> 0 */
long b = x * 2147483648; /* multiplication done in long -> 0x100000000 */
long x;
, как эффект x &= ~0x80000000;
по сравнению с эффектом x &= ~0x100000000;
или x &= ~0x40000000;
?
- person supercat; 28.06.2012
Еще одно исследование, которое еще не дано: сравните (с операторами больше или меньше) -1 как с 32768, так и с 0x8000. Или, если на то пошло, попробуйте сравнить каждый из них на равенство с переменной int, равной -32768.
-1
- это постоянная «единица» с унарным минусом. Применяются обычные унарные и двоичные преобразования, поэтому вы не увидите никакого любопытного поведения ни в одном из ваших случаев, ИМХО.
- person Johan Bezem; 21.11.2011
unsigned int
требуется для получения значения unsigned int
, которое при добавлении к 1 даст ноль. Точно так же, если реализация допускает существование переменной int
, равной -32768, преобразование ее в unsigned int
должно дать значение unsigned int
, которое при добавлении к 32768 даст ноль. Дополнение до двух не имеет к этому ничего.
- person supercat; 29.07.2015
Предположим, что int
- 16 бит, а long
- 32 бита (что на самом деле довольно необычно в наши дни; int
чаще всего 32 бита):
printf("%ld\n", 32768); // prints "32768"
printf("%ld\n", 0x8000); // has undefined behavior
В большинстве контекстов числовое выражение будет неявно преобразовано в соответствующий тип, определяемый контекстом. (Однако это не всегда тот тип, который вам нужен.) Это не относится к нефиксированным аргументам функций с переменным числом аргументов, таким как любой аргумент одной из функций *printf()
, следующих за строкой формата.
printf
использует длинную целочисленную константу, напечатанную как длинное десятичное значение, без проблем (как вы указали); ИМХО второй printf
тоже не проблема, так как преобразование из unsigned int
в signed long
определено и сохраняет значение. Так что, на мой взгляд, во втором выражении нет неопределенного поведения.
- person Johan Bezem; 21.11.2011
, ...
в объявлении функции) не преобразуются, за исключением продвижений аргументов по умолчанию, поскольку компилятор не знает, что ожидается long int
. Для вызова printf
, если тип продвинутого аргумента не соответствует типу, заданному форматом, поведение не определено; C99 7.19.6.1p9 говорит об этом прямо.
- person Keith Thompson; 21.11.2011
Разница была бы в том, что если бы вы попытались добавить значение к 16-битному int, он не смог бы этого сделать, потому что он выйдет за границы переменной, тогда как если бы вы использовали 32-битную длину, вы могли бы добавить любое число, которое менее 2 ^ 16 к нему.
0x8000
, и это не сработало, как ожидалось, потому что оно беззнаковое. Но на самом деле это вряд ли произойдет. - person Seth Carnegie   schedule 09.11.2011