Data Scientist to be
Data Scientist to be

Reputation: 15

Difference in these Linked List Pointer declarations

What is the difference between

  1. struct LinkedList *current = malloc(sizeof(struct LinkedList));

and

  1. struct LinkedList *current;

where

struct LinkedList{
int data;
struct LinkedList *next;
}

When I was creating a LinkedList I couldn't achieve most of my desired outcomes with the latter LinkedList declaration.

Upvotes: 0

Views: 127

Answers (3)

arfneto
arfneto

Reputation: 1765

What is the difference between struct LinkedList *current = malloc(sizeof(struct LinkedList)); and struct LinkedList *current; ?

The difference is that int the first case LinkedList pointer is initialized and in the second it is not.

Note that in C you declare a variable giving the name and the type. So in

    struct LinkedList *current = NULL 

you are declaring current as being of type struct LinkedList*

For the compiler spaces are irrevelant in this line.

If current is a pointer then *current is an instance of such type, *current is struct LinkedList. This is called de-referencing of a pointer, and this is C. But you are not declaring *current.

Writing the way you did can be misleading to beginners, by mixing cause and consequence. This is the real thing:

    struct LinkedList*                       current = NULL;

Then you see what current is being declared to be.

about LinkedList

        struct LinkedList
        {
            int                data;
            struct LinkedList* next;
        }

You can for sure name it LinkedList and write a correct program around that, with an actual linked list. But it is not an abstraction for a linked list. It seems to be just a node of a possible linked list of int.

A linked list is a collection of nodes. Possibly empty. And it has nothing to do with data. Each node by its turn contains or points to an unit of data, usually said to be a record.

If your description does not show that your code will be more complicated, less useful and harder to test. You will need loose pointers to beginning of lis, counters for size, sometimes pointers to the tail and so on.

a complete example: a linked list of strings

Here follows a complete example using a list of strings, so you can see one way of writing and using a more generic list with some useful operations. And one that can be used on an on

output

    list created

3 elements back-inserted
    #2 elements in list.
    head element: Element 000001
    tail element: Element 000002
    "Element 000001" [15]
    "Element 000002" [15]

[-----]


3 elements inserted at front
    #4 elements in list.
    head element: Element 000004
    tail element: Element 000002
    "Element 000004" [15]
    "Element 000003" [15]
    "Element 000001" [15]
    "Element 000002" [15]

[-----]


after multi insert
    #18 elements in list.
    head element: Element 000004
    tail element: Overflow
    "Element 000004" [15]
    "Element 000003" [15]
    "Element 000001" [15]
    "Element 000002" [15]
    "7" [1]
    "8" [1]
    "9" [1]
    "10" [2]
    "11" [2]
    "12" [2]
    "A" [1]
    "list" [4]
    "has" [3]
    "nodes." [6]
    "Not" [3]
    "data" [4]
    "Stack" [5]
    "Overflow" [8]

[-----]

    list deleted

main() for this example

int main(void)
{   // now test the multi insert
    const char delim   = ' ';
    Info* temp    = NULL;
    List* my_list = so_create_list();  // a new list
    for (int i = 0; i < 2; i += 1)
    {
        temp = so_factory_info();
        so_insert_back(temp, my_list);
        free(temp);
    }
    so_print_list(my_list, "\n3 elements back-inserted\n");

    for (int i = 0; i < 2; i += 1)
    {
        temp = so_factory_info();
        so_insert_front(temp, my_list);
        free(temp);
    }
    so_print_list(
        my_list, "\n3 elements inserted at front\n");
    so_multi_insert(
        my_list, delim, 3,
        "7  8   9   10   11 12", "A list has nodes. Not data", "Stack Overflow");
    so_print_list(my_list, "\nafter multi insert\n");
    so_delete_list(my_list);
    return 0;
}

The List is a list

    typedef struct
    {
        size_t size;
        Node*  tail;
        Node*  head;
    } List;  // a (possibly empty) list of nodes

This is a list. A list of nodes. Each List has its size, head and tail. metadata is the name of these things. So when you declare

        List list_set[42];

You are declaring an array of 42 lists. Each one has its size, head and tail. This is called encapsulation and makes life easier.

a node in List

        typedef struct st_node
        {
            struct st_node* prev;  // links
            struct st_node* next;

            Info* info;  // data
        } Node;

Each node is a link. We are building a linked lis after all. But each node has a pointer to some Info thing. Generic. So it can be reused.

data in the list

This is the data for the example, a mixed C/Pascal string.

typedef struct
{
    size_t length;
    char*  string;
} Thing;

typedef Thing Info;

What is Thing is not important. Note here the key typedef relating our present List content with Info: this is the way to reuse the List code. The list is always of nodes of Info. We just redefine Info each time.

List and Info operations

  • the Info thing can have pointers inside.
  • we can try to insert dynamic allocated things into the list. If we free them the list will get corrupted and the program will crash.

So the list must own all is data. If we want the list to work for generic content nodes we need to know to copy a node, how to delete a note, and maybe how to display one n-screen, at least for testing.

We do not need to know how to create one, since when one uses a list of something it sure must have or know how to create something. A list if a list of existing things.

list operations

List* so_create_list();
List* so_delete_list(List*);
int   so_insert_back(Info*, List*);
int   so_insert_front(Info*, List*);
int   so_print_list(List*, const char* msg);

This is just an example, so only these operations are implemented. See that there is no mention of Node here. And the operations are defined in simple terms: only pointers. o insert data in a list we take a pointer to the data and a pointer to the list. The print operation accepts a title, useful in testing. See the code above.

These 2 helper functions

    int so_line_insert(
        const char* string, const char delim, List* list);
    int so_multi_insert(
        List* list, const char delim, const unsigned N, ...);

are useful for inserting words by line or by group into a list.

data operations

    Info* so_copy_info(const Info*);
    Info* so_delete_info(Info*);
    Info* so_factory_info();
    int   so_print_info(Info*, const char* msg);
  • so_factory_info is here just for generating data for testing. It returns strings like "Element NNNNNN" so we can test for a few records or for thousands of them. From the example:
  Info* temp    = NULL;
  List* my_list = so_create_list();  // a new list
  for (int i = 0; i < 2; i += 1)
  {
      temp = so_factory_info();
      so_insert_back(temp, my_list);
      free(temp);
  }
  so_print_list(my_list, "\n3 elements back-inserted\n");

prints

    list created

3 elements back-inserted
    #2 elements in list.
    head element: Element 000001
    tail element: Element 000002
    "Element 000001" [15]
    "Element 000002" [15]

[-----]

complete code

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

// info, node and list

typedef struct
{
    size_t length;
    char*  string;
} Thing;

typedef Thing Info;

Info* so_copy_info(const Info*);
Info* so_delete_info(Info*);
Info* so_factory_info();
int   so_print_info(Info*, const char* msg);

typedef struct st_node
{
    struct st_node* prev;  // links
    struct st_node* next;

    Info* info;  // data
} Node;

typedef struct
{
    size_t size;
    Node*  tail;
    Node*  head;
} List;  // a (possibly empty) list of nodes

List* so_create_list();
List* so_delete_list(List*);
int   so_insert_back(Info*, List*);
int   so_insert_front(Info*, List*);
int   so_print_list(List*, const char* msg);

//
// helpers to build a list
//

int so_line_insert(
    const char* string, const char delim, List* list);
int so_multi_insert(
    List* list, const char delim, const unsigned N, ...);

int main(void)
{   // now test the multi insert
    const char delim   = ' ';
    Info* temp    = NULL;
    List* my_list = so_create_list();  // a new list
    for (int i = 0; i < 2; i += 1)
    {
        temp = so_factory_info();
        so_insert_back(temp, my_list);
        free(temp);
    }
    so_print_list(my_list, "\n3 elements back-inserted\n");

    for (int i = 0; i < 2; i += 1)
    {
        temp = so_factory_info();
        so_insert_front(temp, my_list);
        free(temp);
    }
    so_print_list(
        my_list, "\n3 elements inserted at front\n");
    so_multi_insert(
        my_list, delim, 3,
        "7  8   9   10   11 12", "A list has nodes. Not data", "Stack Overflow");
    so_print_list(my_list, "\nafter multi insert\n");
    so_delete_list(my_list);
    return 0;
}

//
// helper functions:
//

#define ST_OUT 0
#define ST_IN 1
#define MAX_SIZE 255

//
// insert string into list, word by word
//

int so_line_insert(
    const char* string, const char delim, List* list)
{
    if (list == NULL) return -1;

    char state                = ST_OUT;
    char a_word[1 + MAX_SIZE] = {0};  // buffer for word
    Info data   = {.length = 0, .string = (char*)&a_word};
    data.length = 0;
    Info* temp  = NULL;
    // break string into words and put on list
    const char* p_in  = string;
    char*       p_out = data.string;
    while (*p_in != 0)
    {
        switch (state)
        {
            case ST_OUT:  // out of word
                if (*p_in == delim)
                {  // not in word
                    ++p_in;
                }
                else
                {  // new word starting here
                    p_out       = data.string;
                    *p_out      = *p_in;
                    state       = ST_IN;
                    data.length = 1;
                    ++p_in;
                    ++p_out;
                }
                break;
            case ST_IN:  // in a word
            default:     // in a word
                if (*p_in == delim)
                {                // end of word
                    *p_out = 0;  // end string
                    temp   = so_copy_info(&data);
                    so_insert_back(temp, list);
                    free(temp);
                    state =
                        ST_OUT;  // in search for another
                    ++p_in;
                }
                else
                {  // just another letter
                    *p_out = *p_in;
                    data.length += 1;
                    ++p_in;
                    ++p_out;
                }
                break;
        };  // switch(state)
    };      // while()
    // now *p_in == 0, end of line
    if (state == ST_OUT) return 0;
    // state was ST_IN and reached end of input
    if (p_out == data.string) return 0;  // was empty
    // inserts last word into list
    *p_out = 0;
    temp   = so_copy_info(&data);
    so_insert_back(temp, list);
    free(temp);
    return 0;
}

int so_multi_insert(
    List* list, const char delim, const unsigned N, ...)
{
    if (list == NULL) return -1;
    va_list args;
    va_start(args, N);
    for (size_t i = 0; i < N; i += 1)
        so_line_insert(va_arg(args, char*), delim, list);
    va_end(args);
    return 0;
}


// list code 

// data related functions

Info* so_copy_info(const Info* orig)
{
    if (orig == NULL) return NULL;
    Info* cp = malloc(sizeof(Info));
    if (cp == NULL) return NULL;
    cp->string = malloc(1 + orig->length);
    if (cp->string == NULL)
    {
        free(cp);
        return NULL;
    }
    strcpy(cp->string, orig->string);
    cp->length = orig->length;
    return cp;  // returns the address of the copy
}

Info* so_delete_info(Info* del)
{
    if (del == NULL) return NULL;
    if (del->string != NULL) free(del->string);
    free(del);
    return NULL;
}

Info* so_factory_info()
{
    Info* one = malloc(sizeof(Info));
    if (one == NULL) return NULL;
    static int len = 1;
    one->string    = malloc(30);
    one->length =
        1 + sprintf(one->string, "Element %06d", len);
    ++len;
    return one;
}

int so_print_info(Info* info, const char* msg)
{
    if (info == NULL) return -1;
    if (msg != NULL) printf("%s", msg);
    printf(
        "    \"%s\" [%llu]\n", info->string, info->length);
    return 0;
}

// list related functions

List* so_create_list()
{
    List* list = malloc(sizeof(List));
    if (list == NULL) return NULL;
    list->head = NULL;
    list->tail = NULL;
    list->size = 0;
    fprintf(stderr, "    list created\n");
    return list;
}

List* so_delete_list(List* del)
{
    if (del == NULL) return NULL;  // no list
    for (size_t i = 0; i < del->size; i += 1)
    {  // delete nodes, 1 by 1
        Node* save = del->head->next;
        so_delete_info(del->head->info);
        free(del->head);
        del->head = save;
    };
    free(del);  // delete list
    fprintf(stderr, "    list deleted\n");
    return NULL;
}

// inserts 'info' at the tail of the list
int so_insert_back(Info* info, List* list)
{
    if (list == NULL) return -1;  // no list
    Node* node = (Node*)malloc(sizeof(Node));
    if (node == NULL) return -2;  // no alloc
    node->info = so_copy_info(info);
    if (list->size == 0)  // first node
    {
        node->next = NULL;
        list->size = 1;
        list->head = node;
        list->tail = node;
        return 0;  // 1st node
    };
    node->next       = NULL;
    list->tail->next = node;
    list->tail       = node;
    list->size += 1;
    return 0;
}

// inserts 'info' at the head of the list
int so_insert_front(Info* info, List* list)
{
    if (list == NULL) return -1;  // no list
    Node* node = (Node*)malloc(sizeof(Node));
    if (node == NULL) return -2;  // no alloc
    node->info = so_copy_info(info);
    if (list->size == 0)
    {
        node->next = NULL;
        list->size = 1;
        list->head = node;
        list->tail = node;
        return 0;  // 1st node
    };
    node->next = list->head;
    list->head = node;
    list->size += 1;
    return 0;
}

int so_print_list(List* list, const char* msg)
{
    if (list == NULL)
    {
        printf("No valid list!\n");
        return 0;
    }
    if (list->size == 0)
    {
        printf("list is empty\n");
        return 0;
    }
    if (msg != NULL) printf("%s", msg);
    printf(
        "\
    #%llu elements in list.\n\
    head element: %s\n\
    tail element: %s\n",
        list->size, list->head->info->string,
        list->tail->info->string);
    Node* p = list->head;
    for (size_t i = 0; i < list->size; i += 1)
    {
        so_print_info(p->info, NULL);
        p = p->next;
    }
    printf("\n[-----]\n\n");
    return 0;
}

Upvotes: 0

Pejman Khaleghi
Pejman Khaleghi

Reputation: 30

If you define a pointer, it doesn't have an address. But if you equal it with malloc it has an address in memory with that size, and you can fill that addresses with some data. If you use it without malloc and fill it in, you will get segmentation error in run time.

Run this code to see the result:

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

struct LinkedList{
    int data;
    struct LinkedList *next;
};

int main(int argc, char const *argv[])
{
    
    struct LinkedList *current = malloc(sizeof(struct LinkedList));
    current->data = 1;
    printf("%d\n", current->data );

    struct LinkedList *current1;
    current1->data = 2;
    printf("%d\n", current1->data );

    return 0;
}

Upvotes: 1

Eric Postpischil
Eric Postpischil

Reputation: 223673

struct LinkedList *current; defines an object named current of type struct LinkedList * and does not specify an initial value for it. If it appears outside a function, the object is initialized to a null pointer. if it appears inside a function, it is not initialized, and the object’s value is indeterminate.

struct LinkedList *current = malloc(sizeof(struct LinkedList)); defines an object named current as above, calls malloc to allocate enough memory for an object of type struct LinkedList, and initializes current with the return value of malloc. This would normally appear inside a function, so that malloc is executed during program execution.

When I was creating a LinkedList I couldn't achieve most of my desired outcomes with the 2nd mentioned code.

That is because current was not pointing to reserved memory, so attempting to use it as a pointer did something bad.

Upvotes: 3

Related Questions