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

当strcpy在c中时,为什么编码会出错?

  •  2
  • codez  · 技术社区  · 8 年前

    源代码:

    char CUSTOMERS_FILE[50] = "customers.txt";
    
    typedef struct Customer {
        char name[50];
        char password[50];
        char billing_address[100];
        char phone_number[15];
        double amount_paid;
        double amount_due;
        char date[20];
    } Customer;
    
    char* read_string(int length) {
        char data[length];
        rewind(stdin);
        fgets(data, length, stdin);
    
        if (data[0] == '\n') {
            data[0] = '\0';
        }
    
        strtok(data, "\n");
    
        printf("DATA: %s", data);
    
        return data;
    }
    
    void handle_modify_customer(Customer customer) {
        Customer edited_details;
    
        printf("\nMODIFYING DETAILS\n==============\n\n");
    
        printf("CREATE A CUSTOMER PROFILE\n=========================\n");
    
        printf("Name (%s): ", customer.name);
        strcpy(edited_details.name, read_string(50));
    
        printf("Password (%s): ", customer.password);
        strcpy(edited_details.password, read_string(50));
    
        printf("Billing Address (%s): ", customer.billing_address);
        strcpy(edited_details.billing_address, read_string(100));
    
        printf("Phone Number (%s): ", customer.phone_number);
        strcpy(edited_details.phone_number, read_string(15));
    
        printf("Amount Paid (%10.2lf): ", customer.amount_paid);
        scanf("%lf", &edited_details.amount_paid);
    
        printf("Amount Due (%10.2lf): ", customer.amount_due);
        scanf("%lf", &edited_details.amount_due);
    
        printf("Payment Date (%s): ", customer.date);
        strcpy(edited_details.date, read_string(20));
    
        /*
        if (strlen(edited_details.name) == '\0' || strlen(edited_details.billing_address) == '\0' || strlen(edited_details.password) == '\0' || strlen(edited_details.phone_number) == '\0' || strlen(edited_details.date) == '\0') {
            printf("All fields must be filled in!");
            handle_modify_customer(customer);
        }*/
    
        if (edited_details.name[0] == '\0' || edited_details.billing_address[0] == '\0' || edited_details.password[0] == '\0' || edited_details.phone_number[0] == '\0' || edited_details.date[0] == '\0') {
            printf("All fields must be filled in!");
            handle_modify_customer(customer);
        }
    
        FILE *file = fopen(CUSTOMERS_FILE, "r");
        FILE *new_file = fopen("customers_new.txt", "ab+");
    
        Customer record;
    
        while (fscanf(file, "[%[^]]], [%[^]]], [%[^]]], [%[^]]], [%lf], [%lf], [%[^]]]\n",
                      record.name, record.password, record.billing_address, record.phone_number,
                      &record.amount_paid, &record.amount_due, record.date) == 7)
        {
            if (strcmp(customer.name, record.name) == 0) {
                printf("P: %s\nD: %s", edited_details.phone_number, edited_details.date);
                fprintf(new_file, "[%s], [%s], [%s], [%s], [%lf], [%lf], [%s]\n", edited_details.name, edited_details.password, edited_details.billing_address, edited_details.phone_number, edited_details.amount_paid, edited_details.amount_due, edited_details.date);
            } else {
                fprintf(new_file, "[%s], [%s], [%s], [%s], [%lf], [%lf], [%s]\n", record.name, record.password, record.billing_address, record.phone_number, record.amount_paid, record.amount_due, record.date);
            }
        }
    
        fclose(file);
        fclose(new_file);
    
        remove(CUSTOMERS_FILE);
        rename("customers_new.txt", CUSTOMERS_FILE);
    
        printf("\nThe customer details have been successfully modified!\n");
        key_to_continue();
    }
    

    执行示例:

    MODIFYING DETAILS
    ==============
    
    CREATE A CUSTOMER PROFILE
    =========================
    Name (dumbfk): test
    DATA: testPassword (abc123): lol
    DATA: lolBilling Address (pukima jalan): lol
    DATA: lolPhone Number (6969696969): 499449
    DATA: 499449Amount Paid (   6969.00): 499449
    Amount Due (6969699.00): 499494
    Payment Date (6/9/1969): 22/2/2000
    DATA: 22/2/2000P: �O���
    D: �O���
    The customer details have been successfully modified!
    

    数据文件(之前):

    [well lol], [abc123], [wtf bro? 24], [0183188383], [3000.000000], [4000.000000], [12/12/2012]
    [chow hai], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
    [lol head], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
    [stupid face], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
    [dumbfk], [abc123], [pukima jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
    

    数据文件(之后):

    [well lol], [abc123], [wtf bro? 24], [0183188383], [3000.000000], [4000.000000], [12/12/2012]
    [chow hai], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
    [lol head], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
    [stupid face], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
    [test], [lol], [lol], [�O���], [499449.000000], [499494.000000], [�O���]
    

    问题:

    如您所见,问题是付款日期和电话号码字段变得混乱。就在我使用之后 strcpy . 我调试了 read_string(..) 功能很好。我不明白为什么会这样。如能帮助解决这个问题,我们将不胜感激。

    有趣的是:只有 date phone_number 受到影响。 name , password , billing_address 没有问题。

    4 回复  |  直到 8 年前
        1
  •  5
  •   klutt    8 年前

    这是一个很好的例子,说明指针和数组是不同的:

    char* read_string(int length) {
        char data[length];
        // code
        return data;
    }
    

    不会起作用,因为 data 是在堆栈上分配的本地数组,并且在函数返回时将不存在。

    如果你改变 char data[length] static char data[length] 它会起作用的但是,请注意,前面的读取将被覆盖,因此无法按预期工作:

    char *s1, *s2;
    s1 = read_string(10);
    s2 = read_string(10);
    printf("First string: %s\n", s1);
    printf("Second string: %s\n", s2);
    

    一个绕过它的方法就是 char data* = malloc(length * sizeof *data) . 这样你就可以使用以前的阅读。但总的来说你要避免隐藏 malloc 就像这样,因为你需要 free 他们之后如果您想采用这种解决方案,请执行以下操作:

    char * read_string(char * dest, int length) {
        char data[length];
        // code
        return strncpy(dest, data, length);
    }
    

    然后这样称呼:

    char * str = malloc(length);
    if(! read_string(str, length)) {
        fprintf(stderr, "Error reading string\n");
        exit(EXIT_FAILURE);
    }
    // Code
    free(str);
    
        2
  •  2
  •   pm101    8 年前

    将read_string函数中的本地字符数据更改为静态变量,这样内存就不会丢失

      #define MAX_STR_LENGTH 100
    
     char* read_string(int length) 
     {
        static char data[MAX_STR_LENGTH]; // change to static -- it will not change
        rewind(stdin);
        fgets(data, length, stdin);
    
        if (data[0] == '\n') {
            data[0] = '\0';
        }
    
        strtok(data, "\n");
    
        printf("DATA: %s", data);
    
        return data;
    }
    
        3
  •  1
  •   user3121023    8 年前

    另一个选项是将指针与长度一起传递。

    void read_string(char *data, int length) {
    
        fgets(data, length, stdin);
    
        if (data[0] == '\n') {
            data[0] = '\0';
        }
    
        strtok(data, "\n");
    
        printf("DATA: %s", data);
    
    }
    

    打电话

     read_string(edited_details.name, 50);
    

    那就不用用了 strcpy()

        4
  •  -1
  •   Igor S.K.    8 年前

    在C语言中没有对字符串的直接支持。大多数时候你必须处理(我应该说“内存块”而不是“数组”?)包含字符和终止符的内存块 NUL 最后。

    你的情况是这样的:

    1. 将字符串(一定数量的字符)从文件读入 一大块记忆

    2. 你希望那块内存一直有效到 你利用绳子

    3. 你用你的绳子。就这样。

    第二个是你的代码中的错误。

    char* read_string(int length) {
        /* "data" is your chunk of memory. 
         * It is an array. 
         * This array is local to containing function.
         * This array is automatically allocated on the stack.
         * This array has its address in memory. Like &data[0].
         * The address may change from one invocation of the function to another.
         */
        char data[length];
    
        /* some code here... */
    
        return data; /* true, this returns the address &data[0] */
    } 
    /* The function is done. Local variables (array by the name of "data") are gone.
     * The memory on the stack that was once allocated for local variables is  
     * considered "free-to-use" by any other function you will invoke next. This memory
     * is not valid for use outside the function any more, but you may and you do
     * return a pointer to that memory, which is dangerous to use and harmful 
     */
    

    基本上有三种方法可以解决这个问题:

    你要么声明一个全局内存块,比如说一个全局数组:

    char data[MAX_POSSIBLE_LENGTH];  /* Static storage. Global scope. */  
    char* read_string() {
        /* some code here... */
        return data; 
    } 
    

    ... 或者将数组设为固定长度的静态数组:

    char* read_string() {
        static char data[MAX_POSSIBLE_LENGTH];  /* Static storage. Function scope. */  
        /* some code here... */
        return data; 
    } 
    

    ... 或者从外部传入数组及其长度:

    char* read_string(char *data, int length) {
        /* some code here... */
        return data; 
    } 
    

    …或使用动态分配

    char* read_string(int length) {
        /* some code here... */
        char *data;
        data = malloc(length); /* Dynamic storage. Must be freed somewhere./* 
                               /* This is not an "array", now this really is a "chunk of memory" :) */
    
        return data; 
    } 
    
    void handle_modify_customer(Customer customer)
    {
        Customer edited_details;
        char * input_str_ptr;
    
        printf("\nMODIFYING DETAILS\n==============\n\n");
    
        printf("CREATE A CUSTOMER PROFILE\n=========================\n");
    
        printf("Name (%s): ", customer.name);
        strcpy(edited_details.name, input_str_ptr = read_string(50));
        free(input_str_ptr); /* Don't allow mem leakage */
    
        /* ... */
    }