Read element of a dynamic array in c - arrays

I am using VScode as my IDE. So I created an array, and each element of the array stores a pointer to a linked list . My code is shown as below:
typedef struct AdjStopList
{
char stopName[20]; //name of stop bus is equal or less than 20 characters
int numOfAdjStp; //number of adjacent stops to this stop
struct BusAtStopList *buslist; // store all the buses passing this stop in a linked list
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;
typedef struct BusNetwork
{
int nBusStop; //number of bus stops in the newwork
struct AdjStopList **stops;
} BusNetwork;
//create a new empty AdjBusStopList
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;
}
I tried to use a while loop to assign stopName with "test" in a function
BusNetwork *newBusNetwork(int n, const char *BusStops)
{
BusNetwork *newBN;
newBN = malloc(sizeof(BusNetwork));
assert(newBN != NULL);
newBN->nBusStop = n;
newBN->stops = malloc(n * sizeof(AdjStopList *));
for (int i = 0; i < 10; i++)
{
newBN->stops[i] = newAdjStopList();
strcpy(newBN->stops[i]->stopName, "test");
}
//rest of the function
}
Then when I watch the variable (struct AdjStopList (**)[46])newBN->array in VScode, it seems only the first element of the array has been processed correctly, e.g. newBN->array[0]->stopName is test. The rest elements of the array still have random garbage values, e.g. newBN->array[1]->stopName.
Can anyone see why this is happening, please?
Q1: Is my use of index i wrong?
Q2: I expected when i = 0, newBN->stops[i] = newAdjStopList(); would only allocate memory to the first element.
After I run newBN->stops[i] = newAdjStopList();, as can be seen in the below screenshot, newBN->stops[1] has also been allocated memory. However, `numOfAdjStop' doesn't equal to 0. Why...
Q3: Is it the way how I watch the variables in VScode is wrong? I have learnt this from an online article. I remember it worked for me before. See reference: https://shanepanter.com/2020/12/10/view-point-as-array-vscode.html#references

Related

C - A line of code is changing the address of a struct

I have a major issue that is happening to my code, that I've been trying to fix for hours now.
The code below is the one relevant to the issue that I am having...
The method addBucket:
void addBucket(SPACE * hashmap,char * tempvalue, char * tempkey){
printf("BEGINNING OF FUNC...\n");
void *prevadd = hashmap[0];
char *value = varString(tempvalue);
char *key = varString(tempkey);
void *aftadd = hashmap[0];
printf("BUCKET %s - %s\n",value,key);
BUCKET *newBucket = malloc(sizeof(BUCKET *));
fillBucket(value,key,newBucket);
int hash = hashFunc(key);
printf("FILL, FULFILLED\n");
if(!hashmap[hash]){
hashmap[hash] = malloc(sizeof(BASE*));
hashmap[hash]->first = NULL;
}
ITEM *location;
location = hashmap[hash]->first;
//This creates a new item in the list, if there isn't any.
//It does this by initialising the base, called box.
if(!location){
hashmap[hash]->first = (ITEM *) calloc(1,sizeof(ITEM *));
hashmap[hash]->first->next = NULL;
hashmap[hash]->first->prev = NULL;
hashmap[hash]->first->data = newBucket;
}
//This instead adds a new item to the list.
else{
//This loop reaches the last ITEM in the linked list itself
while(location->next){
location = location->next;
}
//This initialises the newItem that will be added
ITEM *newItem = (ITEM *) calloc(1,sizeof(ITEM));
newItem->next = NULL;
newItem->data = newBucket;
newItem->prev = location;
location->next = newItem;
}
}
The declared structs that are used:
//Declares a struct called BUCKET.
//Serves as the bucket of the hash table.
typedef struct bucket{
char * value; //The value inputted.
char * key; //The key to be hashed.
}BUCKET;
//Declares a struct called ITEM.
//Holds the bucket, as well as the address to the next bucket.
//It also holds the address to the previous bucket.
typedef struct item{
struct bucket * data;
struct item * next;
struct item * prev;
}ITEM;
//Declares a struct called BASE.
//Serves as the base node for the linked lists.
//The amount of initialised linked lists is the same as the amount of bases.
typedef struct base{
struct item * first;
}BASE;
//Declares a struct of an array of BASES, meaning linked lists.
//Essentially defines the size of the hashspace.
typedef BASE *SPACE;
...And the method expandHashspace(); :
//Makes the size of the entire hashspace larger.
//Only takes a value larger than the current size due to possible data loss.
SPACE* expandHashspace(SPACE *hashmap, int newSize){
if(newSize>100 || newSize<hashSpaceSize){
printf("Exiting...\n");
return NULL;
}
else {
SPACE *nw = NULL;
nw = realloc(hashmap, sizeof(SPACE *) * newSize);
hashmap = nw;
hashSpaceSize = newSize;
return hashmap;
}
}
Here's also the initHashmap() method:
SPACE* hashmapInit(SPACE *hashmap){
hashmap = calloc(5, sizeof(SPACE *));
hashSpaceSize = 5;
return hashmap;
}
What I am doing here is initialising the hashmap, adding three buckets, expanding the hashmap, then adding three more buckets. Here's the order in more simple terms:
initHashmap();
addBucket(...); x3
expandHashmap();
addBucket(...); x3
However, on that last part, as soon as I run addBucket once, I get a SIGSEGV error. Checking through debugging, I realised something that was off.
Do you see the variables *prevadd and *aftadd? I added them while debugging to see what was happening to the address of hashmap[0]. Here is a picture of my results:
As you can see there, the address of hashmap[0] varied wildly during those two char * lines. Specifically, the change of address happens on the char *value line.
Please go easy on me, as I've just started learning C 3 months ago, and I am still incredibly unaccustomed to memory allocation. If the error is obvious, please point it out, and if I have some problem with the way that I am allocating memory, or freeing it, I am more than happy to hear them (my code has a pretty major heisenbug that I cannot fix for the life of me, but that's beside the point).
Thank you in advance... Sorry for all the recent questions.
update : forgot to add varString();...
char* varString(const char *origString){
size_t i;
for(i = 0;origString[(int)i]!='\0';i++){}
if(origString[i-1]=='\n') i-=2;
char *newString = malloc(i);
for(int j = 0; j <= i; j++){
newString[j] = origString[j];
}
newString[i+1] = '\0';
return newString;
}
This is not an answer, but it needed more formatting than would fit in a comment:
Note that you are writing "Value No. 1"
Note the value of aftadd is 0x756c6156
In memory, assuming a little-endian machine, the layout of the number in aftadd would be:
0x56 0x61 0x6c 0x75
In ASCII these would be:
'V' 'a' 'l' 'u'
Hint hint.

C: Help understanding pointers

I have been building this hash table function that is given an array it gets a word from and a pointer to an array that it fills with my linked list struct. It compiles correctly but I get a segmentation fault at - *hashTable[hashVal] = *newNode; .
void hashTableCreate(char *array, list *hashTable[]) {
while(arrayPos < getArrayLength(array)) {
char *temp = getWord(array);
int hashVal = hashingFunc(temp);
if((*hashTable[hashVal]).word == temp ) {
(*hashTable[hashVal]).count = (*hashTable[hashVal]).count+1;
}
else {
list *newNode = malloc(sizeof(list));
strcpy(newNode->word,temp);
newNode->count = 1;
*hashTable[hashVal] = *newNode;
}
}
}
It's called like this:
void timeStructures(char *newArray) {
list *hashTable[3000];
hashTableCreate(newArray, hashTable);
}
I know this is to do with the way I am using my pointers but I can't figure it out. Any help would be appreciated.
There are several problems on this code:
You seem you are using extra asterisks when assigning newNode to hashTable.
You can use ++ operator to increment count, and use -> to access a member of a pointer to struct.
getWord is unknown to me, but seems impossible it can return a pointer to a string contained inside an element of hashTable, which means the next if will always be false (newNode's word is a copied string, not a pointer to array's word), as it compares memory addresses instead of strings.
You never free temp neither hashTable elements, which seems another misconception of what you are doing here.
As noted by #kaylum , you should initialize hashTable so you avoid comparing against unassigned array elements, neither modifying count on them.
hashTable has to be passed as a pointer to hashTableCreate, or this will work on a copied version of it:
Try this:
void hashTableCreate(char *array, list *(*hashTable[])) {
while(arrayPos < getArrayLength(array)) {
char *temp = getWord(array);
int hashVal = hashingFunc(temp);
if((*hashTable)[hashVal] != NULL &&
strcmp((*hashTable)[hashVal]->word, temp) == 0 ) {
(*hashTable)[hashVal]->count++;
}
else {
list *newNode = malloc(sizeof(list));
strcpy(newNode->word,temp);
newNode->count = 1;
(*hashTable)[hashVal] = newNode;
}
free(temp);
}
}
void timeStructures(char *newArray) {
list *hashTable[3000];
int i;
// Initialize hashTable pointers to NULL
for(i = 0; i < 3000; i++) {
hashTable[i] = NULL;
}
hashTableCreate(newArray, &hashTable);
// Free hashTable elements, malloc'ed at hashTableCreate
for(i = 0; i < 3000; i++) {
if(hashTable[i] != NULL) {
free(hashTable[i]);
}
}
}
Note 1: you never check if hashVal is higher than 3000, which might be fine if you know what you are doing, aka, you know hashingFunc will never return 3000 or higher. If you do not, you'll need to care about growing hashTable appropriately.
Note 2: executing getArrayLength on each loop might be a performance problem for large lists. You probably prefer to create a variable to contain its value and execute it only once.
Note 3: It might be some easier if you declare hashTable as a plain list of elements, not a list of pointers to elements, like list hashTable[3000];, but I don't know exactly what you're looking for. Also, it will always waste 3000 list elements of memory, so might be a performance problem.

Making a linked list from command line arguments

I have to make a linked list from arguments entered from the command line. You are supposed to enter integers and make a linked list out of them. When you enter -1, then stop reading from the command line. Fairly simple, but I am having some trouble. What I have is:
#include <stdlib.h>
struct node
{
int grade;
struct node *next;
};
int
main (int argc, char *argv[])
{
struct node *root;
root = (struct node *) malloc (sizeof (struct node));
root->next = 0;
root->grade = 0;
int i;
for (i = 0; i < argc; i++)
{
if (&argv[i] == -1)
{
break;
}
else
{
struct node *new_item_ptr;
new_item_ptr = malloc (sizeof (struct node));
new_item_ptr->grade = (*int) argv[i];
(*new_item_ptr).next = root;
root = new_item_ptr;
}
}
}
I think I'm pretty close, but there are a few things that doesn't work correctly. For example, I don't think for(i = 0; i < argc; i++) is correct. And the compiler says that new_item_ptr->grade = (*int) argv[i]; is wrong also. Can anyone help me out with this? I think I'm close just have a few minor errors.
To begin with, you have malloc'ed a structure which is totally unnecessary. If the user enters no data, you can return an empty list indicated by root = NULL. So, the initial part could be:
root = ( struct node * ) NULL;
if ( argc < 2 )
return ( 0 );
As your code stands, the first node will always have 0 for root->grade. In the loop, you should allocate the memory and convert the argv[i] to integer using atoi. Finally, your loop should start with i=1 because argv[0] is the command name itself.
The various argv[i]s are C-strings (char*) not integers. You need to convert them to integers, and the best way to do that is with the atoi function:
int value = atoi(argv[i]);
if (value == -1) {
break;
} else {
// etc.
}
The function name atoi is supposed to be a mnemonic for "ASCII to integer." The syntax you're using to assign the next field of new_item_ptr is a little more complicated than it needs to be as well. You can use the arrow syntax to assign the next field just as you did to assign the grade field. This part should look like:
new_item_ptr->grade = atoi(argv[i]);
new_item_ptr->next = root;
Or, better still, re-use the value variable created at the top of this iteration of the for loop instead of running atoi again:
new_item_ptr->grade = value;
new_item_ptr->next = root;

graph implementation with adjacency lists in C

I just started learning C and as a self-learning excercise, I am implementing data structures and algos in C. Right now I am working on a graph and this is the data structure representation of it.
typedef int graphElementT;
typedef struct graphCDT *graphADT;
typedef struct vertexTag
{
graphElementT element;
int visited;
struct edgeTag *edges;
struct vertexTag *next;
} vertexT;
typedef struct edgeTag
{
int weight;
vertexT *connectsTo;
struct edgeTag *next;
} edgeT;
typedef struct graphCDT
{
vertexT *vertices;
} graphCDT;
To this graph I added a addVertex function.
int addVertex(graphADT graph, graphElementT value)
{
vertexT *new = malloc(sizeof(*new));
vertexT *vert;
new->element = value;
new->visited = 0;
new->edges = NULL;
new->next = NULL;
int i = 0;
for(vert=graph->vertices; vert->next != NULL; vert=vert->next)
{
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
}
vert->next = new;
//free(new);
printf("\ninserted %d\n", vert->element);
return 1;
}
This works fine except for three things.
if the newly added vertex is the same as the last vertex in the list, it fails to see it. To prevent this i changed the for loop limiting condition to vert != NULL, but that gives a seg fault.
if i try to free the temporarily allocated pointer, it resets the memory pointer by the pointer and this adds an infinite loop at the end of the vertex list. Is there no way to free the pointer without writing over the memory it points to? Or is it not really needed to free the pointer?
Also would destroying the graph mean destroying every edge and vertices? or is there a better approach?
Also if this data structure for graph is not a good one and there are better implementations, i would appreciate that being pointed out.
1
If you change the limiting condition to vert!=NULL , and if the loop ends with vert==NULL ,i.e. ,the vertex to be added isn't present , then you will be reading next statement :
vert->next = new;
That means you are accesing the NULL ,vert pointer , hence the seg fault .
Now to allow checking if the last element isn't the vertex to be added ,and also to prevent seg fault ,do this :
for(vert=graph->vertices; vert->next != NULL; vert=vert->next)
{
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
}
if(vert->element == value)
{
printf("already exists\n");
return 0;
}
vert->next = new;
2
The temporary "new" pointer is the memory location allocated to the Vertex you added .IT IS NOT to be freed ,as freeing it will mean that you deleted the vertex you just added :O .
3
Yes , detroying the graph essentialy means the same .
It is always a good practice to implement linked list as a adjacency list implementation of graph .Although you can always use a c++ "2 D Vector" to implement the same .
Here's a working addVertex function that you can use.
I am keeping the original declarations as it is.
I have added a main () to which you can give command line arguments to test.
int addVertex(graphADT graph, graphElementT value)
{
vertexT *tmpvert , *vert ;
vert=graph->vertices ;
/*check to see whether we really need to create a new vertex*/
tmpvert = vert;
while(tmpvert != NULL)
{
/* U can put a debug printf here to check what's there in graph:
* printf("tmpvert->elem=%d ", tmpvert->element);
*/
vert = tmpvert;
if(tmpvert->element == value)
return 0;
tmpvert=tmpvert->next ;
}
/*If we are here , then we HAVE to allocate memory and add to our graph.*/
tmpvert = (vertexT*)malloc(sizeof(vertexT));
if ( NULL == tmpvert )
return 0; /* malloc failure */
tmpvert->element = value;
tmpvert->visited = 0;
tmpvert->edges = NULL;
tmpvert->next = NULL;
if ( NULL == vert )
graph->vertices = tmpvert; /*Notice that I dont use virt=tmpvert */
else
vert->next = tmpvert; /*putting stuff in next is fine */
return 1;
/* Dont try printing vert->element here ..vert will be NULL first time */
/*return code for success is normally 0 others are error.
*That way you can have your printfs and error code
*handling outside this function.But its ok for a test code here */
}
Now for the main () snippet for testing :
int main (int argc , char* argv[]) {
graphADT graph ;
graph =(graphADT) malloc ( sizeof(struct graphCDT) );
graph->vertices = NULL;
while ( --argc >0)
{
int value = atoi(argv[argc]);
addVertex(graph,value);
}
}

C - Passing a Pointer to a Function and then Passing that Same Pointer Inside the Function to Another Function

Whew! Long title...here's some pseudo-code to explain that verbiage:
int main(){
int* ptr = function1(); //the data that ptr points to is correct here
function2(ptr);
}
int function2(int* ptr){
//the data that ptr points to is still correct
int i;
for(i=0;i<length;printf("%d\n", (*ptr)[i]), i++); //since ptr points to a contiguous block of memory
function3(ptr);
}
int function3(int* ptr){
//the data that ptr points to is INCORRECT!!!
}
Why would the data in function3 be incorrect?
Note: function1 performs a malloc() and returns the pointer to that memory.
ACTUAL CODE
#include <stdlib.h>
#include <stdio.h>
//Structures
struct hash_table_data_
{
int key, data;
struct hash_table_data_ *next, *prev;
};
struct hash_table_
{
int num_entries;
struct hash_table_data_ **entries;
};
typedef struct hash_table_data_ hash_table_data;
typedef struct hash_table_ hash_table;
//Prototypes
hash_table *new_hash_table(int num_entries);
int hash_table_add(hash_table *ht, int key, int data);
int hash_table_loader(hash_table* ht);
//Main
int main()
{
int num_entries = 8;//THIS MUST BE AUTOMATED
hash_table* ht = new_hash_table(num_entries);
hash_table_loader(ht);
return 0;
}
//Function Definitions
hash_table *new_hash_table(int num_entries)
{
hash_table* ht = (hash_table*) malloc(sizeof(hash_table));
hash_table_data* array = malloc(num_entries * sizeof(hash_table_data));
int i;
for (i=0;i<num_entries;i++)
{
array[i].key = -1;
array[i].data = -1;
array[i].next = NULL;
array[i].prev = NULL;
}
ht->entries = &array;
ht->num_entries = num_entries;
return ht;
}
int hash_table_add(hash_table *ht, int key, int data)
{
//VERIFY THAT THE VALUE ISN'T ALREADY IN THE TABLE!!!!!!!!!!!
int num_entries = ht->num_entries;
hash_table_data* array = *(ht->entries); //array elements are the LL base
int hash_val = key%num_entries;
printf("adding an element now...\n");
printf("current key: %d\n", array[hash_val].key);
int i;
for(i=0;i<num_entries;printf("%d\n", (*(ht->entries))[i].key),i++);//DATA IS INCORRECT!!!!
if (array[hash_val].key == -1)//is this the base link?
{
printf("added a new base link!\n");
array[hash_val].key = key;
array[hash_val].data = data;
array[hash_val].next = NULL;
array[hash_val].prev = &(array[hash_val]);
}
else//since it's not the base link...do stuff
{
hash_table_data* new_link = malloc(sizeof(hash_table_data));
new_link->key = key;//set the key value
new_link->data = data;//set the data value
if (array[hash_val].next == NULL)//we must have the second link
{
printf("added a new second link!\n");
new_link->prev = &(array[hash_val]); //set the new link's previous to be the base link
array[hash_val].next = new_link; //set the first link's next
}
else//we have the 3rd or greater link
{
printf("added a new 3rd or greater link!\n");
hash_table_data next_link_val = *(array[hash_val].next);
while (next_link_val.next != NULL)//follow the links until we reach the last link
{
next_link_val = *(next_link_val.next);//follow the current link to the next
}
//now that we've reached the last link, link it to the new_link
next_link_val.next = new_link; //link the last link to the new link
new_link->prev = &(next_link_val); //link the new link to the last link
}
}
return 0;
}
int hash_table_loader(hash_table* ht)
{
int i;
for(i=0;i<(ht->num_entries);printf("%d\n", (*(ht->entries))[i].key),i++); //DATA IS STILL CORRECT HERE
FILE *infile;
infile = fopen("input.txt", "r");
while(!feof(infile))
{
int key,data;
fscanf(infile, "%d %d", &key, &data);
hash_table_add(ht, key, data);
}
fclose(infile);
}
Note: Issue occurring the first time hash_table_add() is called.
Your first problem is here:
ht->entries = &array;
You cause the structure to hold a hash_table_data** which points to the variable hash_table_data* array which is local to the function; then you exit the function and return a pointer to the structure. The structure still exists (it was allocated via malloc(), and the stuff that array points to still exists, but array itself does not. Accordingly, this pointer within the structure is now invalid.
As far as I can tell, there is no reason for you to be holding a pointer-to-pointer here. Just use hash_table_data* as the entries type, and copy array into that struct member. Pointers are values too.
I guess you iterate incorrectly
for(i=0;i<length;printf("%d\n", (*ptr)[i]), i++);
this is nonsense.
You should rewrite it as this:
for(i=0;i<length;i++)
printf("%d\n", ptr[i]);
(*ptr)[i] is just wrong, it doesn't make sense if you think about it.
*ptr is the first element of the pointed-to array of ints.
ptr[i] is thi ith one, this is what you need.
Please, read Section 6 carefully.
A couple of advises based on this question:
Don't write overcomplicated code like this for statement with comma operator used, it just rarely needed and leads not only to confusion, but to mistakes (although no mistakes with it in this particular example)
Look carefully for mistakes, don't blame everything on functions. If your code doesn't work, try finding the exact place which is wrong and prove it. In this example people who tested your code were right: functions are definitely not the cause of the error.
hash_table *new_hash_table(int num_entries)
{
hash_table* ht = (hash_table*) malloc(sizeof(hash_table));
hash_table_data* array = malloc(num_entries * sizeof(hash_table_data));
// ....
ht->entries = &array; // Problem
// ...
return ht;
} // Life time of array ends at this point.
You are taking the reference of the local variable array and assigning it to ht->entries which is no more valid once the function returns.

Resources