c - 分段错误而不是显示消息 - 使用 c 中的指针从文件中读取
问题描述
我写了一个程序,它从文件中读取。我使用打印数组太大的条件,但是当我使用太大的数组而不是显示此消息时,我遇到了分段错误。
这是我的程序
#include <stdio.h>
#include <stdlib.h>
#define N 10000 // Maximum array size
int _strlen(char *array) {
int i;
for (i = 0; array[i] != '\0'; ++i);
return i;
}
int readText(FILE *wp, char *s, int max) {
int sum = 0;
if (_strlen(s) > max) {
printf("This array is too big. Maximum size is %d", max);
} else {
while ((*s++ = fgetc(wp)) != EOF) {
sum++;
}
*(s-1) = '\0';
}
return sum;
}
int main(int argc, char *argv[]) {
FILE *wz, *wc;
char *s;
char array[N];
s = array;
if (argc != 3) {
printf("Wrong arguments number\n");
printf("I should run this way:\n");
printf("%s source result\n",argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Open error %s\n", argv[1]);
exit(1);
}
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Open error %s\n", argv[2]);
exit(2);
}
fprintf(wc, "Read text from file source.txt");
readText(wz, s, 10000);
return 0;
}
在输出中我想要:This array is too big. Maximum size is %d
而不是Segmentation fault core dumped
另外,我想说程序是当我使用较小的数组时,但是当他使用太大的数组而不是分段错误时,我想向用户显示正确的消息。
谢谢,我以这种方式改变了我的程序。唯一的问题是这个程序在每个 while 循环中检查 if 条件,所以这个程序可能很慢。
int readText(FILE *wp, char *s, int max) {
int sum = 0;
if (_strlen(s) > max) {
printf("This array is too big. Maximum size is %d", max);
} else {
while ((*s++ = fgetc(wp)) != EOF) {
sum++;
if (sum > max) {
printf("This array is too big. Maximum size is %d", max);
break;
}
}
*(s-1) = '\0';
}
return sum;
}
解决方案
评论/其他答案解决了您未定义的行为(在您的情况下是分段错误)。
唯一的问题是这个程序在每个 while 循环中检查 if 条件,所以这个程序可能很慢。
您的程序不是因为“如果”而变慢,而是因为您每个字符读取文件字符。
使用stat或等效函数,您可以获得文件的大小以读取它只抛出一个fread:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#define N 10000 // Maximum array size
int main(int argc, char *argv[]) {
char array[N];
FILE *wz, *wc;
struct stat st;
off_t sz;
if (argc != 3) {
printf("Wrong arguments number\n"
"I should run this way:\n"
"%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
exit(1);
}
if (stat(argv[1], &st) == -1) {
printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
exit(1);
}
if (st.st_size > N-1) {
printf("This array is too big. Maximum size is %d", N-1);
sz = N-1;
}
else
sz = st.st_size;
if (fread(array, 1, sz, wz) != sz) {
printf("cannot read %s : %s", argv[1], strerror(errno));
fclose(wz); /* for valgrind end test etc */
exit(1);
}
array[sz] = 0;
fclose(wz);
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
fclose(wz); /* for valgrind end test etc */
exit(2);
}
/* ... */
fclose(wc);
return 0;
}
知道文件的大小可以消除对恒定大小的限制并尝试读取文件,同时您可以为以下内容分配足够的内存:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char *argv[]) {
char * array;
FILE *wz, *wc;
struct stat st;
if (argc != 3) {
printf("Wrong arguments number\n"
"I should run this way:\n"
"%s source result\n", argv[0]);
exit(1);
}
if ((wz = fopen(argv[1], "r")) == NULL) {
printf("Cannot open %s to read : %s\n", argv[1], strerror(errno));
exit(1);
}
if (stat(argv[1], &st) == -1) {
printf("Cannot get stat of %s : %s\n", argv[1], strerror(errno));
exit(2);
}
if ((array = malloc(st.st_size + 1)) == NULL) {
printf("Not enough memory to memorize the file %s\n", argv[1]);
exit(3);
}
if (fread(array, 1, st.st_size, wz) != st.st_size) {
printf("cannot read %s : %s", argv[1], strerror(errno));
fclose(wz); /* for valgrind end test etc */
free(array); /* for valgrind etc */
exit(4);
}
array[st.st_size] = 0;
fclose(wz);
if ((wc = fopen(argv[2], "w")) == NULL) {
printf("Cannot open %s to write : %s\n", argv[2], strerror(errno));
free(array); /* for valgrind etc */
exit(5);
}
/* ... */
fclose(wc);
free(array); /* for valgrind etc */
return 0;
}
无论如何,由于程序“源结果”的使用,您可能希望将argv[1]指定的文件复制到argv[2]指定的文件中,在这种情况下,最好逐块读写而不是全部读取以不使用大量内存并管理输入文件大小大于内存大小的情况。
推荐阅读
- python - 通过迭代列表长度并使用列表值来使用 map 而不是 for 循环
- macos - Emacs 在 Mac OSX Catalina 中停止工作。怎么修?
- python - 我可以使用 Python SDK 对 Firebase Firestore 子项进行排序吗?
- python - 为什么图像(numpy 数组)在编码为 tfrecord 文件之前转换为字符串?
- javascript - Vue.js 应用程序不会从特定方法向控制台抛出错误
- android - 如何解决 App 制作中的解析错误?
- android - Android:由于配置时的 IllegalArgumentException,无法实例化 MediaCodec
- javascript - 使用 Javascript / HTML-Dropdowns 计算
- javascript - 如何在 React Native 中将两个按钮并排放置在背景顶部和屏幕底部?
- android - WorkManager.getInstance(applicationContext).getWorkInfoByIdLiveData(id).observe 是否保证遵守 WorkInfo.State.ENQUEUED