【问题标题】:Printing (Char*)(Void*) works in main program but not function打印 (Char*)(Void*) 在主程序中有效但不起作用
【发布时间】:2015-06-30 14:04:15
【问题描述】:

我有一个名为nodes 的结构数组。每个节点都包含一个 void 指针字段。

在一个函数中,我获取特定节点并将 void 指针分配给一个字符串,该字符串包含已转换为二进制的十进制结果。

问题是在函数中访问和打印转换为 char* 的 void 指针可以正常工作,以将 void* 分配给新的 char* 并且在返回主函数时打印正常。但是,当我尝试在以节点 [] 和数组索引作为参数的单独函数中打印它时,它无法正确打印。

为了帮助阐明混淆,这是我的程序的简化版本:

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

#define listSize 100

typedef struct Node
{
    union{
        void *dataPtr;
        int countr;
    }dataItem;
    int  link;
}node;

void loadData(node*);
void printFunc(node[], int);

void main()
{
    struct Node Stack[listSize];

    loadData(&Stack[0]);

    //This prints fine
    char * temp;
    temp = (char*)(Stack[0].dataItem.dataPtr);
    printf("\nMain():  Stack[empty].dataItem.dataPtr = %s\n", temp);


    printFunc(Stack, 0);

}

void loadData(node* link){
    char string[220];

    int n, c, k, i;

    printf("Enter an integer: ");
    scanf("%d", &n);

    i = 0;

    for (c = 31; c >= 0; c--)
    {
        k = n >> c;

        if (k & 1){
            string[i] = '1';
            i++;
        }
        else{
            string[i] = '0';
            i++;
        }
        if (c == 0){ string[i] = '\0'; }//end the string
    }

    link->dataItem.dataPtr = &string;

    //This prints fine:
    printf("\nLoadData(): link->dataItem.dataPtr is now %s\n", (char *)(link->dataItem.dataPtr));
}


void printFunc(node Stack[], int newLink){

    //This does not work!
    char* temp;
    temp = (char*)(Stack[newLink].dataItem.dataPtr);
    printf("\npush():  Stack[newLink].dataItem.dataPtr %s\n", temp);
}

输出:

我也在 Visual Studios 2012 中进行编译。我知道有时 GCC 中指向 Microsoft C 编译器的指针可能会有所不同。

我在写什么导致程序无法在 printFunc 函数中将 void* 转换为 char* 打印?

【问题讨论】:

  • link-&gt;dataItem.dataPtr = &amp;string; - 这行不通。首先,您不需要&amp;,因为本地数组将衰减为指针。其次,更重要的是,您将本地数组分配给要在其范围之外使用的指针。这是未定义的行为。

标签: c arrays pointers void-pointers


【解决方案1】:

这就是您的代码中发生的情况:

当您调用 LoadData 函数时,它会在堆栈上分配 string[220]。然后堆栈看起来像这样:

[main variables] [LoadData variables, including string[220]] <-- HEAD

然后加载数据退出。当它退出时,它将堆栈指针移回。此时您的堆栈如下所示:

[main variables] <-- HEAD [LoadData variables, including string[220]]

请注意,在您的情况下,string 在技术上仍然存在,可以读取和访问,但这纯粹是巧合,取决于您的编译器实现。其他一些编译器可能会立即删除它,或者以其他方式对其进行优化。一旦函数退出,就不应该有指针指向堆栈上分配的变量!您的代码违反了这一点,泄漏了指向堆栈分配的string 的指针。这时候你的代码已经在危险区了!在您访问该指针的那一刻,您的代码可能会崩溃,您的计算机可能会着火,或者世界可能会不复存在。

但在您的特定情况下,string 恰好仍然可以访问,因此当您从main 打印它时,它似乎打印正确。它给你一种一切都很好的错觉。当您调用printFunc 时,幻觉消失了,因为现在它将占用string 所在的堆栈空间!

[main variables] [printFunc variables] <-- HEAD

请注意,string 现在不见了!但是您的 Stack 变量仍然指向该内存,现在它包含垃圾!

如何解决?好吧,如果你打算从函数中返回一些东西,你需要在调用端为它分配字符串:

int main {
    ...
    char string[220];
    LoadData(&Stack[0], string); // and make LoadData use the argument string instead of creating its own
    ...
}

或者让LoadData在堆上分配字符串:

char* LoadData (...) {
    char* string = malloc(220);
    ...
}

在这种情况下不要忘记稍后在main 中释放它:

int main() {
    loadData(&Stack[0]);
    ...
    free(Stack[0].dataItem.dataPtr);
}

【讨论】:

    【解决方案2】:

    您正在将dataPtr 分配给在此行的堆栈上声明的字符串:

    link->dataItem.dataPtr = &string;
    

    一旦超出范围,您就会有未定义的行为。您需要为堆上的字符串分配内存。

    char *string = malloc(220);
    
    ... 
    
    //then assign it directly
    link->dataItem.dataPtr = string;
    

    您还需要一个函数来在完成后释放所有数据。

    【讨论】:

    猜你喜欢
    • 2018-08-06
    • 1970-01-01
    • 1970-01-01
    • 2020-05-19
    • 1970-01-01
    • 2016-10-13
    • 2015-06-03
    • 2015-04-12
    • 1970-01-01
    相关资源
    最近更新 更多