I have an assignment in C and I have trouble accessing different members within my structs(some levels deep). I understand the basic principles, but I kinda lose it somewhere. I have 3 structures, with the top one containing an array of the second, which in turn contains an array of the third. My current issue is using malloc the correct way. Here is some of my code. I would appreciate any kind of information or tip, because i still have a long way to go and as you can see the structures are kinda complicated.
.h file
typedef struct user {
char* userID;
int wallet;
bitCoinList userBC; //Also a list
senderTransList userSendList; //Yes it has lists too..
receiverTransList userReceiveList;
}user;
typedef struct bucket {
struct bucket* next;
user** users;
}bucket;
typedef struct hashtable {
unsigned int hashSize;
unsigned int bucketSize;
bucket** buckets;
}hashtable;
Here is my function for creating and initializing the hashtable..I get the error when I try to access users with HT->buckets->users (request for member users in something not a structure or a union)
.c file
// Creation and Initialization of HashTable
hashtable* createInit(unsigned int HTSize,unsigned int buckSize){
hashtable* HT = (hashtable*)malloc(sizeof(hashtable));
if(HT==NULL) {
printf("Error in hashtable memory allocation... \n");
return NULL;
}
HT->hashSize=HTSize;
HT->bucketSize=buckSize;
HT->buckets = malloc(HTSize * sizeof(HT->buckets));
if(HT->buckets==NULL) {
printf("Error in Buckets memory allocation... \n");
return NULL;
}
HT->buckets->users = malloc(buckSize * sizeof(HT->buckets->users));
if(HT->buckets->users==NULL) {
printf("Error in Users memory allocation... \n");
return NULL;
}
for(int i=0; i <HTSize; i++){
HT->buckets[i] = malloc(sizeof(bucket));
HT->buckets[i]->next = NULL;
if(HT->buckets[i]==NULL) {
printf("Error in Bucket %d memory allocation... \n",i);
return NULL;
}
for(int j=0; j <buckSize; j++){
HT->buckets[i]->users[j] = malloc(sizeof(user));
if(HT->buckets[i]==NULL) {
printf("Error in User %d memory allocation... \n",i);
return NULL;
}
}
}
return HT;
}
Because buckets is pointer to pointer type you need to:
(*(HT-> buckets)) ->users = ....
or
HT-> buckets[0] ->users = .... // or any other index depending of the program logic
or (for the n-th pointer)
(*(HT-> buckets + n)) ->users = ....
or
HT-> buckets[n] ->users = .... // or any other index depending of the program logic
This only the syntax answer and I do not analyze the program logic
At least one problem: wrong size allocation.
Allocate to the size of the data pointed to by HT->buckets, not to the size of the pointer.
Avoid mistakes. The below idiom is easy to code to, review and maintain.
ptr = malloc(sizeof *ptr * n);
// HT->buckets = malloc(HTSize * sizeof(HT->buckets));
HT->buckets = malloc(HTSize * sizeof *(HT->buckets));
// HT->buckets->users = malloc(buckSize * sizeof(HT->buckets->users));
HT->buckets->users = malloc(buckSize * sizeof *(HT->buckets->users));
// HT->buckets[i] = malloc(sizeof(bucket));
HT->buckets[i] = malloc(sizeof *(HT->buckets[i]));
// HT->buckets[i]->users[j] = malloc(sizeof(user));
HT->buckets[i]->users[j] = malloc(sizeof *(HT->buckets[i]->users[j]));
Related
I'm writing a code in which I read some graphs from text file to process it later. I have one function to write graph to memory and second one which uses first one and then operates on this graph. The problem is that I allocate some memory in first function but I don't know where should I free it, because freeing it in first function crashed program, while in second function compiler says there is no such a struct.
struct Graph* createGraph(struct edge edges[], int wxk, int l)
{
// allocate memory for the graph data structure
//struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
struct Graph* graph = malloc(sizeof *graph);
graph->head = malloc( l * sizeof *(graph->head) );
// initialize head pointer for all vertices
for ( int i = 0; i < wxk; i++ ) {
graph->head[i] = NULL;
}
// add edges to the directed graph one by one
for ( int i = 0; i < l; i++ )
{
// get the source and destination vertex
int src = edges[i].src;
int dest = edges[i].dest;
double weight = edges[i].weight;
// allocate new node of adjacency list from `src` to `dest`
struct node* newNode = malloc(sizeof *(newNode) );
struct node* newNode2 = malloc( sizeof *(newNode2));
newNode->dest = dest;
newNode->weight = weight;
newNode->next = NULL;
if( graph->head[src] == NULL ) {
graph->head[src] = newNode;
} else {
for( newNode2 = graph->head[src]; newNode2->next != NULL; newNode2 = newNode2->next )
;
newNode2->next = newNode;
}
struct node* newNode3 = malloc( sizeof *(newNode3) );
struct node* newNode4 = malloc( sizeof *(newNode4) );
newNode3->dest = src;
newNode3->weight = weight;
newNode3->next = NULL;
if( graph->head[dest] == NULL ) {
graph->head[dest] = newNode3;
} else {
for( newNode4 = graph->head[dest]; newNode4->next != NULL; newNode4 = newNode4->next )
;
newNode4->next = newNode3;
}
}
return graph;
}
Here is first function code, in which I allocate memory to newNode, newNode2, newNode3 and newNode4. When I free this memory at end of this function, program crashes later.
void check_graph( char *plik)
{
FILE *in = fopen( plik, "r");
struct edge *edges = readfromfile(in);
int l = getl();
int wxk = getwxk();
struct Graph *graph = createGraph( edges, wxk, l);
struct FIFO queue;
short int *visited = malloc ( wxk * sizeof (int));
for( int i = 0; i < wxk; i++)
{
visited[i] = 0;
}
queue.vertices = (int *) malloc( wxk * sizeof(int) );
queue.front = 0;
queue.end = 0;
add_to_queue( &queue, 0);
visited[0] = 1;
while( queue.front != queue.end)
{
int current_vertex = del_from_queue( &queue);
struct node *tmp = graph->head[current_vertex];
while( tmp != NULL)
{
int adjVertex = tmp->dest;
if( visited[adjVertex] == 0)
{
visited[adjVertex] = 1;
add_to_queue( &queue, adjVertex);
}
tmp = tmp->next;
}
}
free(queue.vertices); // czyszczenie pamięci
free(visited);
free(edges);
for( int i = 0; i < wxk; i++ )
free( graph->head[i] );
free(graph->head);
free(graph);
}
If I try to free the previous memory here, compiler says that names of variables are undeclared
Short answer
Freeing memory should be handled in separate functions that destroy a specific object, one for (adjacency) lists and one for graphs (which calls the adjacency list destroying function). The adjacency list destructor should iterate over a list, freeing nodes as it visits them (note the nodes are freed using the destructor's own local variables, not the newNodeI variables in the graph constructor). The graph destructor would be called from check_graph. Note that this parallels how creation is handled in the code.
Longer answer
The program would greatly benefit from following some fundamental design principles. In particular, break up the functions into more basic operations, each of which performs a single task (akin to the Single Responsibility Principle from OOP). When considering the sub-tasks of a task, they should be at the same level and in the same domain (more on this later). Additionally, functions shouldn't be overlong. Repeated code is a candidate for abstraction into a separate function (Don't Repeat Yourself), as long as it is conceptually a single task. Though the program may not be explicitly object-oriented, some OO conventions can be usefully applied. Variable names should be descriptive.
Start thinking about function names. The sample has createGraph and check_graph, a mix of naming conventions. This isn't inherently wrong, but naming conventions should only be mixed when each convention is doing something different, and are in different parts of a program. One C convention for naming methods in an OO manner is to use DromedaryCase for class names and camelCase for method names (as is done in C++), and connect the two with an underscore (basically, snake case) (e.g. ClassName_methodName). Extending this, the underscore indicates going down in scope, so nested class methods would be named as: Outer_Inner_methodName. Alternatives include using camelCase for class names, or snake case for everything, or snake case but with a double underscore for scope (e.g. outer_class__inner_class__method_name). "Private" methods can be indicated with a leading underscore.
The check_graph function performs the following sub-tasks:
opens a file
causes edges to be read from file
causes a Graph object to be created
allocates space for a member field of a queue (queue.vertices)
traverses the graph breadth-first
examines queue members to determine when it's empty
destroys the queue member, edges, and Graph
This mixes different levels of tasks (e.g. causing a Graph object to be created (which happens in a different function) but destroying the object itself; creating a part of the queue) and domains (e.g. file I/O, memory management, and graph algorithms), resulting in multiple responsibilities. Reading objects from files should be handled by a component whose responsibility it is to bridge I/O and object creation. Destroying the graph object should be handled by a separate function, a counterpart to createGraph (or Graph_create, if you use the convention above). This in particular should resolve the issue in question. Queue manipulation should be farmed out to queue functions, encapsulating the operations and data.
The majority of the lines in check_graph are concerned with the breadth-first traversal of the graph. This could be the basis for a function that implements the BFS algorithm, taking a callback that's called for each vertex as it's visited. check_graph would then call the BFS function.
A sketch of a refactored version:
typedef void (*Graph_visitor_t)(Graph_t *graph, int iVertex, void *additional);
/**
* Breadth-first traversal of a graph
*
* visit: a callback, invoked for each vertex when visited
* pAdditional: additional data passed along to the `visit` function
*/
void Graph_bfs(Graph_t *graph, Graph_visitor_t visit, void *pAdditional) {
// TODO: detect & handle memory errors
bool *visited = calloc(sizeof(*visited), graph->nVertices);
IntQueue_t *queue = IntQueue_create(graph->nVertices);
visit(graph, 0, pAdditional);
visited[0] = 1;
IntQueue_push(queue, 0);
while (! IntQueue_empty(queue)) {
int current_vertex = IntQueue_pop(queue);
/* much the same as the original `check_graph` (only
* add a call to `visit`)
*/
// ...
}
IntQueue_destroy(queue);
free(visited);
}
void _Graph_countVisited(Graph_t* graph, int iVertex, int *pnVisited) {
++(*pnVisited);
}
// Demonstrates how to use Graph_bfs (check_graph woudl be similar).
void Graph_isConnected(Graph_t *graph) {
int nVisited = 0;
Graph_bfs(graph, &_Graph_countVisited, &nVisited);
return nVisited == graph->nVertices;
}
createGraph performs the following sub-tasks:
allocates the graph object & members
allocates the adjacency list nodes
traverses adjacency lists
adds nodes to adjacency lists
Again, some of these tasks are at different levels and should be farmed out (e.g. adjacency list manipulation). The code that manipulates the adjacency list within the loop is also repetitive, and is a great candidate for being moved to another function.
Many of the variable names (e.g. l, wxk, newNode3) aren't very descriptive, leading to some bugs. For example, in createGraph, graph->head is allocated to hold l entries, but wxk entries are accessed when initializing it (in this case, the better fix is to use calloc instead of manually initializing all entries to NULL). If these variables were name more descriptively, e.g. nVertices and nEdges (I'm guessing as to purpose), the bug would be more obvious and likely wouldn't have occurred in the first place.
void _Graph_addAdjacency(Graph_t *graph, int from, int to, double weight) {
Node_t *newNode = List_Node_create(to, weight);
if (graph->head[from] == NULL ) {
graph->head[from] = newSrcNode;
} else {
List_append(graph->head[from], newSrcNode);
}
}
void _Graph_addEdge(Graph_t *graph, Edge_t *edge) {
_Graph_addAdjacency(graph, edges[i].src, edges[i].dest, edges[i].weight);
_Graph_addAdjacency(graph, edges[i].dest, edges[i].src, edges[i].weight);
}
Graph_t* Graph_create(Edge_t edges[], int nEdges, int nVertices) {
//// allocation
// TODO: detect & handle memory errors
Graph_t *graph = malloc(sizeof *graph);
graph->head = calloc(sizeof *(graph->head), nVertices);
graph->nVertices = nVertices;
//// initialization
// add edges to the directed graph one by one
for (int i = 0; i < nEdges; i++) {
// TODO: add error detection
_Graph_addEdge(graph, edges[i]);
}
return graph;
}
Rounding out the example are functions to read the graph from the file (Graph_readFromPath) and to tie it all together (main in this example, though in a larger program it wouldn't be the main function).
Graph_t* Graph_readFromPath(const char *fName) {
FILE *in = fopen(fName, "r");
int nVertices = Count_readFromFile(in);
int nEdges = Count_readFromFile(in);
Edge_t *edges = Edges_readFromFile(in, nEdges);
fclose(in);
Graph_t* graph = Graph_create(Edge_t edges[], nEdges, nVertices);
free(edges);
return graph;
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "No input file given.");
return 1;
}
const char *fName = argv[1];
if (access(fname, R_OK)) {
fprintf(stderr, "Error reading input file '%s': %s", fName, strerror(errno));
return 1;
}
Graph_t *graph = Graph_readFromFile(fName);
if (! Graph_isConnected(graph)) {
// ...
}
Graph_destroy(graph);
return 0;
}
I'm making an hashing table data structure and having segmentation fault error on my inicialization function. Here the code:
void allocTableSlots(alu **table, int index){
if(index == MAX)
return;
else{
table[index] = calloc(1, sizeof(alu));
table[index]->registration = -1;
table[index]->next = -1;
allocTableSlots(table, index+1);
}
}
void initializateHashTable(hash *hashing){
hashing = calloc(1, sizeof(hash));
allocTableSlots(hashing->table, 0);
hashing->collisionArea = 690;
}
My structs are these:
#define MAX 997
typedef struct alu{
int registration;
char name[80];
char email[80];
int next;
} alu;
typedef struct reg{
alu *table[MAX];
int collisionArea;
}hash;
The error comes in:
if(index == MAX)
on allocTableSlots() function
If I change MAX, for MAX-1, or any other number, like 500 the error still comes after position 499, so its not look like that I trying to access an invalid position of my array table
I already tried an iterative version (in case that my recursion has some error) but still the same
As suggested in the comments, you most likely should just return the pointer to the allocated block from the init function. Furthermore, if the maximum bucket size is known, as is in your code with MAX, the code simplifies to:
...
typedef struct reg {
alu table[MAX];
int collisionArea;
} hash;
hash *initializateHashTable(void) {
hash *t = calloc(1, sizeof *t);
if (!t) return NULL; // check calloc, just in case.
/* Whatever initialization you want to perform. As per your code,
setting registration and next members to -1 */
for (int i = 0; i < MAX; i++) {
t->table[i].registration = t->table[i].next = -1;
}
t->collisionArea = 690; // EDIT: Forgot the collisionArea
return t;
}
I am trying to make an implementation of an n-ary tree in C. When running it I get the following error:
sibling(1143,0x7fff7e925000) malloc: *** error for object 0x7f946b4032c8: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
I am unsure what is causing the error. As it says it seems that I am writing to an object that was freed. But in my code I do not free any of the memory allocated. I am new to c to this confused me very much. I tried debugging with gdb and it says the error is caused by the printTree(); call in main where I am recursively trying to print the tree. Hope you can help me understand the issue :-).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name;
unsigned int utility;
unsigned int probability;
} Child;
typedef struct {
unsigned int level;
unsigned int player;
unsigned int nChildren;
Child *children;
} Data;
typedef struct sNaryNode{
Data *data;
struct sNaryNode *kid;
struct sNaryNode *sibling;
} NaryNode;
NaryNode* createNode(Data data){
NaryNode *newNaryNode = malloc(sizeof (NaryNode*));
newNaryNode->sibling = NULL;
newNaryNode->kid = NULL;
newNaryNode->data = &data;
return newNaryNode;
}
NaryNode* addSibling(NaryNode* n, Data data){
if(n == NULL) return NULL;
while(n->sibling)
n = n->sibling;
return (n->sibling = createNode(data));
}
NaryNode* addChild(NaryNode* n, Data data){
if(n == NULL) return NULL;
else if(n->kid)
return addSibling(n->kid, data);
else
return (n->kid = createNode(data));
}
void printTree(NaryNode* n) {
if(n == NULL) return;
if(n->sibling) {
printf("%u %u %u %u %u %s", n->data->level, n->data->player, n->data->nChildren, n->data->children[0].probability, n->data->children[0].utility, n->data->children[0].name);
printTree(n->sibling);
}
else if(n->kid) {
printf("%u %u %u %u %u %s", n->data->level, n->data->player, n->data->nChildren, n->data->children[0].probability, n->data->children[0].utility, n->data->children[0].name);
printTree(n->kid);
}
else {
printf("The tree was printed\n");
}
}
int main(void) {
NaryNode *root = calloc(1, sizeof(NaryNode));
Data data;
data.level = 1;
data.player = 1;
data.nChildren = 2;
data.children = calloc(data.nChildren, sizeof data.nChildren);
data.children[0].probability = 50;
data.children[0].utility = 1;
data.children[0].name = "Kom med det første tilbud (anchor)";
data.children[1].probability = 50;
data.children[1].utility = 1;
data.children[1].name = "Afvent modspilleren kommer med første tilbud";
*root = *createNode(data);
int i = 0;
for(i=0; i<root->data->nChildren; i++) {
addChild(root, data);
}
printTree(root);
}
There are various errors in your code.
Allocating an incorrectly sized memory block :
data.children = calloc(data.nChildren, sizeof data.nChildren);
data.children is an array of Child structures, yet you're allocating structures whose size is equal to sizeof(unsigned int), due to data.nChildren being an unsigned int.
Taking the address of a temporary variable and storing it for later usage :
NaryNode* createNode(Data data){
newNaryNode->data = &data;
}
data in createNode only exists for as long as the function is running : in this case, you're taking the address of the local variable data and storing it in the structure that you're returning for later usage. This is a very bad idea, since this pointer will refer to an object that doesn't exist anymore after the function returns.
Keep in mind that you don't need to pass a copy of the Data object into createNode in your current code, since there is really only one Data object in the whole program. Thus, you can change the prototype of createNode to createNode(Data* data), and pass the address of the Data structure that you create in main. Doing anything more involved than that, though, would require deep-copying the structure, I think.
Incorrectly managing the objects' lifetime.
NaryNode *root = calloc(1, sizeof(NaryNode));
*root = *createNode(data);
createNode returns an NaryNode*. However, you never actually assign it to an NaryNode* so that you can free it later. Instead, the pointer to the object that the function returns is known only during the *root = *createNode(data) invocation, and irrevocably lost later on. You do, however, retain the contents of the object due to dereferencing it and copying it into root : the object itself, however, as returned from createNode, is lost and not recoverable, unless pointers to it still exist in the tree.
Here is another problem. This line does not allocate space for a NaryNode, but only for a pointer to a NaryNode:
NaryNode *newNaryNode = malloc(sizeof (NaryNode*));
I want to make an array with unknown size , is it true to make it like this ? :
int *array,var,i=0;
FILE *fp;
fopen=("/home/inputFile.txt","r");
fscanf(fp,"%d",&var);
while(fp!=NULL)
{
if(var>0)
{
array=malloc(sizeof(int));
array[i++]=var
}
fscanf(fp,"%d",&var);
}
This is absurdly false, full of memory leaks and undefined behaviors.
However, it's not that far from one of the right ways, the linked list way:
struct linked_int
{
int value;
struct linked_int* pNext;
};
struct linked_int *pHead=NULL;
struct linked_int **ppTail = &pHead;
int* array = NULL;
int cpt=0;
/*Read file, building linked list*/
FILE *fp = fopen("/home/inputFile.txt","r");
if(fp != NULL)
{
int var;
while(fscanf(fp,"%d",&var)==1)
{
if(var>0)
{
struct linked_int *pNew = malloc(sizeof(struct linked_int));
pNew->value = var;
pNew->pNext = NULL;
/*Append at the tail of the list*/
*ppTail = pNew;
ppTail = &(pNew->pNext);
cpt++;
}
}
fclose(fp);
}
/*Copy from the linked list to an array*/
array = malloc(sizeof(int) * cpt);
if(array != NULL)
{
int i;
struct linked_int const *pCur = pHead;
for(i=0 ; i<cpt ; i++)
{
arr[i] = pCur->value;
pCur = pCur->pNext;
}
}
/*Free the linked list*/
while(pHead != NULL)
{
struct linked_int *pDelete = pHead;
pHead = pHead->pNext;
free(pDelete);
}
ppTail = &pHead;
Other ways:
Another right way is the realloc way, which consists in re-allocating the array with an ever expanding size (usually with a geometric growth, i.e. multiplying the array size by a number such as 1.5 every time). A wrong way to do so is to add 1 to the array size every time.
It goes something like this:
int arrayCapacity=0, numberOfItems=0;
int* array = NULL;
int var;
while(fscanf(fp, "%d", &var)==1)
{
if(numberOfItems >= arrayCapacity)
{
/*Need to resize array before inserting*/
const int MIN_CAPACITY = 4;
const double GROWTH_RATE = 1.5;
int newCapacity = arrayCapacity<MIN_CAPACITY ? MIN_CAPACITY : (int)(arrayCapacity*GROWTH_RATE);
int* tmp = realloc(array, newCapacity*sizeof(int));
if(tmp==NULL)
{
/*FAIL: can't make the array bigger!*/
}
else
{
/*Successfully resized the array.*/
array = tmp;
arrayCapacity = newCapacity;
}
}
if(numberOfItems >= arrayCapacity)
{
puts("Cannot add, array is full and can't be enlarged.");
break;
}
else
{
array[numberOfItems] = var;
numberOfItems++;
}
}
/*Now we have our array with all integers in it*/
The obvious result is that in this code, there can be unused space in the array. This isn't a problem.
sizeof(int) will return you 4 (and note that few compilers/settings may say you 2 or 8 in response). So your code is equivalent to allocating a 4 bytes long array.
If you want an array with unknown size, it could be worth to take a loot at STL containers like std::vector (because it will manage allocations and resizes behind the scene). If you plan to stick with "plain C" scope, you may be interested with TSTL2CL library: http://sourceforge.net/projects/tstl2cl
The basic thing is, ARRAY is STATIC not DYNAMIC.
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.