Home > Back-end >  why gets my data lost / possible problem with pointers
why gets my data lost / possible problem with pointers

Time:09-01

i'm trying to adapt the example code from this site https://www.codesdope.com/blog/article/making-a-queue-using-linked-list-in-c/

but i can't get it to work properly, when changing the node datatype to somesting "more complex" thought it would be simple but when i call display it outputs all "0"

// #include "include/main.h"
#include <time.h>

#include <stdio.h>
#include <stdlib.h>
#define QUEUE_SIZE 30

typedef float dataArray[20];

struct node
{
    dataArray *data;
    struct node *next;
};
typedef struct node node;

struct queue
{
    int count;
    node *front;
    node *rear;
};
typedef struct queue queue;

void initialize(queue *q);
int is_empty(queue *q);
void enqueue(queue *q, dataArray value);
void dequeue(queue *q, dataArray *outputArray);
void display(node *head);

float random_float();

void initialize(queue *q)
{
    q->count = 0;
    q->front = NULL;
    q->rear = NULL;
}

int is_empty(queue *q)
{
    return (q->rear == NULL);
}

void enqueue(queue *q, dataArray value)
{
    if (q->count == QUEUE_SIZE)
    {
        dataArray tmp;
        dequeue(q, &tmp);
        free(tmp);
    }
    node *tmp;
    tmp = malloc(sizeof(node));
    printf("[enqueue] sizeof node %d\r\n", (uint) sizeof(node));
    // memcpy(tmp->data, value, sizeof(value));
    tmp->data = &value;
    tmp->next = NULL;
    
    printf(" contents of value in enqueue\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", value[i]);
    }

    printf(" contents of tmp-node in enqueue\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", tmp->data[i]);
    }

    if (!is_empty(q))
    {
        q->rear->next = tmp;
        q->rear = tmp;
    }
    else
    {
        q->front = q->rear = tmp;
    }
    q->count  ;
}

void dequeue(queue *q, dataArray *outputArray)
{
    node *tmp;
    outputArray = q->front->data;
    printf("dequeue output before freeing memory\r\n===========\r\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", outputArray[i]);
    }
    printf("[dequeue] size %d - %d\r\n", (uint) sizeof(q->front->data), (uint) sizeof(q->front->data[0]));
    tmp = q->front;
    q->front = q->front->next;
    q->count--;
    free(tmp);
}

void display(node *head)
{
    if (head == NULL)
    {
        printf("NULL\r\n");
    }
    else
    {
        for (int i = 0; i < 20; i  )
        {
            printf("%f\n", head->data[i]);
        }

        display(head->next);
    }
}

float random_float()
{
    srand((unsigned int)time(NULL));

    float a = 50.0;
    return ((float)rand() / (float)(RAND_MAX)) * a;
}

int main()
{
    queue *q;
    q = malloc(sizeof(queue));
    initialize(q);
    srand((unsigned int)time(NULL));

    dataArray tmp;
    for (int i = 0; i < 20; i  )
    {
        tmp[i] = random_float();
    }
    printf("display dataArray before fill\r\n===========\r\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", tmp[i]);
    }

    enqueue(q, tmp);
    
    printf("Queue display after init and fill\r\n===========\r\n");
// THIS WILL PRINT 0 <<<<--------------------------------------- what it shouldn't
    display(q->front);

    printf("Queue before dequeue\r\n===========\r\n");
    printf("Queue #1  element count: %d\r\n", q->count);
    // Nächsten Queue Eintrag holen
    dataArray *queData = malloc(sizeof(dataArray));
    dequeue(q, queData);
    printf("Queue after dequeue\r\n===========\r\n");
    printf("Queue #1  element count: %d\r\n", q->count);

    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", queData[i]);
    }
    return 0;
}

does anybody know what i'm doing wrong?

(also first i wanted to give enqueue the pointer of my temp-array so it don't has to copy it but that worked even worse)

EDIT this is the version where i'm passing the pointer of the array to enqueue, but in this version even within enqueue the output is not correct (only for the first value of the array)

// #include "include/main.h"
#include <time.h>

#include <stdio.h>
#include <stdlib.h>
#define QUEUE_SIZE 30

typedef float dataArray[20];

struct node
{
    dataArray *data;
    struct node *next;
};
typedef struct node node;

struct queue
{
    int count;
    node *front;
    node *rear;
};
typedef struct queue queue;

void initialize(queue *q);
int is_empty(queue *q);
void enqueue(queue *q, dataArray *value);
void dequeue(queue *q, dataArray *outputArray);
void display(node *head);

float random_float();

void initialize(queue *q)
{
    q->count = 0;
    q->front = NULL;
    q->rear = NULL;
}

int is_empty(queue *q)
{
    return (q->rear == NULL);
}

void enqueue(queue *q, dataArray *value)
{
    if (q->count == QUEUE_SIZE)
    {
        dataArray tmp;
        dequeue(q, &tmp);
        free(tmp);
    }
    
    printf(" contents of value in enqueue\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", *value[i]);
    }
    
    node *tmp;
    tmp = malloc(sizeof(node));
    printf("[enqueue] sizeof node %d\r\n", (uint) sizeof(node));
    // memcpy(tmp->data, &value, sizeof(value));
    tmp->data = value;
    tmp->next = NULL;
    


    printf(" contents of tmp-node in enqueue\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", tmp->data[i]);
    }

    if (!is_empty(q))
    {
        q->rear->next = tmp;
        q->rear = tmp;
    }
    else
    {
        q->front = q->rear = tmp;
    }
    q->count  ;
}

void dequeue(queue *q, dataArray *outputArray)
{
    node *tmp;
    outputArray = q->front->data;
    printf("dequeue output before freeing memory\r\n===========\r\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", outputArray[i]);
    }
    printf("[dequeue] size %d - %d\r\n", (uint) sizeof(q->front->data), (uint) sizeof(q->front->data[0]));
    tmp = q->front;
    q->front = q->front->next;
    q->count--;
    free(tmp);
}

void display(node *head)
{
    if (head == NULL)
    {
        printf("NULL\r\n");
    }
    else
    {
        for (int i = 0; i < 20; i  )
        {
            printf("%f\n", head->data[i]);
        }

        display(head->next);
    }
}

float random_float()
{
    srand((unsigned int)time(NULL));

    float a = 50.0;
    return ((float)rand() / (float)(RAND_MAX)) * a;
}

int main()
{
    queue *q;
    q = malloc(sizeof(queue));
    initialize(q);
    srand((unsigned int)time(NULL));

    dataArray tmp;
    for (int i = 0; i < 20; i  )
    {
        tmp[i] = random_float();
    }
    printf("display dataArray before fill\r\n===========\r\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", tmp[i]);
    }

    enqueue(q, &tmp);
    
    printf("Queue display after init and fill\r\n===========\r\n");
    display(q->front);

    printf("Queue before dequeue\r\n===========\r\n");
    printf("Queue #1  element count: %d\r\n", q->count);
    // Nächsten Queue Eintrag holen
    dataArray *queData = malloc(sizeof(dataArray));
    dequeue(q, queData);
    printf("Queue after dequeue\r\n===========\r\n");
    printf("Queue #1  element count: %d\r\n", q->count);

    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", queData[i]);
    }
    return 0;
}

CodePudding user response:

"yes that would explain why the first example won't work, but the second SHOULD" -- No, no it won't.

First, enable Full Warnings in your compiler1. Now try and compile your second example. Notice the problems in all of your attempted printf() statements where %f expects a double arguments, but you are passing float * (a pointer to float). Why aren't there more descriptive errors? What do the comments under your question say to remove? (hint: your typedef is masking errors)

So go to your second example and comment the typedef, e.g.

// typedef float dataArray[20];

Why was that causing problems? Look at your parameters, e.g.

void enqueue(queue *q, dataArray *value) { ...

What is your dataArray *value? It's float *value[20]; What's that? It is float *[20], an array of 20 pointers to float. When you assign tmp->data = value; what are you assigning? C18 - 6.3.2.1 Lvalues, arrays, and function designators(p3) tells you. value is converted to a pointer to it's first element (a pointer-to-pointer-to float, e.g. float**). What is tmp->data? It is an array of pointers to float and it is not an lvalue -- see 6.3.2.1(p1). You cannot assign to an array, but your typedef is masking the problem.

Now remove your typedef (throughout your code) and try again, e.g.:

void enqueue(queue *q, float *value[20])
{
    if (q->count == QUEUE_SIZE)
    {
        float tmp[20];
        dequeue(q, &tmp);
        // free(tmp);         /* you can't free what's not allocated */
    }
    
    printf(" contents of value in enqueue\n");
    for (int i = 0; i < 20; i  )
    {
        printf("%f\n", *value[i]);
    }
    
    node *tmp;
    tmp = malloc(sizeof(node));
    printf("[enqueue] sizeof node %d\r\n", (uint) sizeof(node));
    // memcpy(tmp->data, &value, sizeof(value));
    tmp->data = value;
    tmp->next = NULL;
    ...

(you had memcpy(), you were closer there but your types are messed up)

It doesn't work. Now the compiler easily sees the attempt to assign to a non-lvalue and provides a proper diagnostic:

source.c:65:15: error: assignment to expression with array type
     tmp->data = value;
               ^

That problem runs throughout your code. Where does the mismatch begin? In main(). Look at what you are trying to pass to enqueue(), e.g.

int main()
{
    ...
    dataArray tmp;        /* e.g. float tmp[20] */
    ...
    enqueue(q, &tmp);
    ...
}

When you take the address of tmp the type is float (*)[20], a pointer-to-array-of float[20], not an array-of-pointers-to float (20 of them). A pointer-to-array has type float (*)[20] while the array-of-pointers* is float *[20] -- completely different types and pointer arithmetic is completely different between the two.

So when you pass &tmp with enqueue(q, &tmp); (where tmp is passed as value to void enqueue(queue *q, dataArray *value)) and then do value[i] in your first loop, for values of i > 0, you invoke undefined behavior reading beyond the end of the array (passed as a pointer-to-array, value[1] is one past the end of your array tmp in main() of float[20])

Can You Make It Work?

Yes, just fix the types and remove the second call to srand() in random_float() that is messing up your fill and fix your memcpy() call. (there are numerous other small issues, memory not validated and memory not freed, etc..) Carrying the removal of your typedef through the rest of your code and making the types consistent, you would end up with something like:

#include <time.h>

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

#define QUEUE_SIZE 30
#define ASZ 20

// typedef float dataArray[20];
typedef unsigned uint;

struct node
{
    float data[ASZ];
    struct node *next;
};
typedef struct node node;

struct queue
{
    int count;
    node *front;
    node *rear;
};
typedef struct queue queue;

void initialize(queue *q);
int is_empty(queue *q);
void enqueue(queue *q, float *value);
void dequeue(queue *q, float *outputArray);
void display(node *head);

float random_float();

void initialize(queue *q)
{
    q->count = 0;
    q->front = NULL;
    q->rear = NULL;
}

int is_empty(queue *q)
{
    return (q->rear == NULL);
}

void enqueue(queue *q, float *value)
{
    if (q->count == QUEUE_SIZE)
    {
        float tmp[ASZ];
        dequeue(q, tmp);
        // free(tmp);
    }
    
    printf(" contents of value in enqueue\n");
    for (int i = 0; i < ASZ; i  )
    {
        printf("%f\n", value[i]);
    }
    
    node *tmp;
    tmp = malloc(sizeof(node));
    printf("[enqueue] sizeof node %d\r\n", (uint) sizeof(node));
    // memcpy(tmp->data, &value, sizeof(value));
    memcpy (tmp->data, value, ASZ * sizeof *value);
    tmp->next = NULL;
    
    printf(" contents of tmp-node in enqueue\n");
    for (int i = 0; i < ASZ; i  )
    {
        printf("%f\n", tmp->data[i]);
    }

    if (!is_empty(q))
    {
        q->rear->next = tmp;
        q->rear = tmp;
    }
    else
    {
        q->front = q->rear = tmp;
    }
    q->count  ;
}

void dequeue(queue *q, float *outputArray)
{
    node *tmp;
    outputArray = q->front->data;
    printf("dequeue output before freeing memory\r\n===========\r\n");
    for (int i = 0; i < ASZ; i  )
    {
        printf("%f\n", outputArray[i]);
    }
    printf("[dequeue] size %d - %d\r\n", (uint) sizeof(q->front->data), 
            (uint) sizeof(q->front->data[0]));
    tmp = q->front;
    q->front = q->front->next;
    q->count--;
    free(tmp);
}

void display(node *head)
{
    if (head == NULL)
    {
        printf("NULL\r\n");
    }
    else
    {
        for (int i = 0; i < ASZ; i  )
        {
            printf("%f\n", head->data[i]);
        }

        display(head->next);
    }
}

float random_float()
{
    float a = 50.0;
    return ((float)rand() / (float)(RAND_MAX)) * a;
}

int main()
{
    queue *q;
    q = malloc(sizeof(queue));
    initialize(q);
    srand((unsigned int)time(NULL));

    float tmp[ASZ];
    for (int i = 0; i < ASZ; i  )
    {
        tmp[i] = random_float();
    }
    printf("display dataArray before fill\r\n===========\r\n");
    for (int i = 0; i < ASZ; i  )
    {
        printf("%f\n", tmp[i]);
    }

    enqueue(q, tmp);
    
    printf("Queue display after init and fill\r\n===========\r\n");
    display(q->front);

    printf("Queue before dequeue\r\n===========\r\n");
    printf("Queue #1  element count: %d\r\n", q->count);
    // Nächsten Queue Eintrag holen
    float *queData = malloc(sizeof(float[ASZ]));
    dequeue(q, queData);
    printf("Queue after dequeue\r\n===========\r\n");
    printf("Queue #1  element count: %d\r\n", q->count);

    for (int i = 0; i < ASZ; i  )
    {
        printf("%f\n", queData[i]);
    }
    return 0;
}

Example Use/Output

Now your second example compiles without warning and, to the extent the logic is correct, produces reasonable output (whether it is correct is left to you to determine), e.g.

$ ./bin/llqueue
display dataArray before fill
===========
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
 contents of value in enqueue
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
[enqueue] sizeof node 88
 contents of tmp-node in enqueue
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
Queue display after init and fill
===========
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
NULL
Queue before dequeue
===========
Queue #1  element count: 1
dequeue output before freeing memory
===========
5.864568
15.599927
1.622677
6.834927
18.953743
43.170605
37.056793
21.260620
44.568668
41.187069
43.298340
8.273252
13.328741
33.927246
32.561504
14.666824
20.176983
30.163134
21.064775
8.606315
[dequeue] size 80 - 4
Queue after dequeue
===========
Queue #1  element count: 0
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425
-0.001425

Note: this isn't an effort to correct ALL of the issues with your code. This merely gets you back on track from your typedef fiasco. While there are times where typedef'ing arrays makes sense (for experienced kernel developers, etc..), it's not something to do lightly for the reasons explained below. Pointers should not be typedef'ed either. See Is it a good idea to typedef pointers?.

footnotes:

  1. Always compile with warnings enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic to your gcc/clang compile string (also consider adding -Wshadow to warn on shadowed variables). Turn on -Werror to treat all warnings as errors. For VS (cl.exe on windows), use /W3. All other compilers will have similar options. Read and understand each warning -- then go fix it. The warnings will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you.
  •  Tags:  
  • c
  • Related