显然,我对宏如何运作有一个根本的误解。我认为宏只是导致预处理器用替换文本替换@define
d宏。但显然情况并非如此。我的代码如下:
的 TstBasInc.h
#pragma once
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <time.h>
// copy macro
#define Cpy(ToVar, FrmVar) do { \
errno = strncpy_s(ToVar, sizeof(ToVar), FrmVar, _TRUNCATE); \
if (errno == STRUNCATE) \
fprintf(stderr, "string '%s' was truncated to '%s'\n", FrmVar, ToVar); \
} while(0);
// clear numeric array macro
#define ClrNumArr(ArrNam, ArrCnt) \
for (s = 0; s < ArrCnt; s++) \
ArrNam[s] = 0;
uint32_t s; // subscript
typedef struct {
short C;
short YY;
short MM;
short DD;
} SysDat;
TstMacCmpErr:
#include "stdafx.h"
#include "TstBasInc.h" // test basic include file
#define ARRCNT 3
int main()
{
char Cnd = 'E'; // define to use 'else' path
char ToVar[7 + 1]; // Cpy To-Variable
int IntArr[ARRCNT]; // integer array
Cpy(ToVar, "short") // compiles with or without the semi-colon
if (Cnd != 'E')
// Cpy(ToVar, "short string"); // won't compile: illegal else without matching if
Cpy(ToVar, "short string") // will compile
else
Cpy(ToVar, "extra long string"); // compiles with or without the semi-colon
// the following code shows how I thought the macro would expand to
{ \
errno = strncpy_s(ToVar, sizeof(ToVar), "short str", _TRUNCATE); \
if (errno == STRUNCATE) \
fprintf(stderr, "string '%s' was truncated to '%s'\n", "short str", ToVar);; \
}
if (Cnd == 'E') {
ClrNumArr(IntArr, ARRCNT) // compiles with or without the semi-colon
printf("intarr[0] = %d\n", IntArr[0]);
}
else
printf("intarr[0] is garbage\n");
return 0;
}
结果如下:
string 'extra long string' was truncated to 'extra l'
string 'short str' was truncated to 'short s'
intarr[0] = 0;
正如评论所说,当Cpy(ToVar, "short string");
之后我有一个分号时,它甚至无法编译,因为我收到"C2181 illegal else without matching if"
错误。如您所见,我尝试按照建议in this post在宏中添加do-while,但它并没有任何区别。直接复制宏代码(即使没有do-while),代码工作正常。我原本以为只是在宏中添加大括号就可以解决问题,但事实并非如此。它必须与Cpy
结尾的if
有关,因为ClrNumArr
宏可以使用或不使用分号进行编译。那么有人可以告诉我为什么Cpy
宏不能替换文本吗?我必须遗漏一些简单的东西。
我正在使用VS 2015 Community Edition Update 1.
编辑:我记录了问题并将其隔离到(我认为)if
宏中的Cpy
语句。 仍然没有人解释为什么宏没有扩展我认为应该的方式。这应该是帖子的标题,因为我的问题不仅仅是分号的问题,我现在有了解决方案。
答案 0 :(得分:3)
如果你加了分号,
Cpy(ToVar, "short") // compiles with or without the semi-colon
if (Cnd != 'E')
// Cpy(ToVar, "short string"); // won't compile: illegal else without matching if
Cpy(ToVar, "short string") // will compile
else
Cpy(ToVar, "extra long string"); // compiles with or without the semi-colon
这使语法看起来像
if (...)
...;
; // <-- The problem here
else
...;
这是非法语法,因为else
在此语法中没有关联的if
。
OTOH,如果你写的话
if (...) {
...;
; // <-- no problem here, inside a block
}
else
...;
else
与之前的if
相关联。所以,一切都很好。
答案 1 :(得分:3)
如果你看一下扩展会发生什么,问题就变得很明显了。宏已经扩展到包含尾随分号的语句。
如果你将它与if
- else
结构一起使用,问题就会变得明显,因为如果你附加一个额外的分号,它会扩展为两个语句。
例如,如果您执行以下操作:
#define HELLO printf("Hello\n");
然后
if( some_condition )
HELLO;
else
something_else();
这会扩展到
if( some_condition )
printf("Hello\n");;
else
something_else();
你发现if
和else
之间有两个陈述,这意味着else
变得不合适。
使用do
- while(0)
的原因是创建一个适合一个语句适合的位置,然后必须在{{1}之后省略后面的分号}}。那就是你的定义应该是这样的:
while(0)
答案 2 :(得分:1)
在对Sourav的回答的评论中,你问为什么这可以作为一个宏体:
{
errno = strncpy_s(ToVar, sizeof(ToVar), "short str", _TRUNCATE);
if (errno == STRUNCATE)
fprintf(stderr, "string '%s' was truncated to '%s'\n", "short str", ToVar);;
}
那是因为在你的上下文中,它评估为:
if (Cnd != 'E')
{
errno = strncpy_s(ToVar, sizeof(ToVar), "short str", _TRUNCATE);
if (errno == STRUNCATE)
fprintf(stderr, "string '%s' was truncated to '%s'\n", "short str", ToVar);;
}
else
Cpy(ToVar, "extra long string"); // compiles with or without the semi-colon
块的if
部分,以前是单个语句,现在是一个块。所以现在else
已正确匹配。
然而,这仍然不是实现宏的好方法。然后包括分号(宏的用户通常期望工作)将导致错误。
if (Cnd != 'E')
Cpy(ToVar, "short string"); // invalid syntax
else
Cpy(ToVar, "extra long string");
你一直在说“为什么宏没有像我想象的那样扩展”。 正如您所想的那样扩展。根据您的原始定义,这个:
if (Cnd != 'E')
Cpy(ToVar, "short string");
else
Cpy(ToVar, "extra long string"); // compiles with or without the semi-colon
扩展到:
if (Cnd != 'E')
do{
errno = strncpy_s(ToVar, sizeof(ToVar), "short string", _TRUNCATE);
if (errno == STRUNCATE)
fprintf(stderr, "string '%s' was truncated to '%s'\n", "short string", ToVar);
} while(0);;
else
Cpy(ToVar, "extra long string"); // compiles with or without the semi-colon
while(0)
之后的第一个分号(它是宏的一部分)结束了if
语句的第一部分。第二个分号(不是宏的一部分)是另一个(空)语句。该语句被认为是在if
语句之外。因此,else
放错地方了。
如果你有类似的话,你会得到同样的错误:
if (Cnd != 'E')
do{
errno = strncpy_s(ToVar, sizeof(ToVar), "short string", _TRUNCATE);
if (errno == STRUNCATE)
fprintf(stderr, "string '%s' was truncated to '%s'\n", "short string", ToVar);
} while(0);
fprintf(stderr, "this is outside of the if statement\n");
else
Cpy(ToVar, "extra long string"); // compiles with or without the semi-colon
请注意,我将fprintf
调用缩进到逻辑所属的位置。这正是空语句(即额外分号)所属的地方。