Creating an array of pointers to structs is malloc needed? - c

I have created a struct like this
struct PCB
{
int used;
char PID[1];
struct RCB *Other_Resources;
char type[10];
struct PCB **list;
struct PCB *parent;
struct PCB *children;
struct PCB *next;
int priority;
};
then I declared an array of struct PCB * like this
struct PCB *PCBLIST[1024];
my question is do I need to malloc the array of struct pointers in order to use those pointers? I read on another question on here that I should do something like this:
PCBLIST = malloc(1024 * sizeof(struct PCB *));
this is modified slightly, I think in the original question they used MAX instead of giving the actual value like I did 1024.
The error I am getting from gcc c99 is:
error: assignment to expression with array type
but if I try to access an individual struct pointer like with PCBLIST[i]->used I get a seg fault. Can someone shed some light on what I am doing wrong?

You don't need to dynamically allocate the array as that is already allocated by the declaration of the array. You need to allocate struct memory for each of the array entries to point to. Something like:
int ix;
for (ix = 0; ix < sizeof(PCBLIST) / sizeof(PCBLIST[0]); ix++) {
PCBLIST[ix] = malloc(sizeof(struct PCB));
}
Note that that allocates memory for all the entries of the array. If all the entries are always used then a simpler way would be to use a static allocation by declaring the array as an array of structs rather than an array of pointers to those structs:
struct PCB PCBLIST[1024];
That simplies things by not requiring dynamic allocation. But at the cost of potentially using more memory as it does not allow for a sparsely populated array (not all entries allocated). Which one is best to use depends on how you intend to use the array.

PCBLIST should be a pointer, not an array, because you want to allocate space with malloc, and store the address to a pointer.
and you need to allocate the size of the struct and not the size of the pointer.
struct PCB *PCBLIST;
PCBLIST = malloc(1024 * sizeof(struct PCB));
edit: using malloc is matter of choice and not a must

Here are some examples of doing it with static nodes that are not dynamically allocated.
In the first one I don't change the bits you defined in your question (so you do have an array of pointers to PCB objects - I don't think the is what you were after though because the used flag is not useful if you only create the nodes you use:
#include <string.h> // strcpy
// the PCB structure definition
struct PCB
{
int used;
char PID[1];
struct RCB *Other_Resources;
char type[10];
struct PCB **list;
struct PCB *parent;
struct PCB *children;
struct PCB *next;
int priority;
};
// an uninitialized array of pointers to PCB structures
struct PCB *PCBLIST[1024];
// make some PCB objects
struct PCB pcbParent;
struct PCB pcbChild1;
struct PCB pcbChild2;
int main(void)
{
// initialize PCBLIST
for (int i = 0;i < 1024;i++)
PCBLIST[i] = NULL;
// initialize parent
pcbParent.used = 1;
pcbParent.PID[0] = 0;
pcbParent.Other_Resources = NULL;
strcpy(pcbParent.type,"Parent");
pcbParent.list = PCBLIST;
pcbParent.parent = NULL; // no parent
pcbParent.children = &pcbChild1;
pcbParent.next = NULL; // no next sibling
pcbParent.priority = 0;
PCBLIST[0] = &pcbParent;
// initialize child1
pcbChild1.used = 1;
pcbChild1.PID[0] = 1;
pcbChild1.Other_Resources = NULL;
strcpy(pcbChild1.type,"Child");
pcbChild1.list = PCBLIST;
pcbChild1.parent = &pcbParent;
pcbChild1.children = NULL; // no children
pcbChild1.next = &pcbChild2; // next sibling
pcbChild1.priority = 0;
PCBLIST[1] = &pcbChild1;
// initialize child2
pcbChild2.used = 1;
pcbChild2.PID[0] = 2;
pcbChild2.Other_Resources = NULL;
strcpy(pcbChild2.type,"Child");
pcbChild2.list = PCBLIST;
pcbChild2.parent = &pcbParent;
pcbChild2.children = NULL; // no children
pcbChild2.next = NULL; // no next sibling
pcbChild2.priority = 0;
PCBLIST[2] = &pcbChild2;
// do some stuff with the nodes
return 0;
}
It doesn't make much sense to do it that way because you might as well make an array of PCB objects directly - in that case you don't need PCBLIST at all because the list is the array of PCB objects themselves. for example this one makes more sense:
#include <string.h> // strcpy
// the PCB structure definition
struct PCB
{
int used;
char PID[1];
struct RCB *Other_Resources;
char type[10];
struct PCB *list;
struct PCB *parent;
struct PCB *children;
struct PCB *next;
int priority;
};
// an uninitialized array of PCB structures
struct PCB PCBLIST[1024];
int main(void)
{
// initialize PCBLIST
for (int i = 0;i < 1024;i++)
{
PCBLIST[i].used = 0;
PCBLIST[i].Other_Resources = NULL;
PCBLIST[i].list = PCBLIST;
PCBLIST[i].parent = NULL; // no parent by default
PCBLIST[i].children = NULL; // no children by default
PCBLIST[i].next = NULL; // no next sibling by default
}
// initialize parent
PCBLIST[0].used = 1;
PCBLIST[0].PID[0] = 0;
strcpy(PCBLIST[0].type,"Parent");
PCBLIST[0].children = &PCBLIST[1];
PCBLIST[0].priority = 0;
// initialize child1
PCBLIST[1].used = 1;
PCBLIST[1].PID[0] = 1;
strcpy(PCBLIST[1].type,"Child");
PCBLIST[1].parent = &PCBLIST[0];
PCBLIST[1].next = &PCBLIST[2]; // next sibling
PCBLIST[1].priority = 0;
// initialize child2
PCBLIST[2].used = 1;
PCBLIST[2].PID[0] = 2;
strcpy(PCBLIST[2].type,"Child");
PCBLIST[2].parent = &PCBLIST[0];
PCBLIST[2].priority = 0;
// do some stuff with the nodes
return 0;
}

No, you don't need malloc(). You could initialize the pointers to point to static instances of the structs. If you do use dynamic allocation, you should use calloc(), not malloc() plus arithmetic.

Related

Return user defined structure to dynamic array element

I am trying to create a dynamic array to store a linked list in each element of the array. So I defined the linked list structure as below:
//data type for adjacent bus stop
typedef struct AdjStopNode
{
int distance; //travel distance from the bus original stop to this adjcent stop
int stopID;
struct AdjStopNode *prev; //pointer to previous bus stop
struct AdjStopNode *next; //pointer to next bus stop
} AdjStopNode;
AdjStopNode *newAdjStopNode(int distance, int stopID)
{
AdjStopNode *newNode = (AdjStopNode *)malloc(sizeof(AdjStopNode));
assert(newNode != NULL);
newNode->distance = distance;
newNode->stopID = stopID;
newNode->next = NULL;
return newNode;
}
typedef struct AdjStopList
{
char stopname[20];
int numOfAdjStp;
struct BusAtStopList *buslist;
struct AdjStopNode *first; //pointed at the first AdjBusStop of the linked list
struct AdjStopNode *last; //pointed at the first AdjBusStop of the linked list
} AdjStopList;
AdjStopList *newAdjStopList()
{
AdjStopList *newList = (AdjStopList *)malloc(sizeof(AdjStopList));
newList->buslist = newBusAtStopList();
assert(newList != NULL);
memset(newList, NULL, 20 * sizeof(newList[0]));
newList->first = NULL;
newList->last = NULL;
newList->numOfAdjStp = 0;
return newList;
}
Then I defined a dynamic array to store each AdjStopList as an element of the array is as below:
typedef struct BusNetwork
{
int nBusStop; //number of bus stops in the newwork
struct AdjStopList *array;
} BusNetwork;
My function to assign an empty AdjStopList to every element of the array is as below:
//n is the number of AdjStopList
void listToArray(int n)
{
BusNetwork *newBN;
newBN = malloc(sizeof(BusNetwork));
assert(newBN != NULL);
newBN->nBusStop = n;
newBN->array = malloc(n * sizeof(AdjStopList)); //create an array of n number of dejacency lists
for (int i = 0; i < n; i++)
{
newBN->array[i] = newAdjStopList();
}
}
The above code gives me the error at newBN->array[i] = newAdjStopList() as
a value of type "AdjStopList *" cannot be assigned to an
entity of type "struct AdjStopList" C/C++(513)
using VScode.
Could someone help me to fix this problem and explain to me why? Much appreciated.
The type of newBN->array is struct AdjStopList * so the type of newBN->array[i] is struct AdjStopList but the return type fromnewAdjStopList() is struct AdjStopList*. So that should explain the error that you see where you are assigning a struct AdjStopList* to struct AdjStopList in the line
newBN->array[i] = newAdjStopList();
I believe you should change
typedef struct BusNetwork
{
int nBusStop; //number of bus stops in the newwork
struct AdjStopList *array;
} BusNetwork;
to
typedef struct BusNetwork
{
int nBusStop; //number of bus stops in the newwork
struct AdjStopList **array;
} BusNetwork;
So that array becomes an array of pointers to struct AdjStopList. Then that original assignment should work.

C - Trying to make an array of LinkedList pointers

I'm trying to create a HashTable in C where each 'bucket' is a pointer to a LinkedList. That is, I need to create an array of LinkedList pointers.
As of now, SomeHashTable->Buckets[i] is returning a non-pointer LinkedList. I've been looking for answers everywhere and I just can't find anything. Perhaps I'm overlooking something? I've given my current code below.
HashTable.h
#include "LinkedList.h"
typedef struct HashTable
{
LinkedList* Buckets[1009];
} HashTable;
//Creates new hashtable
HashTable* HashTable_new();
//Hashes and adds a new entry
void HashTable_add(HashTable* Table, int data);
HashTable.c
#include "HashTable.h"
HashTable* HashTable_new()
{
HashTable* newTable = (HashTable*)malloc(sizeof(HashTable));
newTable->Buckets = malloc(1009 * sizeof(LinkedList*));
//Create linked lists
for (int i = 0; i < 1009; i++)
{
newTable->Buckets[i] = LinkedList_new();
}
return newTable;
}
void HashTable_add(HashTable* Table, int data)
{
int index = data % 1009;
//Get bucket to hash to
LinkedList* BucketHead = (Table->Buckets[index]);
//Hash it iiinnnn real good
LinkedList_add_at_end(BucketHead, data);
}
The linked List structs for reference:
typedef struct LinkedListNode {
int data;
struct LinkedListNode *next;
struct LinkedListNode *prev;
} LinkedListNode;
typedef struct LinkedList {
struct LinkedListNode *first;
struct LinkedListNode *last;
} LinkedList;
As H.S.'s comment mentions, there is no need to dynamically --and-- statically allocate the Buckets array.
This line:
newTable->Buckets = malloc(1009 * sizeof(LinkedList*));
is overwriting the pointer to the statically allocated array, which is probably not what you want. For scalability, I would ditch the static array and stick with malloc(). That way you could use an argument to HashTable_new() to specify the size of the buckets array, like so:
HashTable* HashTable_new(int nBuckets)
{
HashTable* newTable = (HashTable*)malloc(sizeof(HashTable));
newTable->Buckets = malloc(nBuckets * sizeof(LinkedList*));
newTable->nBuckets = nBuckets;
//Create linked lists
for (int i = 0; i < nBuckets; i++)
{
newTable->Buckets[i] = LinkedList_new();
}
return newTable;
}
Notice that newTable->Buckets is being allocated as a pointer to a pointer to LinkedList (LinkedList**). You'll need to keep track to the size of Buckets[], so add the variable to the struct as follows:
typedef struct HashTable
{
int nBuckets;
LinkedList **Buckets;
} HashTable;
You should be good as long as LinkedList_new()'s return type is LinkedList*, and don't forget to free() it all when you're done.

Initialization error misunderstanding... Need some clarification

So I have a linked list set up like this:
#define MAX 20
//structure for a single linked list
typedef struct element {
int info;
struct element *link;
} Tnode;
//structure for a grapgh
typedef struct graphAdjList {
int nodes;
Tnode *adjList[MAX];
} Tgraph;
In my code I have it set up like this:
Tgraph *graph;
graph = (Tgraph*) malloc(sizeof(Tgraph));
graph -> nodes = 0;
for(i; i < 20; i++){
graph->adjList[i]= NULL;
}
graph->adjList[2]->info = 222;
Now if I compile this I get an access violation on this last line. Is it that I have not reserved memory for the Tnode part of the struct or am I missing something. How can I initialize the array so that I can assign a value to info in any element of the array?
Thank you
Jason
You are right, the issue is that you have not allocated memory for the individual nodes in adjList.
When you do graph->adjList[2]->info = 222;, graph->adjList[2] is still NULL from the for loop right before it.
To fix this, you need to first allocate memory for it like so:
graph->adjList[2] = malloc(sizeof(TNode));
Note: You could just replace graph->adjList[i] = NULL; with graph->adjList[i] = malloc(sizeof(Tnode)); in the for loop, but allocating as you go can be useful for memory efficiency.
You need to replace
graph->adjList[i]= NULL;
to
graph->adjList[i] = (Tnode*)malloc(sizeof(Tnode));

Loop allocation of linked list inside inline struct not allocating memory

I have strange problem with allocating a linked list in a loop.
Consider a simplified source code :
struct main_s {
minor_s minor_structure; (inline)
};
struct minor_s {
list_s *first_dir;
};
struct list_s {
anotherlist_s *first_object;
list_s *next;
};
struct anotherlist_s {
//multiple fields
};
And i have a basic init/deinit functions like :
struct main_s *main_s_init();
void main_s_deinit();
And now i'm kinda riddled with allocating in loop :
im passing to this function main_s->minor_structure.first_dir and, how_many parameter, defining how many linked nodes going to be initiated.
void loop_inittiation(struct list_s *list, int how_many) {
int i;
struct list_s *tmp = list;
for(i = 0; i < how_many; i++) {
tmp = malloc(sizeof(struct list_s));
tmp = tmp->next;
}
}
And this is where i have problem, im allocating the temporary "tmp" instead of the pointed structure. I understand that to allocate a pointer by tmp u have to use double pointer, but it still doesnt work. What am i missing? In gdb there is no memory space allocated :/.
Do i have to use **tmp?
You've got the right idea about what's wrong. The local copy of tmp in the function is changed, but once you're outside, that value is lost. If you want to change a variable inside a different function in C, you MUST pass the address of the thing you want to change. If the thing you want to change is already a pointer, you must pass the address of the pointer (or double pointer). If it's a double pointer you want to change, then you have to pass a triple pointer. If it's a 123141 pointer, you have to pass a 123142 pointer :)
Change the parameter to the function to:
&(main_s->minor_structure.first_dir)
Just change the input parameter to
struct list **list
change tmp to a double pointer to match it, then each time you use tmp, make sure to throw in an extra dereference..
struct list_s **tmp = list
and
*tmp = malloc(sizeof(struct list_s));
*tmp = (*tmp)->next;
So it would look like:
void loop_inittiation(struct list_s **list, int how_many) {
int i;
struct list_s **tmp = list;
for(i = 0; i < how_many; i++) {
*tmp = malloc(sizeof(struct list_s));
tmp = &((**tmp)->next);
}
}
Another way to do it is to leave the tmp stuff alone, as a single pointer, store the first node you allocate, and then just say
*list = tmp;
But then you do have to treat that first allocation as a special case.

c programming: need fresh eyes to look at this [demo code != homework]

Essentially I want qPtr[0] to hold sPtr[0]
struct myQueue{
struct sample* node;
int front;
int size;
int numElements;
};
struct sample{
int field1[5];
char field2[10];
}
int main(){
struct myQueue* qPtr = malloc(10 * sizeof(struct myQueue);
struct sample* samplePtr = malloc(10 * sizeof(struct sample); //assume this array has been initialized
enqueue(qPtr, samplePtr[0]); //this does not work
}
//returns 1 if enqueue was successful
int enqueue(struct myQueue* qPtr, struct sample* sPtr){
qPtr->node[(qPtr->front + qPtr->numElements) % qPtr->size] = sPtr; //code pertains to circular array implementation of queues
return 1;
}
I've been at it for about 2 hours now and would appreciate some clarification on what I'm doing wrong conceptually. thank you!
samplePtr[0] gives the object itself, not a pointer to the object. Try sending &samplePtr[0] or samplePtr itself. enque function, second parameter expects a type of struct sample* and not struct sample.
How about:
enqueue(qPtr, &samplePtr[0]);
The second parameter to enqueue() takes a pointer to a struct sample.
Your code has 2 fundamental problems.
you're passing a struct sample object to enqueue() instead of a pointer to a struct sample. this should be caught by the compiler.
you're setting up an array of queue structures instead of having a single queue structure object that manages an array of pointers to the objects that are on the queue. This is a design problem.
Your code should probably look more like:
struct myQueue{
struct sample* node;
int front;
int size;
int numElements;
};
struct sample{
int field1[5];
char field2[10];
}
struct myQueue q = {0};
int enqueue(struct myQueue* qPtr, struct sample* sPtr);
int main(){
// get memory to hold a collection of pointers to struct sample:
q.node = calloc(10, sizeof(struct sample*));
q.size = 10;
// allocate a sample
struct sample* samplePtr = malloc(sizeof(*samplePtr));
// put the sample on the queue
enqueue(qPtr, samplePtr);
}
//returns 1 if enqueue was successful
int enqueue(struct myQueue* qPtr, struct sample* sPtr){
qPtr->node[(qPtr->front + qPtr->numElements) % qPtr->size] = sPtr; //code pertains to circular array implementation of queues
return 1;
}

Resources