Подтвердить что ты не робот

Что это за таинственный макрос плюс знак в 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))

В чем смысл или значение этого знака плюс?

4b9b3361

Ответ 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

Это унарный плюс.

Есть только две вещи, которые он может сделать.

  • Он применяет то, что известно как обычные арифметические преобразования к операнду. Это устанавливает общий реальный тип для результата. Я не могу придумать никаких причин, чтобы заставить это, если результат вот-вот будет ввергнут в нечто конкретное.

  • Он ограничивает операнд типами, для которых определены арифметические операторы. Это похоже на более вероятную причину.