我想了解如何在我的代码中有效地使用C宏。有人可以指导我关于下面提到的代码吗?
#include<stdio.h>
#include<string.h>
void write_strongswan_conf(char buffer[1000],int buffer_size)
{
FILE *fp;
char delimiter[]="\n";
fp = fopen( "strongswan.txt" , "a" );
strcat(buffer,"\n");
fwrite(buffer, 1 ,buffer_size+1, fp );
printf("Data Written:%s",buffer);
fclose(fp);
}
int main ()
{
char wr_buffer[1000];
int wr_buffer_size =1;
strcpy(wr_buffer,"STRONG SWAN CONFIGURATION FILE");
wr_buffer_size = strlen(wr_buffer);
write_strongswan_conf(wr_buffer,wr_buffer_size);
strcpy(wr_buffer,"THIS IS THE SECOND LINE");
wr_buffer_size = strlen(wr_buffer);
write_strongswan_conf(wr_buffer,wr_buffer_size);
return(0);
}
这是一个将新字符串附加到文件末尾的小程序,我有大约25个字符串要添加到文件中。到目前为止,我必须重复编写下面提到的三行来完成它。如果我的理解是正确的,我们将能够通过为这三行实现宏来减少LOC。
有人可以指导我吗?
strcpy(wr_buffer,"STRONG SWAN CONFIGURATION FILE");
wr_buffer_size = strlen(wr_buffer);
write_strongswan_conf(wr_buffer,wr_buffer_size);
答案 0 :(得分:4)
这是我的镜头:
#define DO_SOMETHING(buf, str) \
do { \
const char *in = str; \
char *out = buf; \
const size_t len = strlen(in); \
strcpy(out, in); \
write_strongswan_conf(out, len);
} while (0)
以这种方式编写宏的原因是什么?
do {} while(0)
并非绝对必要,但这是在条件中正确扩展多语句操作的唯一方法。例如,if (condition) DO_SOMETHING(...)
。请参阅more elaborate answer了解相关信息。
创建临时变量的原因是每次宏参数出现在宏体中时都会对其进行求值。例如,使用PRINT_ME_TWICE(intval) printf("%d\n", intval); printf("%d\n", intval);
调用宏PRINT_ME_TWICE(i++)
将打印不同的值,因为intval
参数被计算两次,因此执行后增量运算符(++
)两次。您只能保证评估问题不会通过创建临时变量,为其分配参数以及再也不提及参数来实现。
注意:如果你不能保证输出缓冲区有足够的空间来保存结果字符串(加上NULL终结符),我反对使用strcpy
。< / p>
<强>更新强>:
正如@Bill Lynch在评论中所提到的,如果你有内部宏使用相同名称的变量,上面的版本可能会让你陷入困境。这称为Variable Shadowing。任何好的编译器都应该能够发出警告。例如,GCC警告您甚至在没有-Wall
或-Wextra
的情况下进行编译。目前,我们可以为宏做的最好的事情就是尝试通过在宏中添加前缀变量名来最小化阴影的风险。这可能是一个很好的做法,但它不是一个真正的解决方案。这就是它的样子:
#define DO_SOMETHING(buf, str) \
do { \
const char *DO_SOMETHING_in = str; \
char *DO_SOMETHING_out = buf; \
const size_t DO_SOMETHING_len = strlen(DO_SOMETHING_in); \
strcpy(DO_SOMETHING_out, DO_SOMETHING_in); \
write_strongswan_conf(DO_SOMETHING_out, DO_SOMETHING_len);
} while (0)
一个好的建议是尽可能用适当的函数替换宏,避免宏的所有晦涩。
答案 1 :(得分:3)
非常直接:
#define FOO(buf, str) \
strcpy(buf, str); \
write_strongswan_conf(buf, strlen(buf));
请注意,缓冲区大小参数是多余的,因此这里不需要它。
然后通过以下方式参考:
int main ()
{
char wr_buffer[1000];
FOO(wr_buffer, "STRONG SWAN CONFIGURATION FILE");
FOO(wr_buffer, "THIS IS THE SECOND LINE");
return(0);
}
如果您确实需要在宏中使用局部参数,则可以使用宏定义块:
#define FOO(buf, str) \
{ \
int bsize; \
strcpy(buf, str); \
bsize = strlen(buf); \
write_strongswan_conf(buf, bsize); \
}
编辑:感谢来自@jweyrich和@BillLynch的评论,他们指出了potential pitfalls with the above method,并且最好在宏中时如下呈现代码块:
#define FOO(buf, str) \
do { \
strcpy(out, str); \
write_strongswan_conf(buf, strlen(str)); \
} while (0)
块结构还有另一个好处,那就是如果你想在C中的复合语句中使用你的宏。如果你采用上面的第一个定义并执行此操作:
if ( some_check )
FOO(wr_buffer, "STRONG SWAN CONFIGURATION FILE");
由于if
仅适用于三个语句中的第一个,因此您无法获得预期的结果。当然,一个好的解决方案是始终阻止你的陈述:
if ( some_check ) {
FOO(wr_buffer, "STRONG SWAN CONFIGURATION FILE");
}
但您也可以在宏定义中使用模式do { ... } while (0)
,如上面第二个示例所示,这也可以避免此问题。
请注意,在宏定义中括起参数有时很重要。我之前没有这样做,因为它不是一个问题,但我们假设您有一个像这样的宏:
#define MUL(X, Y) (X * Y)
这看起来很无辜。我们来试试吧:
int a = 2;
int b = 3
int c;
c = MUL(a, b);
这会产生:
c = (a * b);
请注意,#define
s非常直观。括号包括在内。预处理器只是直接替换我定义的所有内容。如果我的表达更复杂:
c = MUL(a + 2, b + 3);
然后我得到:
c = (a + 2 * b + 3);
等等......这会给我一个相当于a + (2*b) + 3
的结果,这可能不是我想要的。所以我用括号更仔细地定义宏:
#define MUL(X, Y) ((X) * (Y))
现在上面的内容将扩展为:c = ((a + 2) * (b + 3));
。
顺便说一句,如果你在使用宏和/或其他#define
时遇到任何奇怪的行为,那么只运行.c
文件上的预处理器就可以很方便了。 #define
没有编译。一开始输出可能有点笨拙,但它很容易识别复杂宏的问题。您可以准确地看到它生成的内容。
在Unix / Linux中,您只需运行cpp
命令:cpp myfile.c > myfile.pre
或任何您想要调用该文件的命令。如果您正在使用Visual Studio,则可能会生成预处理文件。
为了记录,如果是我,我可能会把它变成一个功能,而不是宏。它当然只能节省一小部分执行时间,就像这样一个宏。
与编码的各个方面一样:仔细思考并仔细考虑您正在做的事情是非常重要的。 :)
答案 2 :(得分:1)
在我看来,没有为此代码调用宏。改为使用数组:
#include <stdio.h>
void write_strongswan_conf(const char *buffer)
{
FILE *fp = fopen("strongswan.txt", "a");
if (fp != 0)
{
fprintf(fp, "%s\n", buffer);
printf("Data Written: %s\n", buffer);
fclose(fp);
}
}
int main(void)
{
static const char *conf_string[] =
{
"STRONG SWAN CONFIGURATION FILE",
"THIS IS THE SECOND LINE",
};
for (int i = 0; i < sizeof(conf_string) / sizeof(conf_string[0]); i++)
write_strongswan_conf(conf_string[i]);
return(0);
}
我很高兴地假设每次写入字符串时打开和关闭文件是明智的。我不相信这种情况,但原始代码做出了假设。 (打开文件是相当昂贵的;你不要无偿地做,这对我来说有点无偿。)
write_strongswan_conf()
函数中的代码使用fprintf()
代替fwrite()
,因为您只是在编写字符串,这样可以避免将换行连接到缓冲区,并且因此减少了制作的副本数量。事实上,你甚至不需要知道弦的长度; fprintf()
将为您计算。
结果代码中的任何地方都无法从转换为宏中受益;没有足够大的重复块。初始化的字符串数组可以占用25行,如果您愿意,也可以使用其他数据源。您可以从另一个文件中读取行,或让用户键入它们,或者程序生成它们。它不依赖于1000个字符的行(给定或取 - 998将是原始代码中字符串的最大安全长度)并且可以处理更长的行。它会错误地检查fopen()
调用,如果fopen()
失败,则不会尝试使用空文件指针进行写操作;这可以避免崩溃。