我最近了解了C中的存储类。特别是我对static
存储类很着迷。来自Haskell我避开了将输出缓冲区传递给函数以获得结果的概念。例如,考虑以下readfile
函数:
#include <stdio.h>
void readfile(const char * filename, char * contents, size_t size) {
FILE * file = fopen(filename, "rb");
fread(contents, size, 1, file);
contents[size] = 0;
fclose(file);
}
我不喜欢这样的代码有几个原因:
void
作为返回类型让我感到烦恼。我不知道为什么。它只是。由于这些问题,我重写了上面的代码如下:
#include <stdio.h>
#include <malloc.h>
char * readfile(const char * filename) {
FILE * file = fopen(filename, "rb");
fseek(file, 0, SEEK_END);
size_t size = ftell(file);
fseek(file, 0, SEEK_SET);
static char * contents;
contents = malloc(size + 1);
fread(contents, size, 1, file);
fclose(file);
contents[size] = 0;
return contents;
}
现在,您只需将文件名传递给readfile
即可读取文件内容。它为文件内容分配空间并返回指向新创建的缓冲区的指针。完成后,您需要做的唯一记账就是free
缓冲区。
正如您在上面的代码中所看到的,我已将contents
声明为static
,因此该变量只有一个实例,因此您可以在没有编译器给出警告的情况下返回它。在我看来,这比使用全局变量更清晰。
尽管如此,我对在生产代码中使用static
持怀疑态度:部分是因为我害怕可变状态加上共享变量,部分是因为这是我第一次使用它。使用static
的潜在风险是什么?
例如,当您同时读取两个文件时,上面的代码是否会产生错误的结果?如何在不恢复C风格代码的情况下解决这些问题(例如,将输出缓冲区传递给函数等)
答案 0 :(得分:5)
您几乎不想在C中将局部变量声明为static
。
使变量static
基本上使它成为一个全局变量,其名称在该函数之外是不可访问的。由于您可能很清楚,全局变量可能会出现问题:如果您从两个不同的线程运行readfile
,则可以进行第一次调用malloc
并将结果存储到contents
,第二个线程调用malloc
并将其结果存储到contents
,然后将第一个线程fread
存入contents
分配的第二个线程,其中如果在第二个线程上读取的文件较小并且在任何情况下都不合适,则可能导致缓冲区溢出。
您可能被诱惑使用static
的原因是contents
以前是一个数组。如果是这样,编译器会正确警告您不能将其返回给调用者:它将分解为指针,但是一旦函数返回,本地数组变量就会被销毁并且指针变为无效。将其声明为static
使其返回有效,因为它是一个全局变量,当函数退出并且指针保持有效时,它不会被销毁。但是,如果将它与线程一起使用,仍然存在问题。
您唯一可能想要使用static
的情况是,如果您有一些仅在函数中使用的常量数据。例如:
static const int some_integers[] = { 1, 2, 3, 4 };
然后你可以节省一些堆栈空间。
为免我忘记提及您的具体代码,删除static
使其按预期工作。如果要在现实生活中使用此代码,我会确保添加一些错误检查,因为几乎所有调用的函数都会失败,并且只会通过返回值发出信号。
答案 1 :(得分:0)
还有像stat(2)
和fstat(2)
这样的POSIX函数,可以让您找出文件的确切大小。您可以使用它来分配缓冲区并一次性读取数据。
如果您的文件相当大,您还可以使用mmap(2)
系统调用或CreateFileMapping
和MapViewOfFile
让操作系统为您完成繁重的工作
答案 2 :(得分:0)
在这种情况下使用static将有效地使变量的内存存储在进程内存的全局数据段中。但是,它只有本地范围(只有这个函数才能看到它)。由于您可能会以其他方式跟踪指针(返回值),因此不必使用此方法并浪费指针值全局数据内存(可能不是什么大问题)。