Фрагмент:
((int8_t) + (78));
- это выражение , , которое принимает значение 78
, применяет унарный +
, затем преобразует его в тип int8_t
, прежде чем выбросить его. Это не отличается от юридических выражений:
42;
a + 1;
, которые также оценивают выражения, а затем отбрасывают результат (хотя они, возможно, будут оптимизированы из-за отсутствия, если компилятор скажет, что побочных эффектов нет).
Эти «обнаженные» выражения вполне допустимы в C и, как правило, полезны только тогда, когда имеют побочные эффекты, например, с i++
, который вычисляет i
и отбрасывает его с побочным эффектом, заключающимся в том, что он увеличивает значение.
То, как вы должны использовать этот макрос, больше похоже на:
int8_t varname = INT8_C (somevalue);
Причину такого, казалось бы, избыточного унарного оператора +
можно найти в стандарте. Цитирование C99 6.5.3.3 Unary arithmetic operators /1
:
Операнд унарного оператора + или - должен иметь арифметический тип;
А, в 6.2.5 Types, /18
:
Целочисленные и плавающие типы вместе называются арифметическими типами.
Другими словами, унарный +
не позволяет использовать все другие типы данных в макросе, такие как указатели, комплексные числа или структуры.
И, наконец, причина ваша:
signed char + 78;
фрагмент не работает, потому что это не одно и то же. Этот начинает объявлять переменную типа signed char
, но задыхается, когда достигает значения +
, поскольку это недопустимое имя переменной. Чтобы сделать его эквивалентным вашему рабочему фрагменту, вы должны использовать:
(signed char) + 78;
, который является приведением значения +78
к типу signed char
.
И, согласно C99 7.8.14 Macros for integer constants /2
, вы также должны быть осторожны с использованием не констант в этих макросах, они не гарантированно работают:
Аргумент в любом случае этих макросов должен быть целой константой без суффикса (как
определено в 6.4.4.1) со значением, которое не превышает пределов для соответствующего типа.
6.4.4.1
просто указывает различные целочисленные форматы (десятичный / восьмеричный / шестнадцатеричный) с различными суффиксами (U
, UL
, ULL
, L
, LL
и эквивалентными строчными буквами, в зависимости от типа). Суть в том, что они должны быть константами , а не переменными.
Например, glibc
имеет:
# define INT8_C(c) c
# define INT16_C(c) c
# define INT32_C(c) c
# if __WORDSIZE == 64
# define INT64_C(c) c ## L
# else
# define INT64_C(c) c ## LL
# endif
, который позволит вашему макросу INT8_C
работать нормально, но текст INT64_C(val)
будет предварительно обработан в valL
или valLL
, ни того, ни другого, как вы бы хотели.