2

I'm working on a linked list implementation in C to get the hang of pointers and structs. Here is the basic code for my LL data structure:

struct Node {
    void *data;
    struct Node *next;
};

struct List {
    struct Node *head;
};

void initList(struct List *list) {
    list->head = 0;
}

struct Node *addFront(struct List *list, void *data) {
    struct Node *newNode;
    newNode->data = data;
    newNode->next = list->head;
    list->head = newNode;
    return newNode;
}

Here is the test I run on it in the int main() function:

int main() {
    /* test addFront */
    double *data1;
    double *data2;
    *data1 = 10.5;
    *data2 = 10.7;
    struct List *newList;
    initList(newList);
    addFront(newList, data1);
    printf("%s\n", newList->head->data);
    addFront(newList, data2);
    printf("%s\n", newList->head->data);

    return 0;
}

My problem is that printf is not printing the output. As it stands now, it obviously doesn't print because %s doesn't match the data type, which is double. If I change the string format to %d, it gives me a segmentation fault. If I add a (double) cast, it says that the second argument has type double *, which confuses me because I thought the -> notation dereferenced a pointer.

I'm lost.

0

4 Answers 4

3

You are dereferencing data1 and data2 without assigning memory to them. Try:

double data1 = 10.5;

addFront(newList, &data1);

Alternatively you could do a malloc, although I don't think you should in this case.

Also, when you want to print them, try:

printf("%f\n", *(double *)newList->head->data);
Sign up to request clarification or add additional context in comments.

Comments

3

You are not allocating memory for your double pointers data1 and data2.

Looks like, actually, you're not allocating memory for nearly any of your pointers.

All a pointer does by itself is reference an address in memory. It does not allocate the memory necessary to support the referenced structure or variable.

If you have

double *data1; // or any other kind of pointer

you need something like

data1 = (double *) malloc(sizeof(double));

THEN you can dereference all you like, eg

*data1 = 12.34;

But without that, your referencing a pointer to the Black Hole of Calcutta.

8 Comments

Fixed all the wrong advices and syntax errors in the answer. Don't mislead the inexperienced, please.
Do you want to say that your original assignment, double data1 = (double *) malloc, was right? Assigning double * to double? Or that casting the return value of malloc() was right? Neither one was. (The change in the sizeof operator is just an additional improvement - in case the type of data1 ever changes.)
I restored my original code there is nothing wrong with explicitly casting malloc's return type, and what should be allocated is, in fact, sizeof(double). And assigning the pointer after the declaration is valid, as well.
"there is nothing wrong with explicitly casting malloc's return type" - there is; "and what should be allocated is, in fact, sizeof(double)" - for now yes, but if you change the type to struct foo, it will fail and invoke UB; "And assigning the pointer after the declaration is valid, as well." - it is, but assigning double * to a double (what you did) isn't.
I did not have "double data1 = (double ) malloc.... I had data1 = (double *) malloc(...). Casting the return value of malloc *IS perfectly legitimate. It is not required in C, but it is in C++. Reference: en.wikipedia.org/wiki/C_dynamic_memory_allocation
|
1

In addition to the 2 printf("%f") there are 4 malloc's missing:

I marked the changed lines with ###:

#include "stdlib.h"
#include "stdio.h"
struct Node {
    void *data;
    struct Node *next;
};

struct List {
    struct Node *head;
};

void initList(struct List *list) {
    list->head = 0;
}

struct Node *addFront(struct List *list, void *data) {
    struct Node *newNode = malloc(sizeof(struct Node)); //###
    newNode->data = data;
    newNode->next = list->head;
    list->head = newNode;
    return newNode;
}
int main() {
    /* test addFront */
    double *data1 = malloc(sizeof(double)); //###
    double *data2 = malloc(sizeof(double)); //###
    *data1 = 10.5;
    *data2 = 10.7;
    struct List *newList = malloc(sizeof(struct List)); //###
    initList(newList);
    addFront(newList, data1);
    printf("%f\n", *(double*)newList->head->data);//###
    addFront(newList, data2);
    printf("%f\n", *(double*)newList->head->data);//###
    // TODO: free()'s //###
    return 0;
}

5 Comments

Okay, that makes sense. The one thing still tripping me up though is the dereferencing * in the printf statements. I thought that the -> operators already dereferenced the data for structs with pointers. Or do they just dereference the structs themselves? If that's the case, then is it wrong for me to have head->data, since head is not a pointer?
head->data does (*head).data. so you deref'ed the head-pointer and have the void* data in your hand (but not yet the double).
Did you run the modified code? It runs perfectly on my machine.
Gotcha. I forgot that head was actually a pointer and not a struct Node. Yeah, works great now, thanks.
Sure. Please comment again if any questions come up.
1

What have you done to try to deal with the problem?

try using "assert.h" to ensure your assertions are right, or if statements with puts/exit.

In particular, if it doesn't print something, clearly what your are printing is not what you want to print, so somewhere along the lines, an assertion must fail, and your mind will "click" where you missed a step.

The reason I can't do this immediatly is because I am not you and I do not know what assertions you are making, so it will take me longer to place them than you would.

Also, as pointed above, you are not allocating memory for newNode, and accessing arbitrary memory, which is causing segmentation fault.

There I fixed it.

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

struct Node {
    void *data;
    struct Node *next;
};

struct List {
    struct Node *head;
};

void initList(struct List **newList)
{
    struct List* tmpList = 0;

    assert(newList != 0);
    tmpList = (struct List*)malloc(sizeof(struct List*));
    assert(tmpList != 0);
    tmpList->head = 0;
    *newList = tmpList;
}

void addFront(struct List* list, void* data)
{
    struct Node* currNode = 0;
    struct Node* prevNode = 0;

    assert(list != 0);
    assert(data != 0);

    currNode = list->head;
    while (currNode != 0) {
        prevNode = currNode;        
        currNode = currNode->next;
    }
    if (prevNode == 0) {
        list->head = (struct Node*)malloc(sizeof(struct Node));
        list->head->data = data;
        list->head->next = 0;
    } else {
        prevNode->next = (struct Node*)malloc(sizeof(struct Node));
        prevNode->next->data = data;
        prevNode->next->next = 0;
    }
}

void test(const struct List *list)
{
    const struct Node *iter;
    assert(list != 0);
    assert(list->head != 0);
    iter = list->head;

    while (iter != 0) {
        assert(iter->data != 0);
        printf("%f\n", *((double*)iter->data));
        iter = iter->next;
    }   
}

int main()
{
    double* data1 = (double*)malloc(sizeof(double));
    double* data2 = (double*)malloc(sizeof(double));
    *data1 = 10.5;
    *data2 = 10.7;
    struct List* newList = 0;

    initList(&newList);
    assert(newList->head == 0);
    puts("pass[0].");

    addFront(newList, data1);
    assert(newList->head != 0);
    assert(newList->head->data == data1);
    puts("pass[1].");

    addFront(newList, data2);
    assert(newList->head != 0);
    assert(newList->head->data == data1);
    assert(newList->head->next != 0);
    assert(newList->head->next->data == data2);
    puts("pass[2].");

    test(newList);

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.