когда использовать макрос в составном операторе - PullRequest
0 голосов
/ 08 июня 2018

Я пишу пример, чтобы показать мою проблему, здесь он использует вложенные макросы для выполнения вызовов, чтобы проверить, является ли число положительным и нечетным.Я знаю, что использовать этот вызов излишне, но, как я уже говорил ранее, он просто показывает, что при использовании макроса вложенным способом возникает некоторая проблема:

#include <stdio.h>
#include <stdlib.h>

#define num_is_positive_odd(num)                           \
({                                                         \
    int __rc = 0;                                          \
    int __num = (num);                                     \
                                                           \
    printf("%s:%d: check linenum\n", __func__, __LINE__);  \
    if (num_is_positive(__num) && num_is_odd(__num))       \
        __rc = 1;                                          \
    __rc;                                                  \
 })

#define num_is_positive(num)                  \
({                                            \
    int __rc = 0;                             \
    int __num = (num);                        \
                                              \
    if (__num > 0) {                          \
        printf("%s: number %d is positive\n", \
               __func__, __num);              \
        __rc = 1;                             \
    }                                         \
    __rc;                                     \
 })

#define num_is_odd(num)                       \
({                                            \
    int __rc = 0;                             \
    int __num = (num);                        \
                                              \
    if (__num / 2) {                          \
        printf("%s: number %d is odd\n",      \
               __func__, __num);              \
        __rc = 1;                             \
    }                                         \
    __rc;                                     \
 })



int main()
{
    int num = 4;

    if (num_is_positive_odd(num++))
        printf("%s: number %d is positive odd\n", __func__, num);

    exit(0);
}

При компиляции с использованием команды: gcc -Wunused-variable chk_comps.c

показывает ошибку:

chk_comps.c: In function ‘main’:
chk_comps.c:7:9: warning: unused variable ‘__num’ [-Wunused-variable]
     int __num = (num);                                     \
         ^
chk_comps.c:47:9: note: in expansion of macro ‘num_is_positive_odd’
     if (num_is_positive_odd(num++))
         ^

`

Может кто-нибудь помочь объяснить, почему и как это исправить?спасибо.

Ответы [ 3 ]

0 голосов
/ 08 июня 2018

При этом используется расширение GCC, называемое операторными выражениями - так что правила специфичны для GCC (и, вероятно, эмуляции Clang GCC).

Если вы запускаете gcc -E, вы можете увидетьчто необработанный вывод для main() (с добавленным мной void) равен:

# 41 "gccm43.c"
int main(void)
{
    int num = 4;

    if (({ int __rc = 0; int __num = (num++); printf("%s:%d: check linenum\n", __func__, 45); if (({ int __rc = 0; int __num = (__num); if (__num > 0) { printf("%s: number %d is positive\n", __func__, __num); __rc = 1; } __rc; }) && ({ int __rc = 0; int __num = (__num); if (__num / 2) { printf("%s: number %d is odd\n", __func__, __num); __rc = 1; } __rc; })) __rc = 1; __rc; }))
        printf("%s: number %d is positive odd\n", __func__, num);
    return 0;
}

, который при ручном форматировании (вариация на тему 'чистилища') может выглядеть следующим образом:

# 41 "gccm43.c"
int main(void)
{
    int num = 4;

    if (({ int __rc = 0;
           int __num = (num++);
           printf("%s:%d: check linenum\n", __func__, 45);
           if (({ int __rc = 0;
                  int __num = (__num);
                  if (__num > 0)
                  {
                      printf("%s: number %d is positive\n", __func__, __num);
                      __rc = 1;
                  }
                  __rc;
                }) &&
                ({ int __rc = 0; 
                   int __num = (__num);
                   if (__num / 2)
                   {
                       printf("%s: number %d is odd\n", __func__, __num);
                       __rc = 1;
                   }
                   __rc;
                 }
               ))
           __rc = 1;
           __rc;
         }
       ))
        printf("%s: number %d is positive odd\n", __func__, num);
    return 0;
}

Строки int __num = (__num); проблематичны;Вы инициализируете переменную с самим собой, что не очень хорошо работает (значение не определено до и после инициализации).Вы также использовали (__num / 2), чтобы определить, является ли __num нечетным, что является странным способом обнаружения странности;вы должны использовать (__num % 2).

Также теперь очевидно, почему компилятор предупреждает, что (одна из) __num (переменные) не используется.Внешнее объявление присваивает num++ __num, но инициализированная переменная никогда не используется, потому что внутренние вхождения int __num = (__num); относятся к себе, а не к внешнему __num, поэтому не used.

Вы бы лучше справились со статическими встроенными функциями - что-то вроде этого:

#include <stdio.h>
#include <stdlib.h>

static inline int num_is_positive(int num)
{
    int rc = 0;

    if (num > 0)
    {
        printf("%s: number %d is positive\n", __func__, num);
        rc = 1;
    }
    return rc;
}

static inline int num_is_odd(int num)
{
    int rc = 0;

    if (num % 2)        // BUG fixed
    {
        printf("%s: number %d is odd\n", __func__, num);
        rc = 1;
    }
    return rc;
}

static inline int num_is_positive_odd(int num)
{
    int rc = 0;

    printf("%s:%d: check linenum\n", __func__, __LINE__);
    if (num_is_positive(num) && num_is_odd(num))
        rc = 1;
    return rc;
}

int main(void)
{
    int num = 4;

    if (num_is_positive_odd(num++))
        printf("%s: number %d is positive odd\n", __func__, num);

    return 0;
}
0 голосов
/ 08 июня 2018

Для завершения обоих ответов Джонатана Леффера и ответа Ричи : если ваш действительный код требует выражений операторов и не может быть выполнен функциями static inline (это маловероятно и не относится к функциям, которые вы показали в своем вопросе), вам следует подумать о создании уникальных идентификаторов в макросах (чтобы сделать их более гигиеническими ).Возможный способ (специфичный для GCC!) Заключается в использовании cpp конкатенации с __COUNTER__ следующим образом:

#define num_is_positive_count(num,Count)     \
({                                           \
    int rc_##Count = 0;                      \
    int num_##Count = (num);                 \
                                             \
    if (num_##Count > 0) {                   \
        printf("%s:number %d is positive\n", \
               __func__, num_##Count);       \
        rc_##Count = 1;                      \
    }                                        \
    rc_##Count;                              \
 })

(Кстати, я избегаюидентификаторы, начинающиеся с _)

Тогда вам понадобится двойное косвенное указание

 #define num_is_positive_count2(num,Count) \
    num_is_positive_count(num,Count)
 #define num_is_positive(num) num_is_positive_count2(num,__COUNTER__)

Но вы лучше будете использовать функции static inline, когда сможете.Обычно это так!

КСТАТИ Оба выражения __COUNTER__ и являются расширениями GNU (принимаются GCC & Clang ), снаружи стандарта C11 n1570 .В некоторых случаях (не в вашем) вы можете использовать (если вы не хотите зависеть от GNU-ism __COUNTER__ ...) стандарт __LINE__ вместо GNU __COUNTER__ и принять соглашение для вызова ваших макросов по одному на строку (см. также this ).

0 голосов
/ 08 июня 2018

Вы выполняете следующее расширение макроса:

if (num_is_positive(__num) && num_is_odd(__num)) 

Оба эти расширения расширяются до выражений блока, которые включают объявление:

int __num = (num);  

Где num - параметр макроса длявнутренний макрос.Поскольку фактический аргумент макроса - __num, результирующий текст замены -

int __num = (__num);  

Область объявления C начинается с момента его завершения и до определения.Таким образом, оба __num в этом объявлении ссылаются на одну и ту же локальную переменную (которая поэтому инициализируется неинициализированным значением).Между тем, __num во внешнем блоке полностью затеняется __num во внутренних блоках и, как отмечает компилятор, не используется.Если бы вы указали -Wall вместо того, чтобы просто включить одно предупреждение, компилятор, вероятно, тоже предупредил бы вас о неинициализированной инициализации, которая могла бы быть другой подсказкой.

Кстати, имена, начинающиеся с двух подчеркиваний,зарезервированный;Вам не разрешено создавать их.Таким образом, ваши макросы демонстрируют неопределенное поведение.Это не ваша непосредственная проблема, но так как вам все равно придется изменить имена, чтобы наложить гигиена , вы могли бы также сделать это правильно.

Исправьте это с помощью static inline функцийвместо макросов.Шутки в сторону.Функции имеют предсказуемую область действия, в отличие от макросов, чьи расширения, как вы можете видеть, негигиеничны.Они требуют меньше обдумывания с вашей стороны, их легче отлаживать, читать и понимать, и они не замедляют цикл.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...