C 程序 - valgrind 报告写入文件错误,但数据在文件中并且可以读回

问题描述 投票:0回答:1

我是 C 编程新手。我正在尝试将 C 结构写入文件并将其读回。我正在使用 valgrind 来检查内存泄漏和潜在错误。当我运行程序时,我收到一组 valgrind 错误,并且没有内存泄漏。该程序还准确地将数据写入文件并从文件中读回数据。我创建了一个单独的测试程序(

testRead
)来测试在运行写入文件的测试程序(
testUsers
)后读回文件,并且数据正确读回。我还可以使用 hexdump 验证磁盘上的数据是否正确。 valgrind 错误是什么意思 - 错误消息中引用的未启动字节是什么?

运行返回的错误:

valgrind -s --track-origins=yes --leak-check=full --show-leak-kinds=all ./testUsers

==1155442== HEAP SUMMARY:
==1155442==     in use at exit: 0 bytes in 0 blocks
==1155442==   total heap usage: 11 allocs, 11 frees, 10,448 bytes allocated
==1155442== 
==1155442== All heap blocks were freed -- no leaks are possible
==1155442== 
==1155442== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==1155442== 
==1155442== 1 errors in context 1 of 1:
==1155442== Syscall param write(buf) points to uninitialised byte(s)
==1155442==    at 0x4989887: write (write.c:26)
==1155442==    by 0x48FFEEC: _IO_file_write@@GLIBC_2.2.5 (fileops.c:1180)
==1155442==    by 0x49019E0: new_do_write (fileops.c:448)
==1155442==    by 0x49019E0: _IO_new_do_write (fileops.c:425)
==1155442==    by 0x49019E0: _IO_do_write@@GLIBC_2.2.5 (fileops.c:422)
==1155442==    by 0x4900FD7: _IO_file_close_it@@GLIBC_2.2.5 (fileops.c:135)
==1155442==    by 0x48F3D8E: fclose@@GLIBC_2.2.5 (iofclose.c:53)
==1155442==    by 0x10991E: saveToFile (in /home/mark/ESP32-Projects/esp32-launcher/sandbox/users-binary_save/testUsers)
==1155442==    by 0x109D0E: main (in /home/mark/ESP32-Projects/esp32-launcher/sandbox/users-binary_save/testUsers)
==1155442==  Address 0x4aa17f6 is 6 bytes inside a block of size 4,096 alloc'd
==1155442==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1155442==    by 0x48F3BA3: _IO_file_doallocate (filedoalloc.c:101)
==1155442==    by 0x4902CDF: _IO_doallocbuf (genops.c:347)
==1155442==    by 0x4901F5F: _IO_file_overflow@@GLIBC_2.2.5 (fileops.c:744)
==1155442==    by 0x49006D4: _IO_new_file_xsputn (fileops.c:1243)
==1155442==    by 0x49006D4: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1196)
==1155442==    by 0x48F4FD6: fwrite (iofwrite.c:39)
==1155442==    by 0x1098A5: saveToFile (in /home/mark/ESP32-Projects/esp32-launcher/sandbox/users-binary_save/testUsers)
==1155442==    by 0x109D0E: main (in /home/mark/ESP32-Projects/esp32-launcher/sandbox/users-binary_save/testUsers)
==1155442==  Uninitialised value was created by a heap allocation
==1155442==    at 0x4848899: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1155442==    by 0x1095F6: findUser (in /home/mark/ESP32-Projects/esp32-launcher/sandbox/users-binary_save/testUsers)
==1155442==    by 0x1094F4: createUser (in /home/mark/ESP32-Projects/esp32-launcher/sandbox/users-binary_save/testUsers)
==1155442==    by 0x1093DE: initDB (in /home/mark/ESP32-Projects/esp32-launcher/sandbox/users-binary_save/testUsers)
==1155442==    by 0x109AFF: main (in /home/mark/ESP32-Projects/esp32-launcher/sandbox/users-binary_save/testUsers)

用户.h

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAX_USERS 3
#define MAX_USER_NAME_SIZE 20

#define PERMISSION_LAUNCH 4
#define PERMISSION_EDIT_USER 2
#define PERMISSION_EDIT_LAUNCHER 1

typedef struct {
  char user_name[20];
  char password[20];
  int permissions;
  int index;
} User;

typedef struct {
    int code;
    char *message;
    char* payload;
} Error;

extern char* user_names[MAX_USERS][MAX_USER_NAME_SIZE];

Error create_error(int code, char *message);

Error createUser(char* user_name, char* password, int permissions);

User* findUser(char * user_name);

void printUsers();

Error initDB();

void deleteUsers();

Error saveToFile();

Error readFromFile();

用户.c

#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "users.h"

/* An array of pointers to the User structs created */
static User* users_storage[MAX_USERS];

/* The number of users in the users_storage array */
int user_counter = 0;

/* File name for saving the users_storage to disk */
const char* USER_FILE_NAME = "users.dat"; 

/*
A struct to return error messages to the UI as needed for errors that come up.
There are no different error codes, just 0 for success and -1 for an error. The UI
Would check the error code and then display the error message.
*/
Error create_error(int code, char *message) {
    Error err;
    err.code = code;
    err.message = message;
    err.payload = "Nothing to see here.";
    return err;
}

/*
Null out the users_storage and add the "admin" user to the users_storage
*/
Error initDB() {
    Error result = create_error(0, "Success");
    for (int i = 0; i < MAX_USERS; i++) {
        if (users_storage[i] != NULL) {
            free(users_storage[i]);
        }
        users_storage[i] = NULL;
    }
    int permissions = PERMISSION_LAUNCH + PERMISSION_EDIT_USER + PERMISSION_EDIT_LAUNCHER;
    result = createUser("admin", "password", permissions);
    return result;
}

/*
Create a new User. 
- Checks for invalid permissions; it must be between 1 and 7
- Checks for duplicate users using the user name
- Creates a new user and adds it to the users_storage
- Increments the user_counter
- If the users_storage is full (ie there are MAX_USERS in the users_storage), then an error is returned 
  with a message for the UI that a user must be deleted before a new one can be created.
*/
Error createUser(char* user_name, char* password, int permissions) {
    int user_permissions = 0;
    Error result = create_error(0, "Success");
    if (permissions < 0 || permissions > 7) {
        result.code = -1;
        result.message = "Invalid permissions. Permissions must be between 0 and 7. Permissions set to 1.";
        user_permissions = 1;
    }
    else {
        user_permissions = permissions;
    }

    User* duplicate = findUser(user_name);
    if (duplicate != NULL) {
        result.message = "Duplicate user";
        result.code = -1;
        return result;
    }

    User* user = findUser(NULL);
    if (user != NULL) {
        strcpy(user->user_name, user_name);
        strcpy(user->password, password);
        user->permissions = user_permissions;
        user_counter++;
        return result;
    }
    else {
        result.message = "Out of user memory. Delete a user before creating another one.";
        result.code = -1;
        return result;
    }
}


/*
Finds a user by user name. 
- Returns NULL if the user does not exist
- Returns the User struct if the user is found
*/
User* findUser(char* user_name) {
    for (int i = 0; i < MAX_USERS; i++) {
        User * user = users_storage[i];
        if (user == NULL && user_name == NULL) {
            // found and empty user slot
            User * user = malloc(sizeof(User));
            user->index = i;
            users_storage[i] = user;
            return user;
        }
        if (user != NULL && user_name != NULL) {
            if (strcmp(user->user_name, user_name) == 0) {
                return user;
            }
        }
    }
    return NULL;
}

/*
Convenience function to print out the contents of the users_storage in a human
readable format for debugging.
*/
void printUsers() {
  for (int i = 0; i < MAX_USERS; i++) {
    User* user = users_storage[i];
    if (user != NULL) {
        printf("%i, %s, %s, %i, %i\n", i, user->user_name, user->password, user->permissions, user->index);
    }
    else {
        printf("%i, NULL\n", i);
    }
  }
}

/*
Used by readFromFile to clear the users_storage.
*/
void deleteUsers() {
    for (int i=0; i<MAX_USERS; i++) {
        if (users_storage[i] != NULL) {
            User* user = users_storage[i];
            printf("deleteUsers user_name=%s\n", user->user_name);
            free(user);
            users_storage[i] = NULL;
        }
    }
}

/*
Write the users_storage to a binary file
*/
Error saveToFile() {
    Error result = create_error(0, "Success");
    FILE *file = fopen(USER_FILE_NAME, "wb");
    if (file == NULL) {
        result.message = "Error opening file";
        result.code = -1;
        return result;
    }
    size_t element_count = 0;
    for (int i = 0; i < MAX_USERS; i++) {
        if (users_storage[i] != NULL) {
            User* user = users_storage[i];
            //printf("user_name=%s, password=%s, permissions=%i, index=%i\n", user->user_name, user->password, user->permissions, user->index);
            fwrite(user, sizeof(User), 1, file);
            if (ferror(file)) {
                perror("Error writing to file");
                result.message = "Error writing to file";
                result.code = -1;
                fclose(file);
                return result;
            } 
            element_count++;
        }
    }
    fclose(file);
    result.payload = "User data saved in a file";
    return result;
}

/*
Read the users_storage from a binary file
*/
Error readFromFile() {
    Error result = create_error(0, "Success");
    FILE *file = fopen(USER_FILE_NAME, "rb");
    if (file == NULL) {
        result.message = "Error opening file";
        result.code = -1;
        return result;
    }
    User user;
    deleteUsers();
    size_t read_size;
    int i = 0;
    int users_created = 0;
    while(1) {
        read_size = fread(&user, sizeof(User), 1, file);
        if (read_size == 0) {
            break;
        }
        else {
            createUser(user.user_name, user.password, user.permissions);
            i++;
            users_created++;
        }
    } 
    fclose(file);
    result.payload = "User data read from file.";
    return result;
}

测试用户.c

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "users.h"

int main() {
  printf("InitDB: ");
  Error result = initDB();
  printf("%i, %s\n",result.code, result.message);
  printUsers();

  //create a user with bad permissions
  printf("\ncreate a user ron with bad permissions (9): ");
  result = createUser("ron", "password", 9);
  printf("%i, %s\n",result.code, result.message);
  printUsers();
 
  // create a user
  printf("\ncreate john: ");
  result = createUser("john", "pw0", 4);
  printf("%i, %s\n",result.code, result.message);
  printUsers();

  // create the same user again
  printf("\ncreate john: ");
  result = createUser("john", "pw0", 4);
  printf("%i, %s\n",result.code, result.message);
  printUsers();

  // create another user when out of users
  // Need to set MAX_USERS to 4 to trip this error condition
  printf("\ncreate mark: ");
  result = createUser("mark", "pw0", 4);
  printf("%i, %s\n",result.code, result.message);
  printUsers();

  printf("\nSaving the file: \n");
  result = saveToFile();
  printf("%i, %s, %s\n",result.code, result.message, result.payload);
  printUsers();

  printf("\nReading the file: \n");
  result = readFromFile();
  printf("%i, %s, %s\n",result.code, result.message, result.payload);
  printUsers();

  printf("\nDeleting all the users\n");
  deleteUsers();

  return(0);
}

testRead.c

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "users.h"

int main() {
  printf("\nReading the file: \n");
  Error result = readFromFile();
  printf("%i, %s, %s\n",result.code, result.message, result.payload);
  printUsers();

  printf("\nRemove all the users from memory\n");
  deleteUsers();

  return(0);
}

生成文件

usermake: users.c testUsers.c
    gcc -o -g -O0 -o testUsers users.c testUsers.c -I .
    gcc -o -g -O0 -o testRead users.c testRead.c -I .
c valgrind
1个回答
0
投票

当您使用

malloc
User
对象分配空间时,分配的字节未初始化。 当您稍后使用
username
写入
password
strcpy
字段时,这不会写入数组的 all 元素。 这会使任何剩余的数组元素未初始化。

当您将结构写入磁盘时,您正在写入 整个 结构,包括

username
password
数组的未初始化元素。 这就是 valgring 所捕捉到的。

您可以在这里做的是使用

calloc
而不是
malloc
来分配空间。 这会将返回内存的所有字节初始化为 0,并防止读取未初始化的字节。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.