#include<stdio.h>
#include<stdlib.h>
typedef struct{
char cStdName[50];
int nStdNum;
char cStdClass[4];
float dStdAvg;
}student;
student* students;
int cmp(const void* a, const void* b);
void main() {
int num = 0,i=0;
FILE *f;
printf("Number of students:");
scanf("%d", &num);
students = (student*)malloc(num*sizeof(student));
for(i=0;i<num;++i){
student* ptr = students+i*sizeof(student);
printf("Name:");
scanf("%s", ptr->cStdName);
printf("Num");
scanf("%d", &ptr->nStdNum);
printf("Class:");
scanf("%s", ptr->cStdClass);
printf("Grade:");
scanf("%f", &ptr->dStdAvg);
}
f = fopen("bin.bin","wb");
fwrite(&num,sizeof(int),1,f);
fwrite(students,sizeof(student),num,f);
fclose(f);
system("pause");
}
这应该在二进制文件中输出学生数和所有结构'数组',并且它适用于1名学生。但是当我添加&gt; = 2个人时,文件看起来像这样: http://i.imgur.com/LgL8fUa.png
如果我只添加一名学生,则仍有一些Windows路径无意义: http://i.imgur.com/s7fm9Uv.png 没关系,读取文件的程序会忽略NULL之后的所有内容(我的意思是,对于第一个char数组)。
我认为问题出现在for()循环和指针玩杂耍的某处,但我不知道在哪里。
答案 0 :(得分:5)
student* ptr = students + i * sizeof(student);
在C中,指针算法已包含sizeof(student)
。你将阅读你的arrray结束。
student* ptr = students + i;
但是,您会注意到访问ptr
与访问students[i]
相同。
答案 1 :(得分:1)
您的代码存在一些问题:
students[i]
。在您的情况下,students + i * sizeof(student)
超出范围。fwrite
与结构数组一起使用绝不是一个好主意。这是因为编译器可能在struct(padding)的成员之间添加一些空格,这意味着当你将一个结构数组传递给fwrite
时,将打印填充字节(将包含垃圾)。fwrite
时将打印垃圾。最好使用strlen
来确定每个char数组包含的读取字符数。以下是我将学生的数组写入文件的方法:
void write(students* array, int len, FILE* out)
{
int i;
fwrite(len, 1, sizeof(len), out);
for (i = 0; i < len; i++){
fwrite(array[i]->cStdName, 1, strlen(array[i]->cStdName), out);
fwrite(array[i]->nStdNum, 1, sizeof(array[i]->nStdNum), out);
fwrite(array[i]->cStdClass, 1, strlen(array[i]->cStdClass), out);
fwrite(array[i]->dStdAvg, 1, sizeof(array[i]->dStdAvg), out);
}
}
答案 2 :(得分:0)
指针赋值可以在for循环外部进行一次,并且可以在for循环结束时递增指针。试试这个。
确定。这是尝试解释发生了什么,ptr指向学生结构类型列表中的第一个元素,当你在for循环结束时递增ptr时,它指向列表中的下一个学生。
---
students = (student*)malloc(num*sizeof(student));
student* ptr = students;
for(i=0;i<num;++i){
printf("Name:");
----
----
ptr++;
}
答案 3 :(得分:0)
基里连科的答案应该可以解决你眼前的问题,但是在这个项目的几乎每一行都有从琐碎到严重的错误。根据您的更大目标,您可能不需要修复所有目标,但无论如何我都要将它们全部写下来,以说明冰山的大小。
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
或sscanf
,因为:
%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
,以便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);
您的文件格式需要magic number。
fwrite(students,sizeof(student),num,f);
您正在以CPU端序的顺序将二进制数据写入磁盘。对于此应用程序而言,这可能不是问题,但请注意,您可能会遇到跨平台兼容性问题。 (就个人而言,对于你正在做的事情,我会使用文本序列化,如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;
}
为了使其正常工作,必须自己进行乘法,并将第二个参数传递给fwrite
;否则,短写可能会在“数据元素”的中间结束,而您无法知道这种情况已经发生。
fclose(f);
关闭文件前检查写入错误。
if (ferror(f) || fclose(f)) {
perror("bin.bin");
return 1; /* unsuccessful exit */
}
...
system("pause");
只需return 0
。让你按键退出的程序是批处理不友好的。