2010-03-04 55 views
1

重要编辑:阅读和写作结构[C]

对不起,大家,我犯了一个大错误的结构。 char * name;意思是在结构之外,写在结构之后的文件中。 这样,你读取结构,找出名字的大小,然后读入字符串。也解释了为什么不需要空终止符。 但是,我觉得某处,我的实际问题已得到解答。如果有人想编辑他们的回复,所以我可以选择一个最合适的,我会很感激。

同样,我所问的问题是“如果你阅读了一个结构体,你还阅读它所保存的数据,或者你需要以其他方式访问它”。

很抱歉的混乱

对于分配,我一直在负责其写入和读取结构的盘(使用的fread和fwrite)中的程序。

我很难理解这个概念。 比方说我们有这样的结构:

typedef struct { 
    short nameLength; 
    char* name; 
}attendenceList; 

attendenceList names; 

现在假设我们给它这样的数据:

names.name = "John Doe\0"; 
names.nameLength = strlen(names.name); /*potentially -1?*/ 

,然后我们使用fwrite的...给出一个文件指针fp。

fwrite(&names,sizeof(names),1,fp); 

现在我们关闭文件,稍后打开它来阅读结构。 问题是这样的:当我们在结构中读取时,我们是否还读取了它存储的变量?

我们能那么现在这样做:

if(names.nameLength < 10) 
{ 
... 
} 

还是我们必须FREAD的东西,不只是结构,或以某种方式进行分配? 假设FREAD是:

fread(&names,sizeof(names),1,fp); 

而且假设我们定义了在我国目前的功能结构,如上所述。

感谢您的帮助!

+0

一对夫妇的评论:' “李四”'已经有了'e'后0,这样你就不需要写' “李四\ 0”'。其次,'strlen'返回'size_t',它是一个无符号类型。因此,它不能返回负值。当你把'strlen'的返回值赋给'short'时,如果这个值在'short'的范围内,那么一切都没问题。否则,你会溢出。 – 2010-03-04 20:52:14

+0

你编辑的答案是,如果你用'fwrite'写了一个'struct',然后用'fread'读回,你会得到它,但是如果'struct'有指针,你会得到值的指针返回,这在阅读时可能没有意义。如果你的struct只有一个'short'元素,我不知道你为什么不能写'short'本身,而不是将它包装在'struct'中。也许你实际的'struct'有更多的数据呢?只要没有指针参与'struct',你就没问题。当然,编写二进制数据不能跨不同的机器移植。 – 2010-03-04 21:02:00

+0

实际的结构体包含两个字符串的大小(在结构之后,因此您可以正确使用fseek或知道如何读取它们),并且该程序连续使用两次。 – Blackbinary 2010-03-04 21:23:45

回答

0

我假设你的意思是char* name而不是char name。 另外sizeof(name)将返回4,因为你得到的大小不是char数组的长度,而是char*。所以你应该在你的fwrite里写strlen(name)而不是sizeof(name)

在你的上面的例子中,我会建议存储没有null终止字符串的确切大小。您不需要存储字符串长度,因为您可以在后面获取该字符串长度。

如果您正在读取文件中的一个字符串,并且您没有空终止符而写下确切的大小。然后,在读取数据后,需要手动将终止缓冲区的内容终止。 因此,请确保至少分配您正在读取的数据的大小加上1.
然后,您可以将该数组的最后一个字节设置为'\0'

如果你一次写入整个结构到缓冲区,你应该小心,因为填充。填充可能并不总是相同的。

当我们在结构中读取的时候,我们是否还读取它存储的变量?

是的,你的问题是,正如我上面提到的,你将存储指针char *(4字节)而不是实际的char数组。我会建议单独存储结构元素。

+0

呵呵?我不明白你的意思是'[我的]缓冲区'? 啊,得到它的感谢,将修复我的代码。这只是一个例子,因为我不想在实际任务附近使用任何东西。 获得一个字符串的长度(它实际上来自一个文件,所以使用fread)我需要-1 strlen吗? – Blackbinary 2010-03-04 20:23:41

+0

'strlen'返回不包含空终止符的字符串长度。 – 2010-03-04 20:30:32

+0

感谢您的澄清,但我的实际问题一直未得到答复。 – Blackbinary 2010-03-04 20:32:05

3

您在这里有一个问题:

fwrite(&names,sizeof(names),1,fp); 

由于attendenceList保存名称作为char *这将只是写出来的指针,而不是实际的文本。当你读回来时,指针所引用的内存很可能还有其他内容。

你有两个选择:

  1. 放入attendenceList字符数组(char names[MAXSIZE])。
  2. 不要写入原始数据结构,而是写入必要的字段。
1

您正在编写包含其成员的结构的内存布局。

如果您再次读取结构,您将返回它们 - 至少如果您在同一平台上执行此操作,并且程序使用相同的编译器和编译器设置进行编译。

您的name成员声明为char,所以不能在其中存储字符串。

如果name是这样一个指针:

typedef struct { 
    short nameLength; 
    char *name; 
}attendenceList; 

你真的不应该读/写结构到一个文件。您将在存储器中编写结构,并且该结构包含name指针的值。

fwrite对结构中的指针一无所知,它不会遵循指针,也不会写任何指向的指针。

当您再次读取结构时,您会看到name指针中的地址,这可能不会指向任何明智的东西。

如果你声明name为一个数组,那么你就可以,因为数组及其内容是结构的一部分。

typedef struct { 
    short nameLength; 
    char name[32]; 
}attendenceList; 

与往常一样,请确保您不尝试复制一个字符串 - 包括其NUL terminator-到name这是大于32.当你回来读一遍。设置yourstruct.name [31] = 0;所以你确定缓冲区是空的。

要写入的结构,你会做

attendenceList my_list; 

//initialize my_list 
if(fwrite(&my_list,sizeof my_list,1,f) != 1) { 
//handle error 
} 

,并备份看了一遍:

attendenceList my_list; 

//initialize my_list 
if(fread(&my_list,sizeof my_list,1,f) != 1) { 
//handle error 
} 

}

0

你问:

现在我们关闭文件,稍后打开它来阅读结构。问题是这样的:当我们阅读结构时,我们是否还在读取它存储的变量?

编号sizeof(names)是在编译时定义的常量值。这将是一样

sizeof(short) + sizeof(void*) + some_amount_of_padding_to_align_things 

它将包括什么names.name点,这将只包括指针本身的尺寸大小。

因此,在将其写入文件时存在两个问题。

  1. 你实际上并没有写名字字符串文件
  2. 你正在写一个指针值,当你读回,将没有任何意义的文件。

由于您的代码是当前编写的,当您读回名称时,names.name将指向某处,但不会指向“John Doe \ 0”。

您需要做的是编写由names.name指向的字符串而不是指针值。

你需要做的事情有时被称为“扁平化”结构,你在内存中创建一个不包含指针,但保存与你想要使用的结构相同的数据的结构,然后将扁平结构写入磁盘。这是做到这一点的一种方法。

typedef struct { 
    short nameLength; 
    char name[1]; // this will be variable sized at runtime. 
}attendenceListFlat; 

int cbFlat = sizeof(attendenceListFlat) + strlen(names.name); 
attendenceListFlat * pflat = malloc(cbFlat); 
pflat->nameLength = names.nameLength; 
strcpy(pflat->name, names.name); 

fwrite(pflat, cbFlat, 1, fp); 

的扁平结构与具有1的最小尺寸的阵列结束,但是当我们的malloc,我们添加的strlen(names.name),所以我们可以把这个作为函数strlen(names.name)的阵列+1大小。

0

有几件事。

结构只是内存的块。它只需要一堆字节并在它们上绘制边界。访问结构元素只是将特定内存偏移量转换为特定类型数据的便捷方式

您正在尝试将字符串分配给char类型。这不起作用。在C中,字符串是在它们结尾处具有NULL字节的字符数组。最简单的方法是将名称设置为固定缓冲区。在创建结构时,必须将名称复制到缓冲区中(非常小心不要写入比缓冲区包含更多的字节)。然后您可以一步写入/读取文件中的缓冲区。

struct attendanceList { 
    int namelen; 
    char name[256]; //fixed size buffer for name 
} 

你可以做的另一种方式是由具有名字是指针为字符串。这使得你想要做的更加复杂,因为为了将结构写入/从文件读取,必须考虑到名称存储在内存中的不同位置。这意味着两次写入和两次读取(取决于您的操作方式),并正确指定name指针指向读取名称数据的任何位置。

struct attendanceList { 
    int namelen; 
    char* name; //the * means "this is a pointer to a char somewhere else in memory" 
} 

有你可以做到这一点,使用一招用在一个结构的结束零长度阵列的动态调整结构第三种方式。一旦知道了名称的长度,就可以分配正确的数量(sizeof(struct attendanceList)+字符串的长度)。然后你将它放在一个连续的缓冲区中。您只需要记住sizeof(struct attendanceList)不是您需要编写/读取的大小。这可能有点混乱作为一个开始。这也是所有编译器都不支持的破解方式。

struct attendanceList { 
    int namelen; 
    char name[0]; //this just allows easy access to the data following the struct. Be careful! 

}