代码之家  ›  专栏  ›  技术社区  ›  user1045680

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

  •  2
  • user1045680  · 技术社区  · 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;
    }
    

    testUsers。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);
    }
    

    Makefile

    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 .
    
    1 回复  |  直到 1 年前
        1
  •  2
  •   dbush    1 年前

    当你使用 malloc 为a分配空间 User 对象,分配的字节未初始化。当你稍后写信给 username password 字段使用 strcpy ,这不写 全部的 数组的元素。这会使任何剩余的数组元素未初始化。

    当你将结构体写入磁盘时,你正在写入 整个的 结构体,包括未初始化的元素 用户名 密码 阵列。这就是瓦尔格林所捕捉到的。

    你在这里能做的就是使用 calloc 而不是 马洛克 以分配空间。这将把返回内存的所有字节初始化为0,并防止读取未初始化的字节。

    与此无关,这对 findUser 创建新功能 用户 对象。这应该是 createUser 功能。这个 findUser 功能应该 只有 找到一个用户。