Что это за таинственный макрос плюс знак в stdint.h?
Посмотрите мой код:
#include <stdint.h>
int main(int argc, char *argv[])
{
unsigned char s = 0xffU;
char ch = 0xff;
int val = 78;
((int8_t) + (78)); /*what does this mean*/
INT8_C(val); /*equivalent to above*/
signed char + 78; /*not allowed*/
return 0;
}
Я обнаружил, что определение макроса в <stdint.h>
:
#define INT8_C(val) ((int8_t) + (val))
В чем смысл или значение этого знака плюс?
Ответы
Ответ 1
Отрывок:
((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
, вы также должны быть осторожны с использованием неконстантов в этих макросах, они не гарантированно работают:
Аргумент в любом экземпляре этих макросов должен быть константой unsuffixed integer (as определенная в пункте 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
, ни один из которых вам не нужен.
Ответ 2
Кажется, что все упустили точку унарного оператора плюса здесь, чтобы сделать результат действительным в директивах препроцессора #if
. Дано:
#define INT8_C(val) ((int8_t) + (val))
директивы:
#define FOO 1
#if FOO == INT8_C(1)
развернуть как:
#if 1 == ((0) + (1))
и, следовательно, работать как ожидалось. Это связано с тем, что любой символ un #define
d в директиве #if
расширяется до 0
.
Без унарного плюса (который становится двоичным знаком в директивах #if
), выражение недействительно в директивах #if
.
Ответ 3
Это унарный оператор +
. Он дает значение его операнда, но его можно применять только к арифметическим типам. Цель здесь состоит в том, чтобы предотвратить использование INT8_C
выражения выражения типа указателя.
Но выражение выражения
INT8_C(val);
имеет поведение undefined. Аргумент INT8_C()
должен быть "неопределенной целочисленной константой... со значением, которое не превышает пределы для соответствующего типа" (N1256 7.18.4). Цель этих макросов - позволить вам написать что-то вроде INT64_C(42)
и расширить его до 42L
, если int64_t
есть long
или 42LL
, если int64_t
длиннее и т.д.
Но записывая либо
((int8_t) + (78));
или
((int8_t) + (val));
в вашем собственном коде совершенно законно.
ИЗМЕНИТЬ
Определение для INT8_C()
в вопросе:
#define INT8_C(val) ((int8_t) + (val))
действителен в C99, но не соответствует требованиям в соответствии с более поздним проектом N1256 в результате изменения одной из технических исправлений.
В оригинальном стандарте C99, раздел 7.18.4.1p2, говорится:
Макрос INT *** N * _C ** (значение) должен расширяться до знаковой целочисленной константы с специфицированное значение и тип int_least *** N * _t **.
N1256 изменил это на (пункт 3):
Каждый вызов одного из этих макросов должен расширяться до целого числа постоянное выражение, подходящее для использования в директивах предварительной обработки #if. Тип выражения должен иметь тот же тип, что и выражение соответствующего типа, преобразованного в соответствии с целые рекламные акции. Значение выражения должно соответствовать значению аргумент.
Это изменение было сделано в ответ на Отчет об ошибках # 209.
EDIT2: Но см. ответ R..; это действительно справедливо в N1256, по довольно неясным причинам.
Ответ 4
Это унарный плюс.
Есть только две вещи, которые он может сделать.
-
Он применяет то, что известно как обычные арифметические преобразования к операнду. Это устанавливает общий реальный тип для результата. Я не могу придумать никаких причин, чтобы заставить это, если результат вот-вот будет ввергнут в нечто конкретное.
-
Он ограничивает операнд типами, для которых определены арифметические операторы. Это похоже на более вероятную причину.