【问题标题】:Can I replace a call to open_memstream with a malloc and an implicit cast?我可以用 malloc 和隐式转换替换对 open_memstream 的调用吗?
【发布时间】:2012-05-05 12:19:52
【问题描述】:

全部,

我有一个打印到流的程序。我需要在内存中缓冲这个流,然后根据需要将每一行打印到实际文件中。

由于fprintf() 函数调用必须有一个FILE * 指针,我需要在内存中说指针寻址空间。我曾使用过open_memstream() 函数,但Windows 不支持此功能。

由于malloc() 返回一个void * 指针,该指针根据需要神奇地转换为必要的指针,我可以将它用作我的FILE * 指针吗?如果是这样,有什么注意事项?我需要注意空间不足吗?

更新:

在找到open_memstream() 的来源后,这比本来应该的要难,看起来他们正在对 malloc 的空间进行文件流。

既然是这样,而且我已经得到了他们的源代码,我将确定我是否无法获得一个工作版本来使用 mingw 为 windows 交叉编译。

【问题讨论】:

标签: c windows stream malloc


【解决方案1】:

对于那些追随我的人,有希望!有一个解决方案。正如我的问题所述,我使用的是open_memstream(),它在 Windows 上不受支持。

由于我有一个File * 指针(不能更改为char *),我需要将它重定向到内存,直到稍后。由于我正在处理内存中的文件,因此我查看了mmap()。它可以轻松解决问题,但同样,它只是 linux。

但是,Windows 包含一个对 mmap() 的推论,称为 MapViewOfFile()。通过#ifdef 的魔力,我可以根据需要使用它:

#ifdef WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#endif

稍后,在 main 方法中,我调用了两个平台都支持的 tmpfile()。这将为我打开一个流到一个有保证的唯一临时文件的流。现在我有了FILE * 指针,我需要mmap() 空间。但是mmap()需要的是文件描述符,而不是流,所以我使用fileno()函数来获取新的文件描述符。

/* create tmp file and get file descriptor */
int fd;
yyout = tmpfile();
fd = fileno(yyout);

现在我有更多#ifdef 代码来确定需要使用哪个内存映射代码集。请注意两个版本之间映射空间的差异。 Windows 映射 16384 bytes 和 linux 映射 4096 bytes。这是因为较小的值会在 Windows 上出现段错误,如 my question here 中所述。

#ifdef WIN32
    HANDLE fm;
    HANDLE h = (HANDLE) _get_osfhandle (fd);

    fm = CreateFileMapping(
             h,
             NULL,
             PAGE_READWRITE|SEC_RESERVE,
             0,
             16384,
             NULL);
    if (fm == NULL) { 
            fprintf (stderr, "%s: Couldn't access memory space! %s\n", argv[0],  strerror (GetLastError()));
            exit(GetLastError());
    }
    bp = (char*)MapViewOfFile(
              fm,
              FILE_MAP_ALL_ACCESS,
              0,
              0,
              0);
    if (bp == NULL) { 
            fprintf (stderr, "%s: Couldn't fill memory space! %s\n", argv[0],  strerror (GetLastError()));
            exit(GetLastError());
    }
#else
    bp = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0);
    if (bp == MAP_FAILED) {
            fprintf (stderr, "%s: Couldn't access memory space! %s\n", argv[0], FileName, strerror (errno));
            exit(errno);
    }
#endif

现在发生了一堆工作,将数据发送到yyout 流。最终,flushData() 方法被调用。它以空终止字符结束流,刷新它,然后倒回它。然后,指向内存空间的指针通过一个函数指针,以及要打印到的正确流。

void flushData(void) {
    /* write out data in the stream and reset */ 
    while (currFields < headerFields) { fprintf(yyout, ",\"\""); currFields++; } 
    currFields = 0;
    fprintf(yyout, "%c%c%c", 13, 10, '\0');
    fflush(yyout);
    rewind(yyout);
    if (faqLine == 1) {
        faqLine = 0; /* don't print faq's to the data file */
    }
    else {
        (*printString)(outfile, bp);
        fflush(outfile);
    }
    fflush(yyout);
    rewind(yyout);
}

这是可以指向打印的函数之一。它遍历内存空间并打印每个字符,直到遇到之前打印的 null。

int printAnsi( FILE *outstream, char *string) {
    /* loop over the chars in string and print them to the outputstream as ansi */
    char * ps = string;
    while (*ps != '\0') {
        fprintf(outstream, "%c", *ps);
        ps++;
    }
    return 0;
}

所有这一切的最终结果是我有一个流到内存空间的流,就像open_memstream() 一样,还有一个 char 指针,我可以在必要时使用它来遍历内存空间。它是跨平台的,并且(看似)功能齐全。

如果有人想了解更多详细信息或对我应该解决的问题有注释,请添加评论。

【讨论】:

  • 不错!在研究同样的问题时,我还遇到了这个麻省理工学院许可的库,它看起来基本上是做同样的解决方案,尽管它试图跨平台并检测用户环境可用github.com/Snaipe/fmem/blob/master/src/fmem-winapi-tmpfile.c
  • 完成后,您是否调用UnmapViewOfFile 或除fclose 之外的任何其他清理函数?
【解决方案2】:

没有。 malloc() 只是给你一块(可能未初始化的)内存。没有“神奇”的演员阵容;当您执行 int * buf = malloc(10*sizeof(int); 时,您实际上将 buf 指向 10 个未初始化的整数。

与 FILE 对应的东西是 FILE * f = malloc(10*sizeof(FILE));,它将 f 指向 10 个未初始化的 FILE 结构,这没有任何意义。此外,如果您幸运,写入未初始化的 FILE 可能会导致崩溃。

如果您告诉我们您所针对的平台以及您真正想要实现的目标,会更容易提供帮助。在 POSIX 上,您可以使用 shm_open() 获取指向“共享内存”的文件描述符,并使用 fdopen() 将文件描述符转换为 FILE*。是的,它可能会用完空间。

【讨论】:

    猜你喜欢
    • 2023-02-22
    • 2012-02-13
    • 1970-01-01
    • 1970-01-01
    • 2020-04-08
    • 2011-07-17
    • 1970-01-01
    • 2011-11-09
    • 2017-03-11
    相关资源
    最近更新 更多