Background
I was writing code that uses functions from ctype.h to identify things in strings. I accidentally passed the string (char*) to the function(s) which take and int type, causing the program to segfault. It was easy enough to see that I forgot to dereference the string pointer, but GCC gave me no warnings even when compiling with the following arguments:

gcc -o main main.c -Wall -Wextra -Werror -pedantic -pedantic-errors -std=c99 -Wconversion

Debian GNU/Linux bookworm 12.5 x86_64gcc (Debian 12.2.0-14) 12.2.0都是最新的.下面是一个问题的例子:

/* main.c */
#include <ctype.h>
#include <stdio.h>

int main(void)
{
    char msg[] = "hello";
    int res = isspace(msg); // char* gets cast to int without warning
                            // It should be `isspace(*msg)`
                            // This also segfaults
    printf("%i\n", res);
    return 0;
}

Questions

  1. 我可以打开什么警告来获得这些指针到整数转换的编译时错误?
  2. 为什么这甚至是一开始的分割错误?

推荐答案

您传递的值超出了函数期望的值范围. 这样做会触发未定义的行为,如C standard中关于ctype. h中定义的函数的第7.4p1节所述:

头部ctype.h声明了几个有用的函数,用于分类<> 和映射字符.在所有情况下,参数都是int, 其价值应表示为unsigned char,或应 等于宏EOF的值.如果参数有任何其他值, 则行为未定义

由于这是未定义的行为,崩溃是一种可能的结果.

至于为什么编译器没有生成警告,我们需要查看预处理器的输出. 调用isspace在预处理器之后转换为以下内容:

int res = ((*__ctype_b_loc ())[(int) ((msg))] & (unsigned short int) _ISspace);

从这里,我们可以看到isspace被实现为一个宏,它使用一个带有给定参数作为索引的查找表,并且我们可以看到该参数被显式地转换为int. 这个明确的角色解释了为什么没有警告.

上面也解释了崩溃的原因,因为指针值可能远远超出了查找表的边界,因此试图访问它无法访问的内存.

C++相关问答推荐

如何将匿名VLA分配给指针?

在Windows上构建无聊的SSL x64

错误:C中需要参数声明符

两个连续的语句是否按顺序排列?

如何在C中通过套接字自定义数据类型读取原始变量?

将宏值传递给ARM链接器,该链接器将变量放置在特定位置

为静态库做准备中的奇怪行为

为什么此共享库没有预期的依赖项?

为什么我可以在GCC的标签后声明变量,但不能声明Clang?

#定义SSL_CONNECTION_NO_CONST

这段代码用于在C中以相反的顺序打印数组,但它不起作用

int * 指向int的哪个字节?

不带Malloc的链表

如何摆脱-WIMPLICIT-Function-声明

C中的数组下标和指针算法给出了不同的结果

为什么这个代码的最后一次迭代不能正常工作?

gdb - 你能找到持有内部 glibc 锁的线程吗?

macos/arm64 上地址空间不使用第一位吗?

为什么创建局部变量的指针需要过程在堆栈上分配空间?

C simd _m128 晶圆厂