Struct with array of structs of unknown size - c

I've been trying to wrap my head around this the whole day...
Basically, I have a struct called State that has a name and another one called StateMachine with a name, an array of states and total number of states added:
#include <stdio.h>
#include <stdlib.h>
typedef struct State {
const char * name;
} State;
typedef struct StateMachine {
const char * name;
int total_states;
State ** states;
} StateMachine;
StateMachine * create_state_machine(const char* name) {
StateMachine * temp;
temp = malloc(sizeof(struct StateMachine));
if (temp == NULL) {
exit(127);
}
temp->name = name;
temp->total_states = 0;
temp->states = malloc(sizeof(struct State));
return temp;
}
void destroy_state_machine(StateMachine* state_machine) {
free(state_machine);
}
State * add_state(StateMachine* state_machine, const char* name) {
State * temp;
temp = malloc(sizeof(struct State));
if (temp == NULL) {
exit(127);
}
temp->name = name;
state_machine->states[state_machine->total_states]= temp;
state_machine->total_states++;
return temp;
}
int main(int argc, char **argv) {
StateMachine * state_machine;
State * init;
State * foo;
State * bar;
state_machine = create_state_machine("My State Machine");
init = add_state(state_machine, "Init");
foo = add_state(state_machine, "Foo");
bar = add_state(state_machine, "Bar");
int i = 0;
for(i; i< state_machine->total_states; i++) {
printf("--> [%d] state: %s\n", i, state_machine->states[i]->name);
}
}
For some reason (read low C-fu / years of ruby/python/php) I'm unable to express the fact that states is an Array of State(s). The above code prints:
--> [0] state: ~
--> [1] state: Foo
--> [2] state: Bar
What happened with the first state added?
If I malloc the states array on the first state added (e.g. state_machine = malloc(sizeof(temp)); then I get the first value but not the second.
Any advices?
This is a C question. I'm using gcc 4.2.1 to compile the sample.

It looks like you're not allocating space for your states in the machine past the first one.
StateMachine * create_state_machine(const char* name) {
StateMachine * temp;
temp = malloc(sizeof(struct StateMachine));
if (temp == NULL) {
exit(127);
}
temp->name = name;
temp->total_states = 0;
temp->states = malloc(sizeof(struct State)); // This bit here only allocates space for 1.
return temp;
}
You're probably better off putting an array of states of fixed size in the state machine struct. If that's not okay, you'll have to realloc and move the whole set around or allocate chunks and keep track of the current length, or make a linked list.
Incidentally, init, foo, and bar never get used.
Edit: What I'm suggesting looks like this:
#define MAX_STATES 128 // Pick something sensible.
typedef struct StateMachine {
const char * name;
int total_states;
State *states[MAX_STATES];
} StateMachine;

It looks like you want to have a variable number of states in each state machine, but you are allocating the memory incorrectly. In create_state_machine, this line:
temp->states = malloc(sizeof(struct State));
Allocates a single State object, not an array of pointers (which is how you are using it).
There are two ways you could change this.
Declare states as State states[<some-fixed-size>]; but then you cant ever have more than a fixed number of states.
Add another member to indicate how much storage has been allocated for states, so you can keep track of that as well as how much is used (which is what total_states is being used for).
The later would look something like this:
#include <stdlib.h>
#include <string.h>
typedef struct
{
const char *name;
} State;
typedef struct
{
const char *name;
int total_states;
int states_capacity;
State *states;
} StateMachine;
StateMachine *create_state_machine(const char *name)
{
StateMachine *temp = malloc(sizeof(StateMachine));
memset(temp, 0, sizeof(*temp));
temp->name = name;
temp->states_capacity = 10;
temp->states = malloc(sizeof(State) * temp->states_capacity);
return temp;
}
State *add_state(StateMachine *machine, const char *name)
{
if (machine->total_states == machine->states_capacity)
{
// could grow in any fashion. here i double the size, could leave
// half the memory wasted though.
machine->states_capacity *= 2;
machine->states = realloc(
machine->states,
sizeof(State) * machine->states_capacity);
}
State *state = (machine->states + machine->total_states);
state->name = name;
machine->total_states++;
return state;
}

Inside of your add_state function:
temp = malloc(sizeof(struct StateMachine));
should be
temp = malloc(sizeof(struct State));
However, even when this is changed, I still get the proper output:
--> [0] state: Init
--> [1] state: Foo
--> [2] state: Bar
Perhaps there's nothing wrong with your code. I'm using gcc version 4.4.3

State ** states;
will create an array of state arrays.
I haven't read through the whole solution truthfully (gotta run), but you mentioned wanting an array of states - did you possibly want to do:
State* states
or
State states[size];
instead? Just food for thought, chances are it wasn't your problem since I didn't fully read it :p

You're doing a conceptual error:
State ** states;
It's true that you can consider states like an array of pointer to State object, but you are allocating space for just one state.
When you do:
state_machine->states[state_machine->total_states]= temp;
you are doing something wrong if total_states is greater than zero because you are pointing to memory segments that are not been allocated (I'm wondering why you don't get a SEGFAULT). To store a dynamic number of State this way you need a linked list, or to call realloc every state you add(but that's not a good idea). The memory you are allocating with different malloc calls isn't continuous.

Related

Pass local variable of a function back to it's parameter

I'm wanting to pass a local variable within a function, back through it's pointer parameter (not returned).
My assignment uses a stack data structure, and one criteria that must be used is the Pop() function must have a pointer parameter that is used to return the top-most item on the stack. I have used this before. My program became more complex with a data struct, I started getting either segmentation faults, or the data not being saved after the function's frame popped.
// Definitions
typedef char * string;
typedef enum { SUCCESS, FAIL } result;
typedef enum { INTEGER, DOUBLE, STRING } item_tag;
// Result Check
static result RESULT;
// Item_Tag
typedef struct {
item_tag tag;
union {
int i;
double d;
string s;
} value;
} item;
// Declarations
int STACK_SIZE = 0;
const int MAX_STACK_SIZE = 1024; // Maximum stack size
item stack[1024];
// Pop
result Pop(item *ip){
item poppedItem;
item * pointerReturn = malloc(sizeof(item));
// Check stack size is not 0
if(STACK_SIZE == 0){
return FAIL;
}
// If stack size is only 1, creates a blank stack
else if(STACK_SIZE == 1){
item emptyItem;
// Initialize
emptyItem.tag = INTEGER;
emptyItem.value.i = 0;
// Check top item's tag
poppedItem = stack[0];
// Store top item data based on tag
switch(stack[0].tag){
case STRING:
poppedItem.value.s = stack[0].value.s;
case DOUBLE:
poppedItem.value.d = stack[0].value.d;
default:
poppedItem.value.i = stack[0].value.i;
}
poppedItem.tag = stack[0].tag;
// Allocate memory for parameter, and have it point to poppedItem
ip = malloc(sizeof(poppedItem));
*ip = poppedItem;
// Store empty stack to top of stack
stack[0] = emptyItem;
// Decrease stack size
STACK_SIZE--;
}
// Grab top Item from stack
else{
// Check top item's tag
poppedItem = stack[0];
// Store top item data based on tag
switch(stack[0].tag){
case STRING:
poppedItem.value.s = stack[0].value.s;
case DOUBLE:
poppedItem.value.d = stack[0].value.d;
default:
poppedItem.value.i = stack[0].value.i;
}
poppedItem.tag = stack[0].tag;
// Allocate memory for parameter, and have it point to poppedItem
ip = malloc(sizeof(poppedItem));
*ip = poppedItem;
// Reshuffle Items in Stack
for(int idx = 0; idx < STACK_SIZE; idx++){
stack[idx] = stack[idx + 1];
}
STACK_SIZE--;
}
return SUCCESS;
}
My knowledge with pointers is alright, and memory location/management. But I can't claim to be an expert by any means. I don't exactly know what happens in the background when you're using the function's own pointer parameter as a means of passing data back.
What is the correct syntax to solve this problem?
How can a parameter pass something back?
Thanks in advance!
EDIT*
Since many people are confused. I'll post some snippets. This is an assignment, so I cannot simply post all of it online as that'd be inappropriate. But I think it's okay to post the function itself and have people analyze it. I'm aware it's a bit messy atm since I've edited it several dozen times to try and figure out the solution. Sorry for the confusion. Keep in mind that not all the code is there. just the function in question, and some of the structure.
The function should receive a pointer to a valid object:
item catcher;
myFunc(&catcher); // Pass a pointer to catcher
and the function should modify the object it received a pointer to:
void myFunc(item *itemPointer)
{
itemPointer->variable = stuff;
// or
*itemPointer = someItem;
}
Update:
You're overcomplicating things immensely – there should be no mallocs when popping, and you're leaking memory all over the place.
(Your knowledge of pointers and memory management is far from "alright". It looks more like a novice's guesswork than knowledge.)
It should be something more like this:
result Pop(item *ip){
if (STACK_SIZE == 0){
return FAIL;
}
else {
*ip = stack[0];
for(int idx = 0; idx < STACK_SIZE; idx++){
stack[idx] = stack[idx + 1];
}
STACK_SIZE--;
}
return SUCCESS;
}
but it's better to push/pop at the far end of the array:
result Pop(item *ip){
if (STACK_SIZE == 0){
return FAIL;
}
else {
*ip = stack[STACK_SIZE-1];
STACK_SIZE--;
}
return SUCCESS;
}
Response to the originally posted code:
typedef struct{
variables
}item;
void myFunc(item *itemPointer){
item newItem;
newItem.variable = stuff;
}
int main(){
item * catcher;
myFunc(catcher);
printf("%s\n", catcher.variable);
}
A few issues.
Your program will not compile. variable has to have a type.
void myFunc(item *itemPointer){
item newItem;
newItem.variable = stuff;
}
stuff is not defined; item *itemPointer is not used.
item * catcher pointer has to point to allocated memory. It is not initialized.
Pass arguments via pointers and modify member of the structure like this:
void myFunc(item *itemPointer, const char *string){
itemPointer->variable = string ;
}
Solution like:
void myFunc(item *itemPointer)
{
itemPointer->variable = stuff;
// or
*itemPointer = someItem;
}
is possible, but it assumes that stuff or someItem is a global variable which is not the best programming practice IMO.
Retrieve value from pointer via -> not . operator.
#include <stdio.h>
#include <stdlib.h>
typedef struct{
char * variable;
}item;
void myFunc(item *itemPointer, const char *string){
itemPointer->variable = string ;
}
int main(){
item * catcher;
char *new_string = "new string";
catcher = malloc(sizeof(item));
myFunc(catcher, new_string);
printf("%s\n", catcher->variable);
free(catcher);
return 0;
}
OUTPUT:
new string

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.

Abort trap 6 malloc

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*));

C Pointer and Memory Allocation: Realloc Arrays and Pointer Passing

For those experienced with C, this will be a simple memory allocation/referencing problem:
Here are my data structures:
struct configsection {
char *name;
unsigned int numopts;
configoption *options;
};
typedef struct configsection configsection;
struct configfile {
unsigned int numsections;
configsection *sections;
};
typedef struct configfile configfile;
Here are my routines for initializing a configsection or configfile, and for adding a configsection to a configfile:
// Initialize a configfile structure (0 sections)
void init_file(configfile *cf) {
cf = malloc(sizeof(configfile));
cf->numsections = 0;
}
// Initialize a configsection structure with a name (and 0 options)
void init_sec(configsection *sec, char *name) {
sec = malloc(sizeof(configsection));
sec->numopts = 0;
sec->name = name;
printf("%s\n", sec->name);
}
// Add a section to a configfile
void add_sec(configfile *cf, configsection *sec) {
// Increase the size indicator by 1
cf->numsections = cf->numsections + 1;
// Reallocate the array to accommodate one more item
cf->sections = realloc(cf->sections, sizeof(configsection)*cf->numsections);
// Insert the new item
cf->sections[cf->numsections] = *sec;
}
I believe my problem originates in my init_sec() function. Here is an example:
int main(void) {
// Initialize test configfile
configfile *cf;
init_file(cf);
// Initialize test configsections
configsection *testcs1;
init_sec(testcs1, "Test Section 1");
// Try printing the value that should have just been stored
printf("test name = %s\n", testcs1->name);
Although the printf() in init_sec() successfully prints the name I just stored in the configsection, attempting the same thing in the printf() of main() produces a segmentation fault. Further, addsec() produces a segmentation fault.
This routine should be
void init_file(configfile **cf) {
*cf = malloc(sizeof(configfile));
(*cf)->numsections = 0;
(*cf)->sections = NULL; // You forgot to initialise this.
}
i.e. called by init_file(&myconfigfilepointer); so the malloc return value gets passed back.
Need to do the same trick for init_sec
This function is incorrect - here is a corrected version
void add_sec(configfile *cf, configsection *sec) {
// Increase the size indicator by 1
// Reallocate the array to accommodate one more item
cf->sections = realloc(cf->sections, sizeof(configsection)*(1 + cf->numsections));
// Insert the new item
cf->sections[cf->numsections] = *sec; // Since arrays start at 0
cf->numsections = cf->numsections + 1;
}
You then need to adjust the calls in main
At no point do you initialise cf->sections, which means when you try to realloc it the first time, you're passing rubbish. Adding:
cf->sections = NULL;
to init_file should help.
You're also not checking any return codes, but you knew that yes?
You need to pass a pointer of the value to be updated... eg:
// Initialize a configfile structure (0 sections)
void init_file(configfile **cf) {
*cf = malloc(sizeof(configfile));
(*cf)->numsections = 0;
}
configfile *var;
init_file(&var);
printf("%d\n", var->numsections);
Otherwise you are just updating the local pointer *cf and not the original passed in value
You need to really rethink how function arguments are passed in C and what pointers are. Your problem has nothing to do with memory allocation. Rather, your code is assigning a pointer to dynamically allocated memory only to a local variable, of which the calling code knows nothing.
While you could solve the problem by passing a pointer to the caller's pointer (i.e. a double pointer), this is not necessarily the most elegant or most usual way of handling things. Rather, you should return the result of the allocation from the function. While you're at it, you should also use calloc to zero out the memory right away. Wrapping it all up:
typedef struct substuff_
{
int a;
double b;
} substuff;
typedef struct stuff_
{
unsigned int n;
substuff * data;
} stuff;
substuff * init_substuff()
{
substuff * const p = malloc(sizeof *p);
if (p) { p->a = 5; p->b = -0.5; }
return p;
}
stuff * init_stuff()
{
substuff * const p = init_substuff();
if (!p) return NULL;
stuff * const q = malloc(sizeof *q);
if (q) { q->n = 10; q->data = p; }
return q;
}
As an exercise, you should write the corresponding functions void free_substuff(substuff *) and void free_stuff(stuff *).
Yes, there is a problem in init_sec
// Initialize a configsection structure with a name (and 0 options)
void init_sec(configsection *sec, char *name) {
sec = malloc(sizeof(configsection));
sec->numopts = 0;
sec->name = name;
printf("%s\n", sec->name);
}
You're just copying the name pointer here, which means, that it points to the original storage of name. If you'd call init_sec like this
configsection foobar()
{
configsection sec;
char name[80];
get_name(name);
init_sec(sec, name);
return sec;
}
The name pointer became invalid the moment foobar returned. You need to duplicate the string and keep your private copy around. In init_sec:
sec->name = strdup(name);
But there's more. In the very first line of init_sec you're overwriting the pointer that was passed to init_sec with the one of malloc. So the new pointer never gets passed back to the calle. Either use a pointer to a pointer, don't take a configsection pointer at all (after all, you're allocating), but just return the allocated pointer: Complete corrected function:
// Initialize a configsection structure with a name (and 0 options)
configsection* init_sec(char *name) {
configsection *sec = malloc(sizeof(configsection));
sec->numopts = 0;
sec->name = name;
printf("%s\n", sec->name);
return sec;
}

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