用 C 宏替换 strncmp 函数中的参数

phi*_*hil 3 c

使用以下 c 宏替换 strncmp(可能还有任何其他函数)中的参数似乎存在问题:

#define STRING_WITH_SIZE_MINUS_ONE(x)   x, sizeof(x)-1

...

if (0 == strncmp(str1, STRING_WITH_SIZE_MINUS_ONE("str2")) {
// do something
}

Run Code Online (Sandbox Code Playgroud)

当使用arm-linux-gnueabihf-gcc 4.9.2构建时,这会抛出错误error : macro "strncmp" require 3 argument, but only 2给定的。使用arm-linux-gnueabihf-gcc 8.3.0 构建似乎工作得很好。

显然,整个 strncmp 函数可以放在宏内部,并且它可以很好地工作,但是理解为什么它不能编译是非常有益的。

nie*_*sen 6

问题是, ifstrncmp()被实现为宏并且预处理器strncmp()if- 语句中看到,那么它将尝试扩展strncmp(str1, STRING_WITH_SIZE_MINUS_ONE("str2"))而不首先扩展STRING_WITH_SIZE_MINUS_ONE,因此只有 2 个参数,因此会出现错误。

有不同的方法,但我认为最简单的方法是放弃一般性STRING_WITH_SIZE_MINUS_ONE并为函数调用编写一个包装宏:

// Call strncmp with a string literal as 2nd argument
#define my_strncmp(s1, s2) strncmp(s1, s2, sizeof(s2)-1)

// Example
#include <stdio.h>
#include <string.h>

#define my_strncmp(s1, s2) strncmp(s1, s2, sizeof(s2)-1)

int main()
{
    const char *str1 = "str2 or str1";
    if (0 == my_strncmp(str1, "str2")) {
        printf("Equal\n");
    } else {
        printf("Not equal\n");
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

无论哪种情况,都有一个警告:只要第二个参数是字符串文字,这就能正常工作,但是当有人将其与 achar *一起使用时,事情将无法正常工作,编译器也不会意识到这一点,因此很可能导致程序错误,需要长时间调试才能找到:

    const char * str2 = "str2";
    if (0 == my_strncmp(str1, str2)) {  <--- Careful, only works with a string literal
Run Code Online (Sandbox Code Playgroud)

仅出于这个原因,我一开始就不会推荐这种方法。


Wei*_*hou 6

摘自标准草案(N3096),7.1.4,库函数的使用

标头中声明的任何函数都可以另外实现为标头中定义的类似函数的宏,因此,如果在包含标头时显式声明库函数,则可以使用下面显示的技术之一来确保声明不被显式声明。受这样一个宏的影响。通过将函数名称括在括号中,可以在本地抑制函数的任何宏定义,因为该名称后面不会跟着指示宏函数名称扩展的左括号。出于相同的语法原因,允许获取库函数的地址,即使它也被定义为宏。1使用#undef 删除任何宏定义也将确保引用实际函数。

在你的例子中,strncmp是你的实现中的一个函数和一个宏,并且该宏strncmp有 3 个参数。当扩展一个类似函数的宏时,参数的数量在任何扩展之前就被确定并与宏定义相匹配,因此对于strncmp带有三个参数的宏,您必须在宏参数列表中传递三个由两个逗号分隔的参数,除非逗号括在括号内。如果strncmp是宏,strncmp(str1, STRING_WITH_SIZE_MINUS_ONE("str2"))就是语法错误。STRING_WITH_SIZE_MINUS_ONE甚至在检查 的定义之前就会生成该错误。

在较新的版本中,他们可能决定删除该宏,因此这不再是问题。您始终可以通过在周围添加一对括号来抑制宏版本strncmp

将其更改为

(strncmp)(str1, STRING_WITH_SIZE_MINUS_ONE("str2"))
Run Code Online (Sandbox Code Playgroud)

旁注:应该注意的是,您的宏仅适用于已知大小的数组(具有编译时常量大小或 VLA 的数组,但不适用于未知大小的数组,例如arr[]),其中字符串文字是一种特殊情况。如果将 a 传递const char*给宏,您的代码将编译,但在运行时具有 UB。您可以考虑使用strlen()替代方法来避免此问题。


1这意味着需要一个实现来为每个库函数提供一个实际的函数,即使它还为该函数提供了一个宏。