为什么我没有得到一个指向int的指针的警告?

时间:2014-12-31 16:28:37

标签: c pointers casting int

下面的一段代码给出了一个警告,用于从指向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

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提供了宏定义,因为宏定义可以比函数调用更有效(尽管内联函数可能同样有效)。