【问题标题】:How to alter pointer value within a function in C如何在C中更改函数中的指针值
【发布时间】:2020-10-07 22:45:28
【问题描述】:

我想知道您是否可以帮助我克服我在使用 C 语法时遇到的障碍。我写了函数:

binary_and_trim(char *password, unsigned *key1, unsigned *key2)

这已经实现了将提供的字符串转换为二进制并修剪掉前导零的目标。我已将我的 key1 和 key2 指针分配给正确的索引。但是,当我返回主函数时,所有值都丢失了。

我认为问题在于,当我将 *key1/*key2 指针传递给函数时,它只会收到它们的副本。但是,由于我是 C 新手,我不知道如何解决它?

我创建了一个 for 循环来帮助我进行测试/调试。

#include <stdio.h>
#include <string.h>
void binary_and_trim(char *password, unsigned *key1, unsigned *key2);
unsigned int get_n_bits(unsigned *bits, int width, int index);


int main(int argc, const char * argv[]) {
    unsigned *key1 = NULL;
    unsigned *key2 = NULL;
    binary_and_trim("password", key1, key2);
    //This test fails with a EXC_BAD_ACCESS error
    for(int i = 0 ; i < 28; i++){
        printf("key1[%d] %u    key2[%d] %d\n", i, key1[i], i, (key2 + i));
    }
}

void binary_and_trim(char *password, unsigned *key1, unsigned *key2){
    char c;
    int count = 0;
    unsigned tmp;
    unsigned long len = strlen(password);
    unsigned trimmedbinary[len * 7];
    
    for(int i = 0; i < len; i++){
        c = *(password + i);
    for( int j = 6; j >= 0; j--) {
        tmp = 0;
        if(c >> j & 1){
            tmp = 1;
            }
        *(trimmedbinary + count) = tmp;
        count++;
    }
    }
    key1 = trimmedbinary;
    key2 = &trimmedbinary[28];
    
    //This test works correctly!!!
    for(int i = 0 ; i < 28; i++){
        printf("key1[%d] %d    key2[%d] %d\n", i, *(key1 + i), i, *(key2 + i));
    }
}

【问题讨论】:

标签: c syntax


【解决方案1】:

我认为问题在于,当我将 *key1/*key2 指针传递给函数时,它只会收到它们的副本。

是的,没错。指针只是整数,整数被复制。你可以用一个指向指针的指针来解决这个问题,一个“双指针”。

但是,还有另一个问题。 trimmedbinary 正在使用 stack/automatic memory。 “自动”意味着一旦函数退出它将被释放。一旦函数返回key1key2 将指向释放的内存。 trimmedbinary 必须在 heap/dynamic memory 中声明为 malloc

void binary_and_trim(char *password, unsigned int **key1, unsigned int **key2){
    unsigned int *trimmedbinary = malloc(len * 7 * sizeof(unsigned int));

    ...

    *key1 = trimmedbinary;
    *key2 = &trimmedbinary[28];

    for(int i = 0 ; i < 28; i++) {
        printf("key1[%d] %u, key2[%d] %u\n", i, (*key1)[i], i, (*key2)[i]);
    }

    return;
}

并将其称为binary_and_trim("password", &amp;key1, &amp;key2);

【讨论】:

  • 我听取了您的建议并为 trimmedbinary 实现了 malloc。那么,我是否必须在某处释放该内存?我主要做吗?或没有?仍在为精细地处理内存而苦苦挣扎。
  • @Learning-C 是的,完成后您必须free(key1)。不要释放key2,因为它指向同一个内存。
【解决方案2】:

更新:我回答了关于如何更改指针值的问题,但我没有注意到代码中的内存问题。请参考this answer

指针本身就是变量。您可能已经知道,使用指针,您可以更改存储在指针指向的变量中的值。因此,您需要使用指向指针的指针来更改存储在指针中的值(内存地址)。

将您的函数签名更改为:

void binary_and_trim(char *password, unsigned **key1, unsigned **key2)

致电:

binary_and_trim("password", &key1, &key2);

并将函数定义中的key1key2 替换为*key1*key2

【讨论】:

    【解决方案3】:

    您的问题是用于填充键数据trimmedbinary 的变量仅分配给函数binary_and_trim 的范围。也就是说,当你在函数内部打印时

    void binary_and_trim(char *password, unsigned **key1, unsigned **key2){
        ...
        unsigned trimmedbinary[len * 7]; // <--
        
        ...
    
        *key1 = trimmedbinary; // <--
        *key2 = &trimmedbinary[28]; // <--
        
        //This test works correctly!!!
        for(int i = 0 ; i < 28; i++){
            printf("key1[%d] %d    key2[%d] %d\n", i, *(key1 + i), i, *(key2 + i));
        }
    }
    

    它之所以有效,是因为您的key1 指针试图访问的数据仍然存在。 但是,当您从函数返回到 main 时,key1key2 仍然指向您在 binary_and_trim 中初始化的缓冲区,因为超出范围而不再有效。

    我建议你在main中创建一个缓冲区并作为参数传递,

    int main(int argc, const char * argv[]) {
        const char* password = "password";
        unsigned long len = strlen(password);
        unsigned buffer[len * 7]; // <-- Add buffer here
        unsigned *key1 = NULL;
        unsigned *key2 = NULL;
        binary_and_trim(password, &key1, &key2, &buffer, len * 7);
    
        //This test succeeds
        for(int i = 0 ; i < 28; i++){
            printf("key1[%d] %u    key2[%d] %d\n", i, key1[i], i, (key2 + i));
        }
    }
    
    void binary_and_trim(char *password, unsigned **key1, unsigned **key2, unsigned** buffer, size_t buff_size){
        char c;
        int count = 0;
        unsigned tmp;
        ...
        //Use *buffer instead of trimmedbinary
    
        //Check if buff_size matches len(password) * 7
    

    或者,创建缓冲区heap allocated(不要忘记稍后释放())。

    我认为问题在于当我通过 *key1/*key2 指向函数的指针,它只接收它们的副本。

    代码也已经修改了。

    【讨论】:

    • @AndreasWenzel 是的,你是对的,应该通过。尽管在这种情况下可以假设总是传递一个具有密码大小的缓冲区,但毫无疑问仍然是不安全的。
    • 在您发表评论之前,我已经删除了我的评论,因为我注意到您确实确保缓冲区足够大。一般来说,我认为接收缓冲区作为函数参数的函数也应该接收具有缓冲区大小的函数参数是一个好主意,这样函数就可以确保不会出现buffer overflow。但是,在这种情况下,可能没有必要。
    • @AndreasWenzel 我编辑了代码,因为检查缓冲区大小似乎完全合理,否则您可以通过使用错误的缓冲区来检测是否发生任何错误,例如。总是通过sizeof(buffer)
    【解决方案4】:

    哇!谢谢大家!我终于把它启动并运行了(在我的头撞到墙上 4 个小时之后)。我不能开始说你们是多么的紧密。 我意识到我有很多东西要学习 C 的粒度内存访问(我习惯于 Java)。我迫不及待想像你们一样成为真正的巫师!

    【讨论】:

    • 是的,在 C 语言中,您必须比其他语言更加小心处理内存。在大多数其他语言中,该程序将为您进行内存管理,并在您做错事时立即警告您。然而,在 C 语言中,即使你做错了,它有时会起作用,有时不会,因此调试起来要困难得多。另一方面,C 的优势在于代码可以运行得更快(如果你高效地编程的话),因为你的程序不必为你做内存管理。
    猜你喜欢
    • 2012-03-13
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多