【问题标题】:String isn't getting mutated after using a function, despite being mutated inside the function c language尽管在函数 c 语言中发生了变异,但字符串在使用函数后没有发生变异
【发布时间】:2021-01-13 06:58:43
【问题描述】:

快速总结 - 我正在测试我的函数,为此,我决定编写自己的 add_as_first() 和 add_as_last() 函数,它们通过创建一个新的字符串数组并将字符添加到旧字符串数组中一个副本,不使用默认的 c 语言字符串函数。出于某种原因,二分之一的字符串数组的行为不像我希望的那样。我添加了用于调试的 strlen() 函数,我可以看到在 print_file 函数中,我的字符串数组非常健康,并且随着函数的循环而变大。但是由于某种原因,当函数结束时,字符串“res”之一的长度恢复为 0,而第二个字符串“body”得到了适当的变异并具有应该具有的值。它们之间的区别在于,“res”是通过在 print_file 循环函数中使用 add_as_last() 函数来填充的,而“body”是通过在 print_file() 函数内部使用 add_as_first() 函数来填充的。 )。知道为什么它会这样吗?这看起来像是范围界定问题,但考虑到“body”和“res”的使用方式大致相同,它应该可以正常工作。

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

#define x_size 4
#define y_size 4


char* add_as_first(char *given_arr, char given_char, int check) {
    int strLen = 0;
    while (given_arr[strLen] != '\0') {
        strLen++;
    }
    char *new_arr;
    new_arr = (char*)malloc((strLen + 1 + 1) * sizeof(char));
    
    new_arr[0] = given_char;
    int i = 0;

    for (; i < strLen; i++) {
        new_arr[i + 1] = given_arr[i]; 
    }
    new_arr[i + 1] = '\0';
    if ((i + 1 + 1) != (strLen + 1 + 1)) {
        printf("WE GOT A MEMORY LEAK OUT THERE\n");
    }

    if (check != 0) {
        free(given_arr);
    }
    return new_arr;
}

char* add_as_last(char *given_arr, char given_char, int check) {
    int strLen = 0; 
    while (given_arr[strLen] != '\0') {
        strLen++;
    }
    char* newString = (char*)malloc((strLen + 1 + 1) * sizeof(char));
    int i = 0;

    for(; i < strLen; i++) {
        newString[i] = given_arr[i];
            
    }

    newString[i] = given_char;
    newString[i + 1] = '\0';
    if ((i + 1 + 1) != (strLen + 1 + 1)) {
        printf("WE GOT A MEMORY LEAK OUT THERE\n");
    }
    return newString;
}

void forming_list(int file_arr[][y_size], int i, int j, char body[]) {
    char thing = '0';
    thing = j + '0';
    body = add_as_first(body, thing, 1);
    body = add_as_first(body, ',', 1);
    thing = i + '0';
    body = add_as_first(body, thing, 1);
    body = add_as_first(body, ' ', 1);
    if ((file_arr[i][j] == 1) || (file_arr[i][j] == 5)) {
        if (j < x_size - 1) {
            forming_list(file_arr, i, j + 1, body);
        } else {
            forming_list(file_arr, i + 1, 0, body);
        }
    } else if ((file_arr[i][j] == 2) || (file_arr[i][j] == 6)) {
        if (j != 0) {
            forming_list(file_arr, i, j - 1, body);
        } else {
            forming_list(file_arr, i - 1, x_size - 1, body);
        } 
    } else if ((file_arr[i][j] == 3) || (file_arr[i][j] == 7)) {
        if (i == 0) {
            forming_list(file_arr, y_size - 1, j, body);
        } else {
            forming_list(file_arr, i - 1, j, body);
        } 
    } else if ((file_arr[i][j] == 4) || (file_arr[i][j] == 8)) {
        if (i == y_size - 1) {
            forming_list(file_arr, 0, j, body);
        } else {
            forming_list(file_arr, i + 1, j, body);
        } 
    } 
}

void print_file(int file_arr[][y_size], char *res, char *body) {
    int i = 0;
    int j = 0;
    for(i; i < y_size; i++) {
        for (j; j < x_size; j++) {
            if ((file_arr[i][j] >= 5) && (file_arr[i][j] <= 9)) {
                res = add_as_last(res, '#', 1);
            } 
            else if ((file_arr[i][j] >= 1) && (file_arr[i][j] <= 4)) {
                res = add_as_last(res, '#', 1);
                forming_list(file_arr, i, j, body); 
            } else {
                res = add_as_last(res, 'x', 1);
            }
        }
        res = add_as_last(res, '\n', 1);
        printf("Length of string = %zu \n",strlen(res));
        j = 0;
    }
}

int main()
{

    int disp[x_size][y_size] = {
    {0, 1, 8, 0},
    {0, 0, 8, 0},
    {0, 9, 6, 0},
    {0, 0, 0, 0}
};
    char *res;
    char *body;
    res = (char*)malloc((1) * sizeof(char));
    body = (char*)malloc((1) * sizeof(char));

    res[0] = '\0';
    body[0] = '\0';
    printf("Length of string = %zu \n",strlen(res));
    print_file(disp, res, body);
    printf("Length of string = %zu \n",strlen(res));
    printf("%s\n", res);
    printf("%s\n", body);
    return 0;
}

输出是:

Length of string = 0 //创建res线后

字符串长度 = 5 //第一个外层for循环之后

字符串长度 = 10 //第二个外层for循环之后

字符串长度 = 15 //第三个外层for循环之后

Length of string = 20 //在第四个外部for循环之后,就在退出函数之前

Length of string = 0 //打印“res”字符串之前的函数外

                  //Printing empty "res" string

2,1 2,2 1,2 0,2 0,1 //打印正确填充的“body”字符串

为什么“body”和“res”的值在我的程序完成工作后却如此不同,尽管如此相似?

【问题讨论】:

  • 记住函数参数是按值传递的。也就是说,参数的值被复制到函数参数变量中。修改这个副本当然不会修改原件。想想电话print_file(disp, res, body)。并做一些关于在C中模拟通过引用传递的研究。
  • 我知道按引用传递/按值传递的差异,并且我知道我可以只返回“res”并且它可以正常工作,但我试图理解差异,为什么“body " 正在按预期工作,而 "res" 在内存中丢失了?
  • 您的代码中很可能存在某种未定义的行为。尝试缩减您的程序(很多!),将minimal reproducible exampleminimal 部分发挥到极致。确保它有效。然后一次添加一小部分,构建(启用详细的额外警告)和测试。当问题发生时,您可以缩小范围并仅调试小的非工作位。
  • 另外请阅读the help pages,采取SO tour,阅读How to Ask,以及this question checklist。您的问题是一堵难以阅读的文字墙。
  • 我知道我应该避免问可重复的问题,但我的问题是我不知道如何制定它。如果我更熟悉 C 中的这些恶作剧和术语,我想我就不会费心提出烦人的问题了。

标签: c string pointers scope mutation


【解决方案1】:

您的 print_file 函数正在修改 res,但新指针永远不会传回 main。
所以 main 的res 仍然指向(长期破坏的)原始res,它为strlen(或其他任何东西)产生未定义的行为。

您应该通过引用传递res 指针或将指针传递给它,以便修改返回到main。

【讨论】:

  • 我是这么想的,但是为什么指向“body”的指针没有被破坏并正常工作?
  • @Jermog — 未定义的行为令人讨厌。它不必打破。它不必工作。它没有意义。它不必是一致的。
  • @Jonathan Leffler 所以,我的“body”指针和“res”一样受到损害,我很幸运(或者甚至因为不了解真正原因而不幸)它有效,我应该确保“res”和“body”指针都以某种方式返回?
  • body 被销毁。如果您通过静态分析器运行它,您甚至会在第 100 行 (forming_list(file_arr, i, j, body)) 收到一条警告,指出这会在释放后使用内存。
  • 我希望我有一个很好的资源来指导你学习 C;我已经长大了,可以通过阅读 K&R Book 来学习它(你可能不应该那样学习 C;那是很久以前的事了)。但我确实建议您坐下来学习一门课程或一本书来学习 C 内存管理。看这段代码,感觉你的背景是使用更自动内存管理的语言。您将resbody 定义为1 元素malloc 块对于C 来说是一种非常不自然的方法。
猜你喜欢
  • 1970-01-01
  • 2015-12-15
  • 2022-06-13
  • 1970-01-01
  • 1970-01-01
  • 2019-02-11
  • 1970-01-01
  • 2015-02-01
  • 1970-01-01
相关资源
最近更新 更多