取消引用指针会杀死它的值?

时间:2012-10-30 18:21:22

标签: c pointers

我有一个方法应该在数组的末尾添加一个“节点”。当我找到一个存储值的地方(有效)时,这会奇怪地从旧地址中删除该值。

void appendAtEndOfArray(struct node * item,struct node * arrayPointer){
    int i=0;
    while (arrayPointer[i].name!='\0') {
        i++;
    }
    arrayPointer[i]=*item; // after this the original memory at &item is changed to '\0'
}

这可能很容易,但我是C和整个指针的新手......

我这样称呼方法:

void addVertice(char source, char destination,int cost){
    struct node * sourceNode = addNode(source);
    struct node * destinationNode = addNode(destination);
    appendAtEndOfArray(destinationNode,sourceNode->children);
    appendAtEndOfArray(sourceNode,destinationNode->parents);
}

我的节点定义如下:

struct node            {
    char name;
    bool visited;
    int distance;
    struct node *children[30];
    struct node *parents[30];
} nodes[30];

struct node * addNode(char name){
    int n=getNodeByName(name); // if exists reuse
    if (n==-1) {
        n=++lastNodeIndex;
    }
    nodes[n].name = name;
    nodes[n].visited=false;
    return &nodes[n];
}

有人可以指出我做错了吗?

1 个答案:

答案 0 :(得分:3)

您看到此行为是因为appendAtEndOfArray的定义与您传递给它的参数之间存在类型不匹配。 NuclearGhost在评论中指出了这一点。正如他所说,函数声明需要更改为

void appendAtEndOfArray(struct node * item, struct node * arrayPointer[])

更改数组参数后看到的“错误访问”错误来自while循环。在更正函数声明后,arrayPointer[i]的类型为struct node *。由于您现在通过指针访问struct成员,因此.(点)运算符必须更改为->

while (arrayPointer[i]->name != '\0') {

现在,您可以采取twalberg的建议直接指定item的值:

arrayPointer[i] = item;

还有一个问题需要纠正:arrayPointer[i]是指针类型,因此它可以具有空值。在取消引用指针之前需要检查该条件,否则程序可能会因分段错误而崩溃:

while (arrayPointer[i] && (arrayPointer[i]->name != '\0')) {

编辑:关于代码“奇怪地删除旧地址中的值。”的背后“原因”的补充说明。

在原始代码中,当您将sourceNode->children传递给appendAtEndOfArray时,编译器会因类型不匹配而发出警告,但无论如何都会生成代码。它可以这样做,因为你传入的值和函数所期望的值都是内存地址 - 指针的“类型”只是确定编译器如何处理指针所指的内存,所以不必实际的数据转换进行。

在我的机器上,一个32位的x86平台,指针是4字节,你的struct node类型是252字节(由于填充char和bool类型最多4个字节,每个)。当appendAtEndOfArrayitem分配给arrayPointer的第一个元素时,(来自原始代码):

arrayPointer[i]=*item;

系统将252字节的数据从结构复制到一个用于保存4字节指针的内存位置。结果,覆盖arrayPointer[i]之后的下一个248个字节。由于节点是在数组中分配的,这意味着nodes数组中下一个节点的一部分将被覆盖。

例如,考虑调用

appendAtEndOfArray(destinationNode,sourceNode->children);

假设sourceNode->children数组为空,因此destinationNode将被分配给第0个元素。由于赋值实际上是将整个struct内容写入第0个元素的内存位置,因此会覆盖sourceNode->children(120字节)的所有30个元素以及sourceNode->parents的所有元素(另外120个字节) ,留下另外12个字节的数据溢出到节点数组中的下一个元素,(在我的机器上)覆盖了namevisited成员。