下面的一段代码给出了一个警告,用于从指向int的指针进行强制转换
int foo[] = {1, 2, 3};
#define bar(x) foo[(int)(x) % 3]
char *baz = "quux";
bar(baz);
另一方面,以下代码编译时没有任何警告(无论它是否可能导致运行时错误)
#include <ctype.h>
// some code
char *baz = "quux";
isalpha(baz);
当我打开ctype.h来查看isalpha
时,我发现它是一个使用其他几个宏的宏
# define _ISbit(bit) (1 << (bit))
enum
{
// some identifiers
_ISalpha = _ISbit (2),
// some identifiers
};
extern const unsigned short int **__ctype_b_loc (void)
__THROW __attribute__ ((__const__));
# define __isctype(c, type) \
((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
# define isalpha(c) __isctype((c), _ISalpha)
正如您可能看到的,扩展isalpha
宏的结果仍然明确地将指针baz
强制转换为int
。但是,编译时不会发出任何警告。显然,这两段代码执行相同的操作(即,将char *
转换为int
),但是一个给出警告而另一个没有。为什么呢?
注意:使用具有相同选项的编译命令来编译包含这些代码段的程序。
编译器版本:
gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
答案 0 :(得分:9)
第一个程序不会对gcc
4.7
发出警告,但会对版本4.8
发出警告。
第二个程序不会发出警告,因为宏定义位于系统头中。添加-Wsystem-headers
以获取第二个程序的警告。
来自gcc文档(强调我的)
-Wsystem报头
打印系统头文件中找到的构造的警告消息。 来自系统标题的警告通常被禁止,在 假设他们通常不表示真正的问题和 只会使编译器输出更难阅读。
答案 1 :(得分:4)
C标准允许将任何标准库函数另外实现为宏。这种宏定义(N1570第7.1.4节)的要求是:
标头中声明的任何函数都可以另外实现为a 标题中定义的类似函数的宏,所以如果库函数是 在包含标题时明确声明,这是一种技术 如下所示可用于确保声明不受影响 这样的宏。可以抑制函数的任何宏定义 本地通过将函数的名称括在括号中,因为 然后,该名称后面没有表示的左括号 扩展宏功能名称。出于同样的语法原因,它 允许获取库函数的地址,即使它是 也定义为宏。使用#undef删除任何宏 定义还将确保引用实际功能。 作为宏实现的库函数的任何调用 应扩展到完全评估其每个参数的代码 曾经,必要时用括号完全保护,所以它是 通常使用任意表达式作为参数是安全的。同样, 以下子条款中描述的那些类似函数的宏可以 在具有兼容性的函数的任何地方的表达式中调用 可以调用return类型。
诸如isalpha()
之类的库函数的宏定义必须能够正确地用于正确的参数,但诊断错误参数需要 。由于C标准明确允许宏定义,如果实现提供了这样的宏,那么就没有函数调用,因此禁止将char*
参数传递给函数的约束期待int
不适用。
如果存在实际的函数调用,则将char*
参数传递给期望int
参数的实际函数是违反约束的,需要诊断。没有从char*
到int
的隐式转换。
这是一个说明问题的小程序:
#include <ctype.h>
int main(void) {
char *p = "hello";
isalpha(p); // line 4, possible macro invocation
(isalpha)(p); // line 5, actual function call
#undef isalpha
isalpha(p); // line 7, actual function call
}
这是用gcc -std=c11 -pedantic
(gcc 4.8.2)编译它的结果:
c.c: In function ‘main’:
c.c:5:5: warning: passing argument 1 of ‘isalpha’ makes integer from pointer without a cast [enabled by default]
(isalpha)(p); // line 5, actual function call
^
In file included from c.c:1:0:
/usr/include/ctype.h:111:1: note: expected ‘int’ but argument is of type ‘char *’
__exctype (isalpha);
^
c.c:7:5: warning: passing argument 1 of ‘isalpha’ makes integer from pointer without a cast [enabled by default]
isalpha(p); // line 7, actual function call
^
In file included from c.c:1:0:
/usr/include/ctype.h:111:1: note: expected ‘int’ but argument is of type ‘char *’
__exctype (isalpha);
^
在第5行,isalpha
周围的括号阻止了宏扩展,暴露了函数本身。在第7行,实际函数被公开,因为宏定义已被删除。
实际的函数调用执行隐式转换,而不是强制转换;由于没有从char*
到int
的隐式转换,编译器会发出诊断信息。 (它可能发出一个致命的警告,但gcc对隐式转换有些松懈,尽管警告确实满足标准的要求。)使用宏,转换由显式转换运算符执行,编译器不会通过它来警告默认值。
请注意,根据实现,所有三个调用都可以是实际的函数调用。标准库函数的宏定义是可选。 GNU C库为isalpha
提供了宏定义,因为宏定义可以比函数调用更有效(尽管内联函数可能同样有效)。