Что это за загадочный макрос плюс в stdint.h? - PullRequest
19 голосов
/ 04 октября 2011

Пожалуйста, смотрите мой код:

#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))

Каково значение или значение этого знака плюс?

Ответы [ 4 ]

27 голосов
/ 04 октября 2011

Фрагмент:

((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, ни того, ни другого, как вы бы хотели.

18 голосов
/ 04 октября 2011

Кажется, что все здесь упустили точку унарного оператора плюс, который должен сделать результат действительным в директивах препроцессора #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.

10 голосов
/ 04 октября 2011

Это унарный оператор +. Он возвращает значение своего операнда, но его можно применять только к арифметическим типам. Цель здесь - предотвратить использование INT8_C в выражении типа указателя.

Но ваше высказывание выражение

INT8_C(val);

имеет неопределенное поведение. Аргумент INT8_C() должен быть «нефиксированной целочисленной константой ... со значением, которое не превышает пределы для соответствующего типа» ( N1256 7.18.4). Цель этих макросов - позволить вам написать что-то вроде INT64_C(42) и расширить его до 42L, если int64_t равно long, или до 42LL, если int64_t это `long long, и так далее.

Но пишу либо

((int8_t) + (78));

или

((int8_t) + (val));

в вашем собственном коде совершенно законно.

EDIT

Определение для 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 по совершенно неясным причинам.

0 голосов
/ 04 октября 2011

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

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

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

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

...