【问题标题】:Using fopen_s in C在 C 中使用 fopen_s
【发布时间】:2014-06-09 19:03:18
【问题描述】:

我在使用 C 编程时遇到问题,尤其是在 Visual Studio 中使用 fopen 时。我阅读了fopen_s 函数并将以下行添加到我的项目中,但它仍然不起作用。

_CRT_SECURE_NO_WARNINGS

所以我尝试以这种方式使用fopen_s

FILE *fp;
errno_t err;
if ((err = fopen_s(&fp, "C:/File.txt", "rt")) != 0)
    printf("File was not opened\n");
else
    fprintf(fp, "Date: %s, Time: %s, Score: %i \n", __DATE__, __TIME__, score);
fclose(fp);

它仍然在崩溃。怎么了?

【问题讨论】:

  • 什么时候崩溃?视觉说什么?
  • “rt”应该代表什么[可能是错误]?这似乎与 MSDN/CRT 相关。试着通过检查你的错误号来找出你得到的错误(不仅仅是比较它是否不为零)。似乎为此目的提供了一个名为 strerror_s 的函数。可以试试吗?
  • “还是不行。”充其量是模糊的。无法打开文件?无法检测到打开文件失败?打开文件但关闭时崩溃?无法打开文件,报告错误,然后在关闭时崩溃(我正在用 hyde 对冲这个赌注)?你能至少稍微更具体一点吗?
  • 好的,抱歉。它是:“Janusze Kosmosu.exe 中 0x00BE347E 处未处理的异常:一个无效参数被传递给了一个认为无效参数致命的函数。如果有这个异常的处理程序,程序可以安全地继续。”
  • 当 fopen_s() 失败时,应输出相应的信息并退出程序,不要继续下一条语句。顺便说一句:变量 'score' 正在被使用,但没有在任何地方设置,为什么当您从未读取文件时还要打开文件进行读取?

标签: c visual-c++ fopen


【解决方案1】:

您使用 fclose 和无效的 fp 值,即使打开失败也是如此。至少在else 分支周围添加{}

许多开发人员认为在任何地方都使用大括号通常是个好主意,即使其中包含一个语句。如果您不这样做,即使对于经验丰富的开发人员,也很容易犯这样的错误。所以也将它们放在 then 分支周围。

【讨论】:

  • 然后分支?你是说else吗? (第二段)
  • @ryyker 其他分支需要它们,我建议将它们放在分支周围too
【解决方案2】:

_s 函数是不可移植的 Microsoft 发明,在大多数情况下,它复制了以更便携的名称存在的功能。此外,盲目地将函数的非_s 变体更改为_s 变体通常不会解决任何问题。 (例如,静默截断字符串比破坏堆栈的灾难性更小,但它仍然是可能被利用的不当行为。)

您的问题——不受fopenfopen_s 之间差异的影响——几乎可以肯定是您没有费心正确检查错误。以下是正确检查错误的方法:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(int argc, char **argv)
{
    FILE *fp;

    if (argc != 2) {
        fprintf(stderr, "usage: %s filename\n", argv[0]);
        return 2;
    }

    fp = fopen(argv[1], "rb");
    if (!fp) {
        fprintf(stderr, "opening %s: %s\n", argv[1], strerror(errno)); // HERE
        return 1;
    }

    // use 'fp' here...

    return 0;
}

注意标记// HERE 的行如何打印both传递给fopen 的确切字符串和strerror(errno) 的结果。每当系统调用失败时,打印参数和strerror(errno) 是绝对必要的。 (注意:如果您最终使用了返回错误代码的_s 函数之一,而不是设置errno,那么您必须将返回值传递给strerror。)

更改您的程序以执行此操作,您将能够找出为什么它不起作用。

【讨论】:

    【解决方案3】:

    //试一试,它正在工作。

    #include <stdio.h>
    #include <tchar.h>
    #include<iostream>
    using namespace std;
    int main()
    {
        int score = 10;
        FILE *fp;
        errno_t err;
        if ((err = fopen_s(&fp, "C:\\Users\\achea\\Desktop\\File.txt", "w+")) != 0)
        {
            printf("File was not opened\n");
        }
        else
        {
            fprintf(fp, "Date: %s, Time: %s, Score: %i \n", __DATE__, __TIME__, 
            score);
        }
        fclose(fp);
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      在 Windows 上,斜杠与 Linux 上的不同,因此您可以考虑使用 C:\File.txt 而不是 C:/File.txt。

      【讨论】:

        【解决方案5】:

        关于你使用的打开方式,

         err = fopen_s(&fp, "C:/File.txt", "rt")
        

        我从来没有使用过字母 t 作为参数,也不知道它是干什么用的……

        但是您正在使用 R(读取)打开并尝试写入...

        试试

         err = fopen_s(&fp, "C:/File.txt", "w")
        

         err = fopen_s(&fp, "C:/File.txt", "w+")
        

        此外,您实际上并不需要 err 变量。如果 fopen 调用失败,指针将为 NULL。

        【讨论】:

        • -1 告诉人们不要记录详细的错误代码。顺便说一句,“t”代表“文本”,与“b”代表“二进制”相对。这不是必需的(这是默认设置),但在某些情况下,它可以使期望更清晰。
        • -1 来解决他的问题?还是 -1 使代码更便携?如果他尝试在 Visual Studio 之外使用它,请告诉我他在哪里得到这个错误代码?据我记得签名是:“FILE * fopen (const char * filename, const char * mode);”而且我没有看到他将这个错误代码用于任何无法检查指针的事情。 -10 for ... 我最好不要告诉
        • 它是-1,专门用于“你并不真的需要 err 变量”。您可以通过检查指针是否为NULL 来知道fopen(_s) 失败,但是您需要err 变量才能知道为什么操作失败。 OP 的程序忽略了检查err,这是一个错误。你不应该鼓励他们让事情变得更糟;相反(如我的回答中所述),您应该已经解释了使用 strerror 和/或 perror 将错误代码转换为人类可读的诊断消息。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-01-19
        • 1970-01-01
        • 1970-01-01
        • 2015-04-25
        • 2021-06-19
        • 2017-10-26
        • 1970-01-01
        相关资源
        最近更新 更多