fgets,优先级问题导致Segmentation fault

本文通过一个实例分析了由于运算符优先级问题导致的Segmentation fault。代码中的错误在于误用if语句,使得fpin被赋值为NULL,而非打开文件的指针。修正if语句的括号后,问题得到解决, fgets能够正常从文件读取数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先,看段代码

 

 1 #include <stdio.h>

  2 
  3 int main()
  4 {
  5     char buffer[BUFSIZ+1];
  6     FILE *fpin;
  7 
  8     if((fpin = fopen("test","r") == NULL))
  9     {
 10         printf("can't open test.\n");
 11         return 1;
 12     }
 13 
 14     while(fgets(buffer,BUFSIZ,fpin) != NULL)
 15     {
 16         if(fputs(buffer,stdout) == EOF)
 17         {
 18             printf("fputs error to pipe.\n");
 19             return 1;
 20         }
 21     }

 22 }

这段代码运行会产生段错误:Segmentation fault

经过调试gdb调试发现段错误是第14行代码处 fgets(buffer,BUFSIZ,fpin) 产生的。

这里的fpin 是 NULL。

在编程中以下几类做法容易导致段错误,基本是是错误地使用指针引起的 
1)访问系统数据区,尤其是往 系统保护的内存地址写数据最常见就是给一个指针以0地址 
2)内存越界(数组越界,变量类型不一致等) 访问到不属于你的内存区域 

所以再查找fpin 是 NULL的原因

仔细看第8行代码

if((fpin = fopen("test","r") == NULL))

因为 == 的优先级高于 = 且  ==号是左结合,=是右结合,所以这句话等价于

fpin = ( fopen("test","r") == NULL )  

即 fopen的返回值 与 NULL做 逻辑比较运算,将返回的NULL 复制给fpin,

所以这句话并没有起到检查fpin指针的作用

故第8行代码应该为

if((fpin = fopen("test","r"))== NULL)


运行结果正常:

zyx@zyx-Lenovo:~/Desktop/pipe$ cat test 
hello,aa!
the second line!
i know i can!
you can too!
zyx@zyx-Lenovo:~/Desktop/pipe$ ./segment 
hello,aa!
the second line!
i know i can!
you can too!


另附C运算符优先级表:

C语言运算符分类
  1级优先级(左结合) 
  () 圆括号;[]下标运算符;->指向结构体成员运算符;. 结构体成员运算符。 
  2级优先级(右结合) 
  !逻辑非运算符;~按位取反运算符;++前缀增量运算符;--前缀减量运算符;+正号运算符;-负号运算符;(类型)类型转换运算符;*指针运算符;&地址运算符;sizeof长度运算符。 
  3级优先级(左结合) 
  *乘法运算符;/除法运算符;%取余运算符。 
  4级优先级(左结合) 
  +加法运算符;-减法运算符。 
  5级优先级(左结合) 
  <<左移运算符;>>右移运算符。 
  6级优先级(左结合) 
  <、<=、>、>=关系运算符。 
  7级优先级(左结合) 
  ==等于运算符;!=不等于运算符。 
  8级优先级(左结合) 
  &按位与运算符。 
  9级优先级(左结合) 
  ^按位异或运算符。 
  10级优先级(左结合) 
  |按位或运算符。 
  11级优先级(左结合) 
  &&逻辑与运算符。 
  12级优先级(左结合) 
  ||逻辑或运算符。 
  13级优先级(右结合) 
  ? :条件运算符。 
  14级优先级(右结合) 
  =、 +=、 -=、 *=、 /=、 %=、 &=、 ^=、 |=、 <<=、 >>=赋值运算符。 
  15级优先级(左结合) 
  ,逗号运算符。


### 原因分析 `fgets` 导致段错误的主要原因是传递给 `fgets` 函数的目标缓冲区未被正确分配或初始化。具体来说,在提供的代码中: ```c char *buf; buf = malloc(1 << 31); fgets(buf, 1024, stdin); ``` 这里存在两个潜在问题[^1]: 1. **内存分配失败**:如果 `malloc(1 << 31)` 返回的是 `NULL`,则说明未能成功分配所需的大块内存(可能由于系统资源不足)。此时调用 `fgets(buf, 1024, stdin)` 实际上传递了一个无效的指针 `NULL`,从而引发段错误。 2. **输入长度控制不当**:虽然 `fgets` 的第二个参数指定最大读取字符数为 `1024`,但如果实际分配的空间不足以容纳该大小的数据流,则仍然可能导致未定义行为。 另外一种情况是当试图向只读存储器或者非法地址空间写入数据时也会触发类似的异常状况[^4]。 ### 解决方案 针对上述提到的问题可以采取如下措施来预防此类错误发生: #### 方法一: 检查动态内存分配结果 在每次执行完 `malloc()` 后都应该立即验证其返回值是否有效 (即非 NULL),只有确认申请到了足够的连续字节之后才能继续后续操作。修改后的版本如下所示: ```c #include <stdio.h> #include <stdlib.h> int main(int argc, char **argv) { char *buf; buf = malloc((1 << 31)); if (!buf){ perror("Failed to allocate memory"); exit(EXIT_FAILURE); } fgets(buf, 1024, stdin); printf("%s\n", buf); free(buf); // Don't forget to release the allocated resource. return EXIT_SUCCESS; } ``` 通过增加简单的条件判断语句即可避免因为缺乏可用堆内存而导致崩溃的情况出现[^1]. #### 方法二: 调整请求尺寸至合理范围之内 考虑到现代操作系统对于单次进程所能获取的最大虚拟地址区间有所限制,因此建议适当减少所求区块规模以便于提高成功率并降低风险。例如改为更常见的数值如8MB 或者依据实际情况设定合理的上限值。 同时需要注意的是即使调整了数量级也应始终遵循第一条原则先测试再利用所得对象[^4]. ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值