带循环的宏是如何在C/C++中实际工作的

How macros with loop actually work in C/C++

本文关键字:C++ 实际工作 循环      更新时间:2023-10-16

这是我直接从CImg库中获取的一段代码,试图了解它在中的实际工作方式

宏在第628行中定义为

#define cimg_for(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; )

CImg在9035 行中有一个类似的构造函数

template<typename t>
CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
     const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
  if (is_shared) {
    _width = _height = _depth = _spectrum = 0; _data = 0;
    throw CImgArgumentException(_cimg_instance
                                "CImg() : Invalid construction request of a (%u,%u,%u,%u) shared instance from a (%s*) buffer "
                                "(pixel types are different).",
                                cimg_instance,
                                size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
  }
  const unsigned int siz = size_x*size_y*size_z*size_c;
  if (values && siz) {
    _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
    try { _data = new T[siz]; } catch (...) {
      _width = _height = _depth = _spectrum = 0; _data = 0;
      throw CImgInstanceException(_cimg_instance
                                  "CImg() : Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
                                  cimg_instance,
                                  cimg::strbuffersize(size_x*size_y*size_z*size_c*sizeof(T)),size_x,size_y,size_z,size_c);
    }
    const t *ptrs = values + siz; cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);
  } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
}

我相信这就是宏的使用方式,但我希望有第二种意见

for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; )
     *ptrd = (T)*(--ptrs);

整个混乱是因为两个ptr变量

预处理器不知道C。它对令牌进行操作。据预处理器所知,forrof一样是一个令牌。

那么,宏后面的位呢?预处理器不知道这是for语句的一部分。一旦看到cimg_for(的闭合),就完成了。没有其他替代品。

在您的情况下,cimg_for(*this,ptrd,T)设置:

  • imgthis
  • ptrsptrd
  • T_ptrsT(ptr类型)

这段代码很奇怪,顺便说一句:如果你有C++,你就不需要这些宏技巧。

这看起来不像C,但以下是宏在C:中的工作方式

宏关键字在整个代码中进行搜索。宏关键字将在编译前替换为其定义。如果是带参数的宏,则传递的参数将替换为定义中的参数。

在您的情况下,

cimg_for(*this,ptrd,T)

将变成以下内容:

for (T * ptrd = (*this)._data + (*this).size(); (ptrd--)>(*this)._data; )

在写这篇文章的时候,我首先复制了定义,然后用*this替换了定义中的每个img,然后用ptrd替换了每个ptrs,最后用T替换了每个T_ptrd。这就是宏定义告诉我要做的,也是预处理器在编译之前要做的。

在这个宏之后,有一个语句,所以最后,循环看起来如下:

for (T * ptrd = (*this)._data + (*this).size(); (ptrd--)>(*this)._data; )
    *ptrd = (T)*(--ptrs);

宏不是函数。它们基本上是一个精心设计的"搜索和替换"。预处理器用宏的主体替换它找到宏的位置(在宏声明的范围内)。

一些gatchas:因为宏不像函数,所以像这样的东西会变得危险

#define SQUARE(A) A*A
int i = 2;
int j = SQUARE(++i);

k==9//oops!

或者这个

int i = SQUARE(IncrediblyExpensiveFuncionThatReturnsAnInt());

这个巨大的函数被调用了两次。宏中每个A一次

有关宏的危险及其工作方式的更多信息,请查看此

#define cimg_for(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size(); (ptrs--)>(img)._data; )
cimg_for(*this,ptrd,T) *ptrd = (T)*(--ptrs);

成为

for (T *ptrd = (*this)._data + (*this).size(); (ptrd--)>(*this)._data; )
  *ptrd = (T)*(--ptrs);