Почему использование макроса в моем коде приводит к ошибкам?

Я написал макрос, который суммирует количество включенных нечетных индексированных битов.

Несколько примеров:

В переменной, которая содержит следующие биты:

10010101 

Включен только бит с индексом 7, включен только 1 бит с нечетным индексом, поэтому ответ равен 1.

В переменной, которая содержит следующие биты:

00101011 

Бит с индексом 1 включен, бит с индексом 3 включен и бит с индексом 5 включен, и, следовательно, всего 3 нечетных индексированных бита, которые включены, поэтому ответ равен 3.

Я написал основную функцию для проверки этого макроса.

Вот вся программа:

#include <stdio.h>

#define ODD(n)\
 int count = 0;\
 int i = 1; \
while(n>>i)\
{\
    if( ((n>>i)%2) == 1)\
        count++;\
        i = i+2;\
}\




int main()
{
  int i;
  int j;
  int array1[] = {1,2,3,4};
 /*array1 contains (binary representation):
  00000001
  00000010
  00000011
  00000100
  After passing these into the macro:
  00000001 ----> 0 odd indexed bits are turned on
  00000010 ---->1 odd indexed bit is turned on
  00000011 ---->1 odd indexed bit is turned on
  00000100 ----> 0 odd indexed bits is turned on

 */
int array2[4];
for(i=0; i<4; i++)
{
    array2[i] = ODD(array1[i]);
}

for(j=0; j<4; j++)
{
    printf("Array2: %d\n",array2[j]);
}




  return 0;
}

Я понятия не имею, почему я получил следующие ошибки:

odd.c: In function ���main���:
odd.c:4:5: error: expected expression before ���int���
 int count = 0;\

odd.c:34:19: note: in expansion of macro ���ODD���
   array2[i] = ODD(array1[i]);
               ^

odd.c:8:13: error: ���count��� undeclared (first use in this function)
         count++;\
         ^

count объявлен, поэтому я не знаю, в чем проблема.

Почему я получил эти ошибки и как я могу их исправить?


person Tree    schedule 26.02.2017    source источник


Ответы (1)


Вы получили ошибку, потому что макрос не является функцией. Это механизм расширения токена, и он расширяется до следующей чепухи:

array2[i] = int count = 0;
while(array1[i]>>1)\
{\
    if( ((array1[i]>>1)%2) == 1)\
        count++;\
}\

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

int odd(int n) {
  int count = 0;
  while(n>>1)
  {
    if( ((n>>1)%2) == 1)
        count++;
  }
  return count;
}

Если вы настаиваете на написании этого с помощью макроса, вам нужно будет его реструктурировать:

#define ODD(n, res) do { \
  int count = 0; \
  while(n>>1)\
  {\
    if( ((n>>1)%2) == 1)\
      count++;\
  }\
  (res) = count;\
} while(0)

Чтобы определить переменную, вы должны ввести область действия, поэтому для этого добавляется цикл do while. У этой конкретной конструкции цикла есть приятная особенность: она не выдает предупреждения о пустых операторах, когда вы пишете терминатор оператора после вызова макроса (ODD(...);).

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

ODD(array1[i], array2[i]);
person StoryTeller - Unslander Monica    schedule 26.02.2017
comment
Большое спасибо! @Рассказчик - person Tree; 26.02.2017
comment
@Tree - как подсказывает мое редактирование. А также помните, что правильное смещение целых чисел со знаком имеет результаты, определенные реализацией. - person StoryTeller - Unslander Monica; 26.02.2017
comment
Спасибо. Дело в том, что мне интересно написать это как макрос. Как я могу этого добиться? @Рассказчик - person Tree; 26.02.2017
comment
@Tree - Почему вы хотите написать это как макрос? - person StoryTeller - Unslander Monica; 26.02.2017
comment
Я хотел потренироваться и наткнулся на это упражнение, и мне любопытно @StoryTeller - person Tree; 26.02.2017
comment
@Tree - Хорошо, я написал это, если вы хотите с этим поработать. Просто помните, что когда вы пишете реальные приложения, отдавайте предпочтение функциям на первом и втором месте. - person StoryTeller - Unslander Monica; 26.02.2017
comment
Большое спасибо! @StoryTeller Я хотел бы кое-что спросить. Значит, переменные в макросах нужно объявлять внутри блока? Я немного запутался, оператор array2[i] = ODD(array1[i]); на самом деле array2[i] = do { \ int count = 0; \ while(n››1)\ {\ if( ((n››1)%2) == 1)\ count++;\ }\ (res) = count; } while(0) Как получится, что res будет храниться в массиве2[i]? Разрешен ли цикл while внутри макросов? - person Tree; 26.02.2017
comment
@Tree - запустите свой код только через препроцессор. Для gcc нужно просто вызвать его с помощью gcc -E myfile.c. Вы увидите, что я имею в виду под расширением макроса, и как res является array2[i]. Я также исправил часть расширения в вашем ошибочном коде, чтобы вы могли видеть, как там заменено n. - person StoryTeller - Unslander Monica; 26.02.2017
comment
Спасибо. Что вы имеете в виду, хотя препроцессор в одиночку? Почему мы пишем это: в то время как (0), мне кажется, что код никогда не будет выполняться таким образом, так как 0 как ложь. Где моя ошибка? Я был бы рад, если бы вы могли подробнее рассказать об изменениях, которые были сделаны, так как я не очень понимаю их. @Рассказчик - person Tree; 26.02.2017
comment
@Tree - Ваша ошибка, похоже, заключается в том, что вы учитесь мастерить, а не из хорошо структурированной книги или учебника. Препроцессор — это первый этап компиляции C-файла. А цикл — это цикл do while, который всегда выполняет первую итерацию и только потом проверяет условие. - person StoryTeller - Unslander Monica; 26.02.2017
comment
Моя ошибка не из-за ковыряния, я знаю, что такое процессор C, и я знаю, что такое цикл do while. Чего я не понимаю, так это почему были сделаны эти модификации @StoryTeller - person Tree; 26.02.2017
comment
@Tree - если вы хотите понять масштаб, удалите его. Затем напишите простую программу, которая дважды вызывает макрос в одной и той же области видимости, что-то вроде ODD(...); ODD(...);. Как только вы поймете, почему использование области действия является хорошей идеей, попробуйте другие конструкции области действия и увеличьте предупреждения вашего компилятора (для gcc -Wall -Wextra -pedantic -Werror), и вы поймете, почему я выбрал цикл do while. Ноль существует с единственной целью — запустить код один раз, а не в цикле. - person StoryTeller - Unslander Monica; 26.02.2017