C/C++文件指针的区别:读取同一个文件结果诡异

探讨C++与C在文件操作上的不同表现,特别是在文件位置指针的移动和末尾字符处理上的差异。通过对比分析,揭示两种语言在文件读写机制上的特点。

对于C/C++的文件操作,就是对指向FILE的指针©或者对fstream对象进行操作,此时有一个”文件位置指针“至关重要,这个是指示下一次读写操作所在字节的整数值。每次读写一个(或者1组)数据后,系统自动将文件位置指针移动到下一个读写位置上。
其实我是在用C++写一个简单的词法分析程序时候遇到的问题,废话少说,我们直接进入正题~~(也就是我遇到的问题【捂脸】)
在C++中对一个文本完全读写,分别打印读写前和读写后的文件位置指针(fstream流可以用tellg()函数来查看),我的代码如下:

# include <iostream>
# include <fstream>
using namespace std;
int main()
{
	char c;
	fstream f;
	f.open("test.txt",ios::in);
	if(!f) {
		cout << "Open file failed!";
		exit(1);
	}
	while(!f.eof()) {
		cout << f.tellg()<<' ';
		f.get(c);
		cout<< c<<' '<<f.tellg()<<endl;
	}
	f.close();
	return 0;
}

其中txt文件和源程序在相同文件夹中,截图如下(运行结果+TXT文件内容:文件以换行符结尾):
在两个疑惑点:
1.为啥那个文件位置指针从0直接跳到了2;
2.为啥最后一个字符’\n’打印了两遍
在两个疑惑点:1.为啥那个文件位置指针从0直接跳到了2;2.为啥最后一个字符'\n'打印了两遍
最后我把TXT文件最后一个换行符去掉,文件内容就是void Merge(),打印就正常了,如下
跳变问题解决了,但是最后依然把最后一个字符’)'打了两遍
最后依然把最后一个字符')'打了两遍
最后我想了一下原因可能是:最后那个文件指针到了尽头12的位置,就没法再移动了,所以就读取了’)’;
但是还有一个问题硬是不明白,我最后没办法了就用c相同的思路写了一下,代码如下:

# include <stdio.h>
int main()
{
	char c;
	FILE *f;
	if((f=fopen("test.txt","r"))!=NULL)
	{
		while(!feof(f)) {
			printf("%d ",ftell(f));
			c = fgetc(f);
			printf("%c %d\n",c,ftell(f));
		}
	}
 fclose(f);
 return 0;
}

运行结果:果然和C++不一样,没有从0直接跳到2,还有最后一个字符也就是换行符没有打印两遍,最后打印了一个空格
注意txt文件内容是以换行符结尾
注意txt文件内容是以换行符结尾
最后我也运行了上面C++的第二个例子,结果也不一样
注意这个txt文件结尾符是’)'
注意这个txt文件结尾符是')'
最后可以清楚的看到区别,最后字符没有打印两遍,无论是哪一种情况,都没有出现像c++一样的从0到2的跳变

以上就是我发现的问题,可能自己没有太弄懂,希望大佬们如果看到可以帮忙指出一下,在下感激不尽。(但是最后我还是用了c的文件操作来解决我的问题,因为C++那个跳变很让人无解,我还是相信c啊!!!

<think>在C++中,`fopen`函数的第一个参数为`NULL`(空指针)会导致未定义行为(Undefined Behavior)。根据C和C++标准,`fopen`要求第一个参数(文件路径)必须是有效的C字符串指针(即非空且以`'\0'`结尾)。传递`NULL`违反此约定,具体后果如下: ### 1. **未定义行为的具体表现** - **程序崩溃**:大多数实现中,`fopen`内部会尝试读取`NULL`指针指向的字符串,而访问空指针会触发段错误(Segmentation Fault)或访问冲突(Access Violation),导致程序立即终止。 - **返回`NULL`**:少数库实现可能进行空指针检查并返回`NULL`(如某些安全增强的库),但这不是标准行为,不可依赖。 - **不可预测结果**:编译器优化可能导致更诡异的错误(如跳转到随机地址执行代码)。 ### 2. **代码示例与验证** 以下代码演示了传递`NULL`给`fopen`的后果: ```cpp #include <cstdio> int main() { FILE* file = fopen(NULL, "rb"); // 危险:第一个参数为NULL if (file == NULL) { // 即使进入这里,也不代表安全:访问空指针可能已引发崩溃 printf("fopen failed\n"); } else { fclose(file); } return 0; } ``` **实际运行结果**(Linux/macOS): ``` Segmentation fault (core dumped) ``` **Windows**: ``` ##### 异常 0xC0000005 (访问冲突) ``` ### 3. **标准依据** - C标准(如C11 §7.21.5.3)明确要求`fopen`的第一个参数`filename`必须是字符串指针。传递`NULL`违反此约束,属于未定义行为[^1]。 - C++标准继承C的文件操作规范,同样适用此规则。 ### 4. **如何避免** - **检查路径指针**:在调用`fopen`前确保路径字符串非空。 ```cpp void safeOpen(const char* path) { if (path == nullptr) { // 显式检查空指针 fprintf(stderr, "错误:文件路径为NULL\n"); return; } FILE* file = fopen(path, "rb"); // ... 其他处理 } ``` - **使用`std::string`或`std::filesystem`**:C++17及以上推荐使用`std::filesystem::path`和`std::ifstream`/`std::ofstream`,它们提供更安全的接口: ```cpp #include <fstream> #include <filesystem> void safeOpenFS(const std::filesystem::path& path) { std::ifstream file(path); // 直接传递路径对象 if (!file.is_open()) { std::cerr << "文件打开失败: " << path << std::endl; } } ``` ### 5. **错误处理建议** - 若路径来自外部输入(如用户、网络),需同时验证: - 指针非空(`path != nullptr`)。 - 字符串内容有效(非空字符串且符合路径格式)。 - 结合`try-catch`(适用于C++流): ```cpp try { std::ifstream file("data.bin", std::ios::binary); if (!file) throw std::runtime_error("文件打开失败"); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } ``` ### 总结 **绝对不要将`NULL`作为`fopen`的第一个参数**!这会导致未定义行为,通常引发程序崩溃。始终在调用前验证路径指针的有效性,并优先使用C++的RAII文件流(如`std::fstream`)替代C风格API以提升安全性[^4]。 --- §§相关问题§§ 1. C++中如何安全地处理用户输入的文件路径? 2. `std::fstream`与C的`fopen`在错误处理机制上有何不同? 3. 为什么C++推荐使用RAII管理文件资源?</think>在C++中调用`fopen(NULL, "rb")`会导致**未定义行为(Undefined Behavior)**,具体表现取决于编译器和运行时环境。以下是详细分析: ### 1. **标准规定与未定义行为** - C/C++标准要求`fopen`的第一个参数必须是有效的C风格字符串(以`\0`结尾的字符数组)[^4]。 - 传递`NULL`指针违反了函数的前提条件,属于未定义行为(UB)。根据C++标准
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值