Home > Enterprise >  C says struct doesn't exist even though I imported the file where I declared it
C says struct doesn't exist even though I imported the file where I declared it

Time:12-08

Ok, this sounds trivial, but hear me out: GCC is giving me the following error when I try to compile a 7-file C program:

error: unknown type Gnode

(it's for a programming assignment due soon on Dijkstra's Algorithm). I have 7 files on my folder: pa3.c, graph.c, graph.h, pqueue.c, pqueue.h, processes.c, and processes.h, and this error appears for every mention of Gnode in the pqueue and processes files. Graph.h looks like this:

// === GRAPH.H === //
// This file contains all the functions and structs related to graphs

#ifndef __GRAPH__
#define __GRAPH__
#include"pqueue.h"
#include"processes.h"

typedef struct _graphnode {
    
    //char name;

    int distance; // Distance from source node (0 if it IS source node)
    int q_idx; // position in queue
    short weight; // Weight of the individual node. Ideally shouldn't be changed
    int position[2]; // position of the node in graph. Format is {row, col}
    struct _graphnode* prev; // The node that led to this path


} Gnode;

// Creates the Graph as a Matrix
Gnode*** create(char* filename, short* ht, short* width);

// You have just created a graph! Congratulations!
// By creating this graph, you have allocated three different times:
// - Every Row 
// - Every Collumn
// - Every Item 
// It is important that they are freed in the following order:
// Item, Collumn, Row
// As deleting the collumns, will lose you access to the items and deleting the rows will lose you access to the collumns
// so you must not need to access any of the Items before deleting the cols and any of the cols before deleting the rows
void free_graph(Gnode*** graph, short ht, short width);

#endif

the other files look like this (yes, even the comments at the top are there):

// === PQUEUE.C === //
// This file defines the functions declared in pqueue.h

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<string.h>
#include"processes.h" 
#include"graph.h"
#include"pqueue.h"

// Below this line are functions and whatnot
// === PROCESSES.C === //
// This file executes the functions declared in
// - graph.h
// - pqueue.h
// - processes.h
// and defined in 
// - graph.c
// - pqueue.c
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<string.h>
#include"processes.h" 
#include"graph.h"
#include"pqueue.h"

// below this line are functions and whatnot
// === PQUEUE.H ==== //
// This file contains all functions and structs related to the priority queue

#ifndef __PQUEUE__
#define __PQUEUE__
#include"graph.h"
#include"processes.h"

// =================================================
// [function declarations]
// =================================================

#endif
// === PROCESSES.H === //
// This file declares the functions that will be used in processes.c 

#ifndef __PROCESSES__
#define __PROCESSES__
#include"graph.h"
#include"pqueue.h"

// =================================================
// [function declarations]
// =================================================

#endif

(I would like to point out that the "function declarations" and "below this line are functions" comments aren't all that's there. I have actual functions and other things running, but I didn't want to clutter the post)

I've been compiling this with gcc -g -std=c99 -o a.out *.c but a friend suggested I add the -import flag, so I did and I found the following:

running gcc -g -std=c99 -o a.out *.c -import processes.h fixes the issues with the processes files

running gcc -g -std=c99 -o a.out *.c -import pqueue.h fixes the issues with the pqueue files

running either gcc -g -std=c99 -o a.out *.c -import processes.h pqueue.h or gcc -g -std=c99 -o a.out *.c -import processes.h -import pqueue.h (I wasn't sure which would've been correct) doesn't fix both issues. I'm not entirely sure it even fixes the issue for even one of them, but I know it certainly doesn't fix both.

I've also made sure there are no spelling mistakes in Gnode because I've copy-pasted the struct name wherever I found it after I got this error...

It was also suggested to me that Circular imports might be an issue. I tried making sure this wasn't the case (but I might've done it incorrectly, who knows, I'm open to trying this again), but when I ran again with less imports, I got the same errors

One last thing I tried is just importing everything everywhere. This also didn't fix the problem.

Below are the full files (except graph.h, becuase that one was already the full file):

// === PQUEUE.C === //
// This file defines the functions declared in pqueue.h

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<string.h>
#include"processes.h" 
#include"graph.h"
#include"pqueue.h"

PQ* PQ_init(Gnode*** graph_matrix, short ht, short width){
    // Since at the beginning, all Nodes are the same distance from the desired node ("infinity"), they can
    // go in whatever order in the Queue at first. 
    // When we start traversing the graph using BFS, we'll use PQ_updt to make sure everything's in place
    PQ* pq = malloc(sizeof(*pq));
    pq->max_idx = (ht * width) - 1;
    pq->Queue = malloc((pq->max_idx   1)*sizeof(Gnode*));
    int k = 0;

    for(int i = (ht - 1); i > -1; i--){ // We start from the bottom to the top because that way we have the bottom items first, which have weight 0
        for(int j = 0; j < width; j  ){
            graph_matrix[i][j]->q_idx = k;
            pq->Queue[k] = graph_matrix[i][j];
            k  ;
        }
    }

    return pq;
}

void swap(PQ* pq, int idx1, int idx2){
    Gnode* temp = pq->Queue[idx2];
    pq->Queue[idx2] = pq->Queue[idx1];
    pq->Queue[idx1] = temp;
    pq->Queue[idx2]->q_idx = idx2;
    pq->Queue[idx1]->q_idx = idx1;
}

void PQ_updt(PQ* pq, int idx){
    // Just implement upwards heapify starting from the value at idx... not sure how... 
    int i = idx;
    int dist_idx = (pq->Queue[idx])->distance;
    while(i >= 0){
        int idx_parent = (i - 1) / 2;
        int dist_parent = (pq->Queue[idx_parent])->distance;
        if(dist_idx < dist_parent){
            swap(pq, i, idx_parent);
            i = idx_parent;
        }
        else{
            break;
        }
    }
}

void heapify_dn(PQ* pq){
    int max_idx = pq->max_idx; //it's easier to type, OK? 
    // Initializing the main index
    int i = 0;
    // Getting the distances in the different items of the queue because they're annoying to type out
    int dist_top = (pq->Queue[0])->distance;
    while(i <= max_idx){ 
        // As long as I'm inside the heap and my "top" node isn't in the right place ^^^
        // If the left value is smaller than right
        //  switch left and "top" (top is no longer top, but I'm still calling it top)
        //  Update values for left and right based on where top now is 
        //  loop again
        // If the right value is smaller than left
        //  switch right and "top" (top is no longer top, but I'm still calling it top)
        //  Update values for left and right based on where top now is 
        //  loop again   
        int idx_left = (i * 2)   1;
        int idx_right = (i * 2)   2;    
        int dist_l = (pq->Queue[idx_left])->distance;
        int dist_r = (pq->Queue[idx_right])->distance;

        //find smallest child
        Gnode* smallest_child = (dist_l < dist_r) ? pq->Queue[idx_left] : pq->Queue[idx_right]; 
        int potential_new_i = smallest_child->q_idx;
 
        if(pq->Queue[0]->distance > smallest_child->distance){
            swap(pq, pq->Queue[i]->q_idx, smallest_child->q_idx);
            i = potential_new_i; 
        }
        else {
            break;
        }
    }
}

void PQ_dq(PQ* pq){
    // Swap the first and last elements of the queue
    swap(pq, 0, pq->max_idx);
    // Decrease the length of the queue
    pq->max_idx --;
    // Heapify Down
    heapify_dn(pq);
}
// === PROCESSES.C === //
// This file executes the functions declared in
// - graph.h
// - pqueue.h
// and defined in 
// - graph.c
// - pqueue.c
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<string.h>
#include"processes.h" 
#include"graph.h"
#include"pqueue.h"

void updt_dist(Gnode**** a_graph_mtx, int pos[2], int mtx_ht, int mtx_width){
    short curr_wt = (*a_graph_mtx)[pos[0]][pos[1]]->weight; // stores the current weight we'll be adding to the nodes in the graph
    int new_dist = (*a_graph_mtx)[pos[0]][pos[1]]->distance   curr_wt; // stores the distance from the node to all its adjacent nodes
    if(pos[0]){ // If it's not colliding with the top of the graph;
        if(new_dist < ((*a_graph_mtx)[pos[0]-1][pos[1]]->distance)){ // if my new distance is less than the distance at the node right above
            (*a_graph_mtx)[pos[0]-1][pos[1]]->distance = new_dist; // Replace the distance
            (*a_graph_mtx)[pos[0]-1][pos[1]]->prev = (*a_graph_mtx)[pos[0]][pos[1]]; // and say which was the previous node
        } // Otherwise, keep the old distance
    }
    if(pos[1]){ // if it's not colliding with the left of the graph
        if(new_dist < ((*a_graph_mtx)[pos[0]][pos[1]-1]->distance)){ // if my new distance is less than the distance at the node to its left
            (*a_graph_mtx)[pos[0]][pos[1]-1]->distance = new_dist; // replace the distance
            (*a_graph_mtx)[pos[0]][pos[1]-1]->prev = (*a_graph_mtx)[pos[0]][pos[1]]; // and say which was the previous node
        } // Otherwise keep the old one
    }
    if(pos[0] < (mtx_ht-1)){ // if it's not colliding with the bottom of the graph
        if(new_dist < ((*a_graph_mtx)[pos[0] 1][pos[1]]->distance)){ // if my new distance is less than the distance at the node right below it
            (*a_graph_mtx)[pos[0] 1][pos[1]]->distance = new_dist; // replace the distance
            (*a_graph_mtx)[pos[0] 1][pos[1]]->prev = (*a_graph_mtx)[pos[0]][pos[1]]; // and say which was the previous node
        } // otherwise keep the old one
    }
    if(pos[1] < (mtx_width - 1)){ // If it's not colliding with the right of the graph
        if(new_dist < ((*a_graph_mtx)[pos[0]][pos[1] 1]->distance)){ // if my new distance is less than the distance at the node to its right
            (*a_graph_mtx)[pos[0]][pos[1] 1]->distance = new_dist; // replace the distance
            (*a_graph_mtx)[pos[0]][pos[1] 1]->prev = (*a_graph_mtx)[pos[0]][pos[1]]; // and say which was the previous node
        } // otherwise keep the old one
    }

}

Gnode*** dijkstra(Gnode*** graph_mtx, short ht, short width){
/*
//  1. Create the matrix with all my weighted nodes [DONE]
//  2. Assign all bottom nodes as source (distance = 0) [DONE]
//  3. Find the shortest distance between the bottom and each node in the graph actually using Dijsktra's 
//      - While there's any priority queue left [JUST A WHILE LOOP]
//      - Take the first node in the Priority queue, and find the distance to all its adjacent nodes. If the distance is less than
//      whatever is currently there, replace it with the new distance [DONE] (not checked)
//      - Dequeue the node we just checked all the neighbors for and use downwards heapify to move the new top node to the end [In progress...]
//      - Use Upwards heapify to change all nodes that had been changed 
//      - Go back to 3.
//  4. Out of all the top nodes, whichever has the shortest distance from top to bottom will be the chosen one, so we need too get some
//  things from it to save
//      - Gnode->distance = time
//      - We also need to (efficiently) find out how many steps we took from that node to the bottom
//      - Detailed path from start to end
//  5. Then from all the top nodes, we need to find out all their distances (easy) to save alongside the width of the graph
//  6. We also have to save the graph to a file
// !! when adding distances, add the distance of the current node to the next distance, then at the end, add the distance of the last node to the total distance !!
*/
    // Initializing the queue
    PQ* pq = PQ_init(graph_mtx, ht, width); // Remember to free this later... how..? uh... yes! 
    while(pq->max_idx >= 0){ // while there are indexes in the queue
        int zero_row = (pq->Queue[0])->position[0]; // row where the 0th element of the queue is
        int zero_col = (pq->Queue[0])->position[1]; // col where the 0th element of the queue is
        updt_dist(&(graph_mtx), (pq->Queue[0])->position, ht, width); // Update the distances from the node that's at the front of the PQ
        PQ_dq(pq);
        if(zero_row){ // if there was a node adjacent to it above 
            int adj_idx = graph_mtx[zero_row-1][zero_col]->q_idx;
            PQ_updt(pq, adj_idx);
        }
        if(zero_col){ // if there was a node adjacent to its right
            int adj_idx = graph_mtx[zero_row][zero_col-1]->q_idx;
            PQ_updt(pq, adj_idx);
        }
        if(zero_row < (ht-1)){ // if there was a node adjacent to it below
            int adj_idx = graph_mtx[zero_row 1][zero_col]->q_idx;
            PQ_updt(pq, adj_idx);
        }
        if(zero_col < (width-1)){ // if there was a node adjacent to its left
            int adj_idx = graph_mtx[zero_row][zero_col 1]->q_idx;
            PQ_updt(pq, adj_idx);
        }
    }
}
// === GRAPH.H === //
// This file contains all the functions and structs related to graphs

#ifndef __GRAPH__
#define __GRAPH__
#include"pqueue.h"
#include"processes.h"

typedef struct _graphnode {
    
    //char name;

    int distance; // Distance from source node (0 if it IS source node)
    int q_idx; // position in queue
    short weight; // Weight of the individual node. Ideally shouldn't be changed
    int position[2]; // position of the node in graph. Format is {row, col}
    struct _graphnode* prev; // The node that led to this path

} Gnode;

// Creates a Matrix graph
Gnode*** create(char* filename, short* ht, short* width);

// You have just created a graph! Congratulations!
// By creating this graph, you have allocated three different times:
// - Every Row 
// - Every Collumn
// - Every Item MAYBE NOT!! REVISE!!
// It is important that they are freed in the following order:
// Item, Collumn, Row
// As deleting the collumns, will lose you access to the items and deleting the rows will lose you access to the collumns
// so you must not need to access any of the Items before deleting the cols and any of the cols before deleting the rows
void free_graph(Gnode*** graph, short ht, short width);

#endif
// === PQUEUE.H ==== //
// This file contains all functions and structs related to the priority queue

#ifndef __PQUEUE__
#define __PQUEUE__
#include"graph.h"
#include"processes.h"

typedef struct{
    Gnode** Queue;
    int max_idx;
} PQ;

// INITQUEUE; will add all the values to the queue.
// Returns the PQ that was built
PQ* PQ_init(Gnode*** graph_matrix, short ht, short width); 

// DEQUEUE; will implement downwards heapify on the array heap.
void PQ_dq(PQ* pq); 

// UPDATE QUEUE; implements upwards heapify
void PQ_updt(PQ* pq, int idx); 


#endif
#ifndef __PROCESSES__
#define __PROCESSES__
#include"graph.h"
#include"pqueue.h"

// Updates distance using the following parameters:
// ) Gnode**** a_graph_mtx - pointer to a graph matrix that will be modified by address
// ) int pos[2] - the position of the node we're looking at adjacency from
// ) int mtx_ht - how many rows in the matrix (note: not the index of the rows)
// ) int mtx_width - how many collumns in the matrix (note: not the index of the collumns)
void updt_dist(Gnode**** a_graph_mtx, int pos[2], int mtx_ht, int mtx_width);

Gnode*** dijkstra(Gnode*** graph_mtx, short ht, short width);

void save_grid();
void save_times();
void save_path();

#endif
// === GRAPH.C === //
// This file defines the functions declared in graph.h

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>
#include<string.h>
#include"processes.h" 
#include"graph.h"
#include"pqueue.h"

Gnode*** create(char* filename, short* ht, short* width){

    FILE* file = fopen(filename, "rb");
    short val;
    Gnode*** graph_matrix; // Matrix to store all the graph nodes to find adjacency

    if(file == NULL){
        graph_matrix = NULL; 
        // FILE IS NULL. POOPY!!!!
    }
    else{
        fread(&val, sizeof(short), 1, file);
        *ht = val;
        fread(&val, sizeof(short), 1, file);
        *width = val;

        graph_matrix = malloc((*ht)*sizeof(Gnode*)); // Allocating Rows
        for(int i = 0; i < (*ht); i  ){
            graph_matrix[i] = malloc((*width)*sizeof(Gnode*)); // Allocating Collumns
        }


        // Making a matrix to later assign adjancent nodes
        for(int j = 0; j < (*ht); j  ){
            for(int k = 0; k < (*width); k  ){
                fread(&val, sizeof(short), 1, file);
                Gnode* new_node = malloc(sizeof(*new_node)); // Not freed yet
                new_node->distance = (j == ((*ht) - 1)) ? 0 : __INT_MAX__;
                new_node->weight = val;
                new_node->position[0] = j;
                new_node->position[1] = k;
                graph_matrix[j][k] = new_node;
            }
        }

        printf("~ Graph Matrix Built! ~");

        // It's later. We're assigning adjacent nodes
        /* NVM. I'm not
        for(int j = 0; j < ht; j  ){
            for(int k = 0; k < width; k  ){
                if(j){
                    graph_matrix[j][k]->adj[0] = graph_matrix[j-1][k];
                }
                if(k){
                    graph_matrix[j][k]->adj[1] = graph_matrix[j][k-1];
                }
                if(j != (ht-1)){
                    graph_matrix[j][k]->adj[2] = graph_matrix[j 1][k];
                }
                if(k != (width-1)){
                    graph_matrix[j][k]->adj[3] = graph_matrix[j][k 1];
                }
            }
        }
        // Maybe not the best idea but I still want to see whaddup

        printf("~ Adjancencies Assigned! ~");
        */

        /* // CODE TO PRINT THE GRAPH
        while(!(feof(file))){
            int counter = 0;
            while(counter < width){
                fread(&val, sizeof(short), 1, file);

                printf("[");
                if(val < 10){
                    printf(" ");
                }
                printf("%d]", val);
                
                counter   ;
            }
            printf("\n");            
        }
        */ // CODE TO PRINT THE GRAPH

        fclose(file);
    }

    return graph_matrix;
}


void free_graph(Gnode*** graph_mtx, short ht, short width){
    // Freeing all nodes separately (might revise)
    int i = 0; // Start looking at the rows
    while(i < ht){ // As long as we have rows
        int j = 0; // Start looking at the cols
        while(j < width){ // And as long as we have collumns
            free(graph_mtx[i][j]); // Free the value of every space in the (row, col) pair
            j   ; // Go to the next col in this row
        }
        i   ; // Go to the next row
    }

    // Freeing all cols
    i = 0; // Start looking at the rows again
    while(i < ht){ // While we still have rows
        free(graph_mtx[i]); // Free the col stored at the row
        i  ; // Look at the next row
    }

    // Freeing the rows
    free(graph_mtx);
}

CodePudding user response:

In the file pqueue.c, the line

#include"processes.h" 

causes compiler to behave as if the entire content of the file processes.h were pasted into the file at that point.

When the compiler processes the included file processes.h, it will encounter the line

#include"graph.h"

as one of the first lines of the file, so the compiler will behave as if the entire content of the file graph.h were pasted into the file at that point.

When the compiler processes the included file graph.h, it will encounter the line

#include"pqueue.h"

as one of the first lines of the file, so the compiler will behave as if the entire content of the file pqueue.h were pasted into the file at that point.

When the compiler processes the included file pqueue.h, it will encounter the lines

#include"graph.h"
#include"processes.h"

as some of the first lines of the file, but these two lines effectively do nothing, due to the include guards that you are using in the header files, which prevent multiple inclusion of the same header file.

When the compiler continues processing the file pqueue.h and reaches the lines

typedef struct{
    Gnode** Queue;
    int max_idx;
} PQ;

it reports an error, because the type Gnode has not yet been defined. That is because that type is defined in the file graph.h, but the compiler has not yet reached that definition, because in the file graph.h, the compiler is still processing the line

#include"pqueue.h"

near the top of the file.

To fix this, you should not use circular references in your #include files. That way, you can ensure that the compiler encounters the definition of Gnode in the file graph.h before it is used.

The lines

#include"pqueue.h"
#include"processes.h"

in the file graph.h should be removed, as they are not necessary.

The line

#include"processes.h"

in the file pqueue.h should also be removed, as it is not necessary.

And the line

#include"pqueue.h"

in the file processes.h should be removed, as it is not necessary.

After removing those #include directives, your header files will no longer have any circular references, so you now have full control of the order in which the header files are processed.

CodePudding user response:

pqueue.h and process.h both require including graph.h to get access to Gnode. But you include both of those files in graph.h, so those get processed before the definition of Gnode.

You should only include what it necessary, so remove the includes of pqueue.h and process.h from graph.h, and have those two files only include graph.h.

  •  Tags:  
  • cgcc
  • Related