我的程序的目标是从标准输入中读取人员列表,并能够将它们写入和读取到二进制文件中。列表中的每个人都是一个包含三个字符串变量的 tPessoa 结构体。因为,我试图读取未指定数量的人,所以我使用“指针到指针”来创建一个结构数组。
当我使用 valgrind --leak-check=full --track-origins=yes` 执行编译的脚本时,出现以下错误:
==1211042== Syscall param write(buf) points to uninitialised byte(s)
==1211042== at 0x4986A77: write (write.c:26)
==1211042== by 0x48FCEEC: _IO_file_write@@GLIBC_2.2.5 (fileops.c:1180)
==1211042== by 0x48FE9E0: new_do_write (fileops.c:448)
==1211042== by 0x48FE9E0: _IO_new_do_write (fileops.c:425)
==1211042== by 0x48FE9E0: _IO_do_write@@GLIBC_2.2.5 (fileops.c:422)
==1211042== by 0x48FDFD7: _IO_file_close_it@@GLIBC_2.2.5 (fileops.c:135)
==1211042== by 0x48F0D8E: fclose@@GLIBC_2.2.5 (iofclose.c:53)
==1211042== by 0x1095A7: EscrevePessoasBinario (ex.c:68)
==1211042== by 0x109704: main (ex.c:107)
==1211042== Address 0x4a9e965 is 21 bytes inside a block of size 4,096 alloc'd
==1211042== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1211042== by 0x48F0BA3: _IO_file_doallocate (filedoalloc.c:101)
==1211042== by 0x48FFCDF: _IO_doallocbuf (genops.c:347)
==1211042== by 0x48FEF5F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:744)
==1211042== by 0x48FD6D4: _IO_new_file_xsputn (fileops.c:1243)
==1211042== by 0x48FD6D4: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1196)
==1211042== by 0x48F1FD6: fwrite (iofwrite.c:39)
==1211042== by 0x109572: EscrevePessoasBinario (ex.c:60)
==1211042== by 0x109704: main (ex.c:107)
==1211042== Uninitialised value was created by a heap allocation
==1211042== at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1211042== by 0x109384: LePessoasInput (ex.c:28)
==1211042== by 0x1096EF: main (ex.c:106)
==1211042==
==1211042==
==1211042== HEAP SUMMARY:
==1211042== in use at exit: 0 bytes in 0 blocks
==1211042== total heap usage: 12 allocs, 12 frees, 14,666 bytes allocated
==1211042==
==1211042== All heap blocks were freed -- no leaks are possible
==1211042==
==1211042== For lists of detected and suppressed errors, rerun with: -s
==1211042== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
顺便说一句,我将完整的代码留在帖子的末尾,以便任何人都可以进行测试和调查。
这是我迄今为止发现的。
Valgrind 说我正在尝试对未初始化的值进行操作,我相信这意味着我可能会向二进制文件写入垃圾并触发错误。 Valgrind 说我在第 68 行这样做,但这似乎不正确。在第 68 行,调用
fclose(arquivo)
并且释放 FILE 指针(这里没什么不寻常的)。所以我认为错误是在该函数的其他地方,在注释掉某些部分后,我发现错误发生在 if ( fwrite(pessoas[pessoa], sizeof(tPessoa), 1, arquivo) != 1 )
上。这是有道理的,因为错误似乎与将未初始化的值写入 bin 文件有关。
Valgrind 还表示正在第 28 行创建统一值,
pessoas[(*nPessoas) - 1] = (tPessoa *) malloc(sizeof(tPessoa));
。在第 28 行,我为 pessoas 数组中的另一个 tPessoa 结构分配内存。但是,我看不出该结构如何未初始化,因为我在为其分配内存后立即将值传递给该结构的变量。我怀疑我为 nome (名称,英文)变量分配了 100 个字节,但我不一定传递 99 个字符加上 ' ',因为并非所有名称都那么长。因此,如果一个人的名字长度为 15 个字符,则意味着 16 个字节(算上“ ”),剩下 84 个字节未初始化。
问题可能是 nome 变量及其统一字符吗?如果是这样,我该如何解决这个问题。
如果没有,我将把整个代码留在下面,这样任何人都可以得出结论。欢迎任何建议。我还想更好地理解
Syscall param write(buf) points to uninitialised byte(s)
的真正含义。 Valgrind 错误对我来说有点神秘。
P.S.:我正在使用
gcc -g
编译代码。
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct ex
{
char nome[100];
char dataNascimento[11];
char cpf[15];
} tPessoa;
void DesalocaPessoas(tPessoa** pessoas, int nPessoas) {
for (int pessoa = 0; pessoa < nPessoas; pessoa++) {
free(pessoas[pessoa]);
}
free(pessoas);
}
tPessoa** LePessoasInput(int* nPessoas) {
tPessoa **pessoas = (tPessoa**) malloc(sizeof(tPessoa *));
//pessoas[0] = (tPessoa*) malloc(sizeof(tPessoa));
char controle = 0;
while (1)
{
(*nPessoas)++;
pessoas = (tPessoa **) realloc(pessoas, sizeof(tPessoa*) * (*nPessoas));
pessoas[(*nPessoas) - 1] = (tPessoa *) malloc(sizeof(tPessoa));
printf("----------- NOVA PESSOA -----------\n");
printf("Nome: ");
scanf("%[^\n]%*c", pessoas[(*nPessoas)-1]->nome);
printf("Data de nascimento: ");
scanf("%[^\n]%*c", pessoas[(*nPessoas)-1]->dataNascimento);
printf("CPF: ");
scanf("%[^\n]%*c", pessoas[(*nPessoas)-1]->cpf);
while(1){
printf("\nDeseja adicionar outra pessoa? [s/n] ");
scanf("%c%*c", &controle);
printf("\n");
if ( controle == 'n' || controle == 'N' ) break;
else if ( controle == 's' || controle == 'S' ) break;
}
if ( controle == 'n' || controle == 'N' ) break;
}
return pessoas;
}
void EscrevePessoasBinario(tPessoa** pessoas, int nPessoas) {
FILE *arquivo;
int pessoasGravadas = 0;
arquivo = fopen("pessoas.bin", "wb");
if (arquivo == NULL) {
return;
}
for ( int pessoa = 0; pessoa < nPessoas; pessoa++ ) {
if ( fwrite(pessoas[pessoa], sizeof(tPessoa), 1, arquivo) != 1 ) {
fclose(arquivo);
return;
}
pessoasGravadas++;
}
printf("Pessoas gravadas: %d\n", pessoasGravadas);
fclose(arquivo);
}
void ImprimePessoasBinario(int nPessoas) {
FILE *arquivo;
tPessoa* pessoaLida = (tPessoa*) malloc(sizeof(tPessoa));
arquivo = fopen("pessoas.bin", "rb");
if (arquivo == NULL) {
free(pessoaLida);
return;
}
int nPessoasLidas = 0;
for (int pessoa = 0; pessoa < nPessoas; pessoa++) {
if (fread(pessoaLida, sizeof(tPessoa), 1, arquivo) != 1) {
break;
}
nPessoasLidas++;
printf("-----------------------------------\n");
printf("%s\n", pessoaLida->nome);
printf("%s\n", pessoaLida->dataNascimento);
printf("%s\n", pessoaLida->cpf);
printf("-----------------------------------\n");
}
printf("Pessoas lidas: %d\n", nPessoasLidas);
free(pessoaLida);
fclose(arquivo);
}
int main() {
int nPessoas = 0;
tPessoa** pessoas = LePessoasInput(&nPessoas);
EscrevePessoasBinario(pessoas, nPessoas);
ImprimePessoasBinario(nPessoas);
DesalocaPessoas(pessoas, nPessoas);
return 0;
}
默认情况下,基于在第 68 行,调用
并且释放fclose(arquivo)
指针(这里没什么不寻常的)。FILE
FILE
的流会被缓冲,并且 fclose()
可以并且将会对打开的文件进行写入操作。根据 7.21.5.1 fclose 函数,C11 标准(草案)第 2 段(粗体矿井):
成功调用fclose函数会导致stream指向的流被刷新并关闭关联的文件。 流中任何未写入的缓冲数据都会被传送到主机环境以写入文件; ...
因此,对
fclose()
的调用可以将未初始化的内存写入您的文件,这意味着 Valgrind 识别它是正确的。
您确实应该检查从
fclose()
返回的值。
您还可以显式调用
fflush()
并检查其返回值以强制写入缓冲数据。
我怀疑我为 nome(名称,英文)变量分配了 100 个字节,但我不一定传递 99 个字符加上“”,因为并非所有名称都那么长。因此,如果一个人的名字长度为 15 个字符,则意味着 16 个字节(算上“ ”),剩下 84 个字节未初始化。
这实际上会导致读取然后写入未初始化的数据,因为从
malloc()
返回的字节明确未初始化:
malloc函数为大小由size指定且值不确定的对象分配空间。
您可以调用
memset()
显式设置整个缓冲区的内容,也可以将 malloc()
替换为 calloc()
以确保在分配缓冲区时将整个缓冲区设置为零。假设您正在从用户处读取数据并执行文件 IO 操作,则性能差异根本无法衡量。