Какой инициализатор подходит для int64_t?

Мне нравится инициализировать мои переменные некоторым «фиктивным» значением, и я начал использовать int64_t и uint64_t. Пока что похоже, что есть как минимум три способа инициализировать int64_t определенным значением (и с небольшими изменениями для беззнакового эквивалента):

int64_t method_one   = 0;
int64_t method_two   = 0LL;
int64_t method_three = INT64_C(0);

Я использую GCC и ориентируюсь на OS X и Linux. Я хотел бы выбрать метод, который нацелен на простоту переносимости и ясность, но, прежде всего, на правильность. Я слишком много думаю об этом, или есть «лучший» или «наиболее рекомендуемый» подход для инициализации этого типа переменной для любого конкретного значения, которое я ему добавляю (которое, конечно, находится в пределах его границ)?


person Alex Reynolds    schedule 07.11.2012    source источник
comment
ты сборку смотрел? кто-нибудь из них на самом деле имеет какое-либо значение?   -  person iabdalkader    schedule 07.11.2012
comment
Нет, сборку не смотрел. Однако не будет ли это зависеть от конкретного случая?   -  person Alex Reynolds    schedule 07.11.2012
comment
Да, но компилятор, скорее всего, будет следовать какому-то стандарту, я все равно спрашивал из любопытства.   -  person iabdalkader    schedule 07.11.2012
comment
@mux: поправьте меня, если я ошибаюсь, фактическое битовое представление между (со знаком) 0 и без знака (0); разве это не то же самое?   -  person Fingolfin    schedule 07.11.2012
comment
@AdelQodmani, если это дополнение до двух, то да, но я хотел проверить, будет ли ассемблер сгенерирован до нуля для всех 64 бит или нет.   -  person iabdalkader    schedule 07.11.2012


Ответы (3)


int64_t method_one   = 0;

... вполне разумно. C99 (см., например, черновик здесь; да, я знаю, что это не самый последний стандарт, но это тот, который ввел типы int<N>_t) говорит, что:

  • 0 имеет тип int (§6.4.4.1 параграф 5);
  • тип выражения int64_t (§6.5.16 параграф 3);
  • тип правой части будет преобразован в тип выражения (§6.5.16.1 параграф 2);
  • это преобразование не изменит значение (§6.3.1.3 параграф 1).

Так что в этом нет ничего плохого, и отсутствие дополнительных помех делает его наиболее читаемым из вариантов при инициализации до 0 или любого другого значения в диапазоне int.

int64_t method_two   = 0LL;

int64_t не обязательно совпадает с long long; однако на самом деле это должно переносимо работать и для любого 64-битного значения со знаком (и аналогично ULL для 64-битных значений без знака): long longunsigned long long) должно быть не менее 64 бит в реализации, совместимой с C99 (§5.2. 4.2.1), поэтому LLULL) всегда должны быть безопасными для инициализации 64-битных значений.

int64_t method_three = INT64_C(0);

Возможно, это лучший вариант для значений, которые могут находиться за пределами диапазона int, поскольку он более четко выражает намерение: INT64_C(n) расширится до чего-то подходящего для любого n в (по крайней мере) 64-битном диапазоне (см. §7.18 в общее и, в частности, §7.8.4.1).


На практике я вполне мог бы использовать любой из вышеперечисленных, в зависимости от контекста. Например:

uint64_t counter = 0;

(Зачем добавлять ненужный беспорядок?)

uint64_t some_bit = 1ULL << 40;

(1 << 40 просто не будет работать, если только int не будет необычно широким; а UINT64_C(1) << 40 здесь кажется мне менее читаемым.)

uint64_t some_mask = UINT64_C(0xFF00FF00FF00FF00);

(В этом случае явный вызов значения как 64-битной константы кажется мне более читаемым, чем запись 0xFF00FF00FF00FF00ULL.)

person Matthew Slattery    schedule 08.11.2012
comment
Спасибо, это гораздо более подробный ответ. Сейчас я использую C99 (чтобы получить поддержку int64_t), и мне кажется, что мне нравится использовать INT64_C() по сравнению с двумя другими вариантами, хотя бы потому, что он настолько явный, в то время как первый не работает с литералами за пределами диапазона int, а второй, кажется, подразумевает зависимость от long long (и его беззнакового эквивалента), имеющего ширину ровно 64 бита, чего я предпочел бы избежать. Это проясняет мои опасения и мысли. Спасибо еще раз. - person Alex Reynolds; 08.11.2012

Лично я бы использовал третий, самый портативный способ добиться этого.

#include <stdint.h>

int64_t method_three  = INT64_C(0);
uint64_t method_three = UINT64_C(0);

В любом случае, я не думаю, что это очень важно.

person md5    schedule 07.11.2012
comment
+1 за это. Весь смысл макросов приведения INT64_C и UINT64_C в том, чтобы быть переносимым. Кто-то может попытаться возразить, что это не важно для 64-битных значений (они всегда будут long long int!), но ничто не говорит о том, что int64_t должно быть long long int. - person tomlogic; 07.11.2012

Согласно стандарту ANSI C суффикс для long long int и unsigned long long int равен LL и ULL соответственно:

восьмеричное или шестнадцатеричное с суффиксом ll или LL long long int, unsigned long long int десятичное, восьмеричное или шестнадцатеричное с суффиксом как u или U, так и ll или LL unsigned long long инт

Если вы знаете, что int64_t определяется как:

typedef signed long long int int64_t

Тогда второй метод, безусловно, правильный:

int64_t method_two   = 0LL;
uint64_t method_two   = 0ULL;

Изменить:

Принимая во внимание проблемы с переносимостью и тот факт, что не гарантируется, что он будет определен как long long, было бы лучше использовать третий метод:

INT64_C()
UINT64_C()
person iabdalkader    schedule 07.11.2012
comment
Нет никаких гарантий, что int64_t это long long. Если бы они были, нам бы не понадобился typedef. - person Bo Persson; 07.11.2012
comment
Я помню, когда (1980-е годы) большое число было 16-битным. Мне нравится uint64_t и т. д., определенные в stdint.h. Что такое long или long long исторически меняется со временем, поэтому я их избегаю. Вы всегда можете выполнить быструю проверку sizeof(), если вам нужно их использовать. - person Alan Corey; 19.03.2018