基里连科的回答应该可以解决您当前的问题,但是该程序的几乎每一行都存在从微不足道到严重的错误。根据您更大的目标,您可能不需要修复所有这些问题,但无论如何我都会将它们全部写下来,以说明冰山的大小。
void main() {
int main(void)
. 是int
绝对要求。()
在 C(不是 C++)中,为函数参数列表编写意味着参数是未指定的,而不是没有参数;从技术上讲,您可以在这里摆脱它,因为这是一个函数定义,但它的风格很糟糕。
函数定义的左大括号总是单独一行,即使所有其他左大括号都被拥抱。
int num = 0,i=0;
间距不一致。初始化是不必要的。
printf("Number of students:");
fputs("Number of students", stdout);
当您不使用printf
的格式化功能时,一些样式指南更喜欢。但是有些编译器可以为你做转换,这没什么大不了的。
scanf("%d", &num);
永远不要使用scanf
, fscanf
, orsscanf
,因为:
- 数值溢出会触发未定义的行为。允许 C 运行时仅仅因为某人输入了太多数字而使您的程序崩溃。
- 一些格式说明符(特别是
%s
,您稍后将在此程序中使用!)是不安全的,其方式与不安全完全相同gets
,即它们会愉快地写入提供的缓冲区的末尾并让您的程序崩溃(这个特定的程序看起来不安全- 对我很敏感,但应该总是像一个人的程序那样编码至少有点危险)。
- 它们使正确处理格式错误的输入变得极其困难。
从用户那里读取单个非负数的正确方法是这样的:
unsigned long getul(void)
{
char buf[80], *endp;
unsigned long val;
for (;;) {
fgets(buf, 80, stdin);
val = strtoul(buf, &endp, 10);
if (endp != buf && *endp == '\n')
return val;
if (buf[strlen(buf)] != '\n')
while (getchar() != '\n')
/* discard */;
fprintf(stderr, "*** Enter one nonnegative integer, smaller than %lu.\n",
ULONG_MAX);
}
}
您可以在这里使用固定大小的缓冲区,因为没有人的 ULONG_MAX 大到无法容纳 80 个字符。
students = (student*)malloc(num*sizeof(student));
您应该calloc
在此处使用,这样当您将结构写入磁盘时,它们不会充满垃圾。(或者按照 Alexandros 的建议编写自定义序列化程序,这也可以避免这个问题。)
for(i=0;i<num;++i){
首选样式是for (i = 0; i < num; i++) {
. 除了间距之外,使用i++
代替++i
so 可以i
在所有三个表达式中出现在相同的位置;这使它更容易阅读。
student* ptr = students+i*sizeof(student);
student* ptr = students + i;
如其他地方所讨论的。
printf("Name:");
scanf("%s", ptr->cStdName);
请参阅上面的评论。您需要另一个辅助函数,如下所示:
void getstr(char *buf, size_t len)
{
size_t n;
for (;;) {
fgets(buf, len, stdin);
n = strlen(buf);
if (n < len && buf[n] == '\n') {
memset(buf+n, 0, len-n);
return;
}
while (getchar() != '\n')
/* discard */;
fprintf(stderr, "*** Enter no more than %lu characters.",
(unsigned long)(len-1));
}
}
...
scanf("%f", &ptr->dStdAvg);
在这里你需要另一个辅助函数。和除了它使用getf
的完全一样,当然错误信息也有一点不同。getul
strtod
f = fopen("bin.bin","wb");
这不是一个非常通用的文件名吗?用户可能应该有一种方法来指定它。
fwrite(&num,sizeof(int),1,f);
您的文件格式需要一个幻数。
fwrite(students,sizeof(student),num,f);
您正在以 CPU-endian 顺序将二进制数据写入磁盘。对于此应用程序来说,这可能不是问题,但请注意,您可能会遇到跨平台兼容性问题。(就个人而言,对于您正在做的事情,我会使用诸如 JSON 之类的文本序列化,或诸如 sqlite 之类的简单无守护进程数据库。)有关此文件格式的更多潜在问题,请参阅 Alexandros 的回答。
写入磁盘上的文件时很少出现问题,而且这不是那种输出通过管道传输到某处的程序,但是,我仍然要提到它fwrite
并不能保证写入您提供的所有数据。从技术上讲,您必须fwrite
像这样调用循环:
size_t r, n = sizeof(student) * num;
char *p = (char *)students;
while (n > 0) {
r = fwrite(p, 1, n, f);
if (r == 0) break; /* write error */
n -= r;
p += r;
}
为此,您必须自己进行乘法运算并将第二个参数的 1 传递给fwrite
; 否则,简短的写入可能会在“数据元素”的中间结束,您无法知道这已经发生了。
fclose(f);
在关闭文件之前检查写入错误。
if (ferror(f) || fclose(f)) {
perror("bin.bin");
return 1; /* unsuccessful exit */
}
...
system("pause");
只是return 0
。使您按键退出的程序对批处理不友好。