HolyMac
HolyMac

Reputation: 57

Dereferencing pointer kills its value?

I have a method that should add a 'node' to the end of an array. When i've found a place to store the value (which works), this strangely removes the value from the old address.

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'
}

This is probably very easy, but I am new to C and the whole pointer thing ....

I call the method like this:

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);
}

My node is defined like this:

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];
}

Can someone please point out what I am doing wrong?

Upvotes: 1

Views: 136

Answers (1)

Greg Hensley
Greg Hensley

Reputation: 634

You are seeing this behavior because you have a type mismatch between the definition of appendAtEndOfArray and the arguments you are passing into it. NuclearGhost pointed this out in the comments. As he stated, the function declaration needs to be changed to

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

The "Bad access" error you saw after changing the array parameter comes from the while loop. After correcting the function declaration, arrayPointer[i] is has type struct node *. Since you are now accessing the struct member through a pointer, the . (dot) operator must be changed to ->:

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

Now you can take twalberg's advice to assign the value of item directly:

arrayPointer[i] = item;

There is one more problem that needs to be corrected: arrayPointer[i] is a pointer type, so it can have a null value. That condition needs to be checked before dereferencing the pointer, or the program will likely crash from a segmentation fault:

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

Edit: Additional explanation of the "why" behind your concern that the code "strangely removes the value from the old address."

In your original code, when you passed sourceNode->children into appendAtEndOfArray, the compiler issues a warning because of the type mismatch but generates the code anyway. It can do this because the value you pass in and the value the function expects are both memory addresses -- the "type" of the pointer simply determines how the compiler treats the memory the pointer refers to, so no actual data conversion has to be performed.

On my machine, a 32-bit x86 platform, pointers are 4-bytes and your struct node type is 252 bytes (due to padding the char and bool types up to 4-bytes, each). When appendAtEndOfArray assigns item to the first element of arrayPointer, as (from the original code):

arrayPointer[i]=*item;

The system copies the 252 bytes of data from the struct to a memory location that was intended to hold a 4-byte pointer. As a result, the next 248 bytes after arrayPointer[i] are overwritten. Since the nodes are allocated in an array, that means that part of the next node in the nodes array will be overwritten.

For example, consider the call

appendAtEndOfArray(destinationNode,sourceNode->children);

Assume the sourceNode->children array was empty, so destinationNode would be assigned to the 0th element. Since the assignment is actually writing the entire struct contents to the memory location of the 0th element, this would overwrite all 30 elements of sourceNode->children (120 bytes) as well as all of sourceNode->parents (another 120 bytes), leaving another 12 bytes of data that spills into the next element in the nodes array, which (on my machine) covers both the name and visited members.

Upvotes: 3

Related Questions