odd program behavior after adding one instruction in C - c

Actually, I have asked another question with the same code, but this is very different.
I have this code below that displays a very annoying behavior. I've included as much comment in the code as I can so that you can have an idea of what's going on.
#include <stdio.h>
#include <stdlib.h>
/* This is a struct describing properties of an element */
struct element{
int age;
char* name;
};
/* This struct contains a pointer to a pointer on a element "struct element" */
struct person{
struct element** p;
size_t size;
unsigned int id;
};
/* This function initializes a struct person by allocating memory for it */
struct person* init(int _size)
{
if(_size == 0)
{
printf("You gonna have to make some choices \n");
exit(1);
}
struct person* sample = (struct person* )malloc(_size*sizeof(struct person));
sample->p = (struct element** ) malloc(_size*sizeof(struct element*));
sample->id = 0;
sample->size = _size;
return sample;
}
/* use this function to insert a new element in the struct */
void insert(struct person* sample, char* _name, int _age)
{
if (sample->id >= sample->size) {
sample->p = (struct element** ) realloc(sample->p, (sample->size*2) * sizeof(struct element*));
if(sample->p == NULL){
printf("Get a new RAM buddy \n");
exit(1);
}
}
sample->p[sample->id]->name = _name;
sample->p[sample->id]->age = _age; /* of course, this will cause trouble too because it has the same construct as the previous one */
sample->id++;
}
/* main entry */
int main(int argc, char** argv)
{
int i = 0;
struct person* student = init(10); /* Allocating space for 10 students */
insert(student, "baby", 2);
insert(student, "dady", 33);
/* if you remove this line, the program runs, but GDB will signal a segmentation fault. If you keep it, the program will freeze and GDB will behave as expected */
/* I don't understand why this is happening!!!??? */
insert(student, "grandma", 63);
printf("Your name is %s and your age is %d \n", student->p[1]->name, student->p[1]->age);
/* When you only insert two elements, use the results here to match with GDB's results*/
printf("student->p: %p \n", &student->p);
printf("student->p[0]: %p \n", &student->p[0]);
printf("student->p[1]: %p \n", &student->p[1]);
printf("student->p[0]->age: %p \n", &student->p[0]->age);
printf("student->p[0]->name: %p \n", &student->p[0]->name);
/* Won't work for more than two elements inserted */
for(i = 0; i < 2; i++){
printf("Your name is %s and your age is %d \n", student->p[i]->name, student->p[i]->age);
}
return 0;
}
I hope you can figured out what's going on.
Here is a part of a debugging session.
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: C:\Users\NTWALI\Desktop\tests\help\bin\Debug/help.exe
[New thread 11408.0x1228]
Error: dll starting at 0x770a0000 not found.
Error: dll starting at 0x76ab0000 not found.
Error: dll starting at 0x770a0000 not found.
Error: dll starting at 0x76d40000 not found.
Program received signal SIGSEGV, Segmentation fault.
0x0040146f in insert (sample=0x6816c0, _name=0x409031 "ntwali", _age=22) at C:\Users\NTWALI\Desktop\tests\help\main.c:44
44 sample->p[sample->id]->name = _name;
(gdb) p sample
$4 = (struct person *) 0x6816c0
(gdb) p sample->p
$5 = (struct element **) 0x681750
(gdb) p sample->p[0]
$6 = (struct element *) 0xbaadf00d
(gdb) p sample->p[1]
$7 = (struct element *) 0xbaadf00d
(gdb)
As you see in the code comment's, the data the program gives when it works, don't match with what one gets with GDB.
Thanks for helping.

You haven't allocated any memory for an element as far as I can see.
Here you allocate memory for a pointer to an element:
sample->p = (struct element** ) malloc(_size*sizeof(struct element*));

If the presence of a debugger alters the way your program behaves, you are very likely misusing memory or threads. Just like daven11 points out, you are not allocating the elements itself.

The root cause of your problem is that you are allocating pointers to struct element, but those pointers are uninitialised - you're not allocating any actual struct element objects. When you dereference those invalid pointers, you get undefined behaviour.
There is also no need to allocate _size of the struct person structs - you only ever use one. Your struct person should look like (note type of p is different):
struct person{
struct element *p;
size_t size;
unsigned int id;
};
and your init() function should then look like:
struct person* init(int _size)
{
if(_size < 1)
{
printf("You gonna have to make some choices \n");
exit(1);
}
struct person* sample = malloc(sizeof *sample);
sample->p = malloc(_size * sizeof sample->p[0]);
sample->id = 0;
sample->size = _size;
return sample;
}
The insert() function should look like:
void insert(struct person* sample, char* _name, int _age)
{
if (sample->id >= sample->size) {
sample->size *= 2;
sample->p = realloc(sample->p, sample->size * sizeof sample->p[0]);
if(sample->p == NULL){
printf("Get a new RAM buddy \n");
exit(1);
}
}
sample->p[sample->id].name = _name;
sample->p[sample->id].age = _age; /* of course, this will cause trouble too because it has the same construct as the previous one */
sample->id++;
}
The main function should then use student->p[i].name and student->p[i].age to access the data.

here you are using p[] without first initializing it to point to anything. you have only allocated space for the pointers but haven't initialized them to point to anything. So when you do
sample->p[sample->id]->name = _name;
sample->p[sample->id]->age = _age;
p is pointing somewhere in memory and you are modifying what it points to.
instead, insert a
sample->p[sample->id] = malloc(struct element);
sample->p[sample->id]->name = _name;
sample->p[sample->id]->age = _age;
and it should work
PS. normally you don't cast malloc in C

Related

Segfault when dereferencing pointer to pointer within struct

I have a structure which includes a pointer to a pointer as one of its members. I keep getting a segfault when trying to dereference this pointer.
Create a person in person_init and give it a name (John). Name is a pointer to a character string. I can printf() no problem in this function. Returning to the main() function, again I can printf() the name no problem. But then when
I enter a new function and try to printf() I get a segfault. I'm really confused because I'm pretty sure name is being allocated on the heap.
What am I missing here?
code:
#include <stdio.h>
#include <stdlib.h>
/* structure with a pointer to pointer member */
struct person {
char **name;
};
/* allocate space for the strucutre */
int person_init(struct person **p)
{
struct person *newp = malloc(sizeof(struct person));
/* give a name, allocated on the heap */
char *name = malloc(sizeof(char) * 5);
*name = 'J';
*(name + 1) = 'o';
*(name + 2) = 'h';
*(name + 3) = 'n';
*(name + 4) = '\0';
newp->name = &name;
*p = newp;
printf("Name in init: %s\n", *(*p)->name); /* this works */
return 0;
}
void print_name(struct person *p)
{
printf(*p->name);
}
int main()
{
struct person *person;
person_init(&person);
printf("Name in main: %s\n", *person->name); /* works */
print_name(person); /* segfault */
}
Here's the problem:
newp->name = &name;
newp->name now points to name, which is a local variable in person_init. As soon as person_init returns, name is gone and newp->name is an invalid pointer. Any attempt to use it afterwards results in undefined behavior.
Fix:
struct person {
char *name;
};
And initialize it as
newp->name = name;
Now newp->name is a copy of name, i.e. it points to the allocated string.

Am I freeing memory properly?

Okay, so I have a homework assignment for a C programming class and I just finished with the output doing what I expected. However, I am still a bit confused on memory allocation and freeing.
Basically what has me question my self is the freeing of the structure memory and the "Change_name" function. In my program I am just taking the new name and setting the value who.name to the new name. But what happens to the "old" name in this scenario? when I call free(who), is the old name being deleted?
any clarification would be appreciated!
code:
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char *name;
int age;
int height;
int weight;
};
/* complete this function, which initialize the fileds of the struct, and return a pointer to the initialzied struct */
struct Person *Person_create(char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof(struct Person));
assert(who != NULL);
(*who).name = name;
(*who).age = age;
(*who).height = height;
(*who).weight = weight;
return who;
}
/* complete this function, which free memory that was allocated for a struct*/
void Person_destroy(struct Person *who)
{
assert(who != NULL);
free(who);
}
/* complete this function, which print the value of member of struct for the input argument */
void Person_print(struct Person *who)
{
printf("This person have the values of...\n");
printf("name: %s\n", (*who).name);
printf("age: %d\n", (*who).age);
printf("height: %d\n", (*who).height);
printf("weight: %d\n", (*who).weight);
}
/* complete this function, which change the value of filed member of the struct to the value of newName */
void Change_name (struct Person *who, char * newName)
{
(*who).name = newName;
}
int main(int argc, char *argv[])
{
// make two people structures
struct Person *joe = Person_create(
"Joe Alex", 32, 64, 140);
struct Person *frank = Person_create(
"Frank Blank", 20, 72, 180);
// print them out and where they are in memory
printf("Joe is at memory location %p:\n", joe);
Person_print(joe);
printf("Frank is at memory location %p:\n", frank);
Person_print(frank);
// make changes in filed of goe's struct print them again
joe->age += 20;
joe->height -= 2;
joe->weight += 40;
Change_name(joe, "Jack The third Junior Smith Benedickt");
Person_print(joe);
// destroy them both so we clean up
Person_destroy(joe);
Person_destroy(frank);
return 0;
}
Your call to Person_destroy doesn't free any of the names because you're just freeing who. But that's OK because you're also not dynamically allocating any of the names with malloc/strdup/etc...
TL;DR: For your specific example: yes, but there are caveats to what you're doing.
One question that needs addressing WRT change_name is this:
ut what happens to the "old" name in this scenario? when I call free(who), is the old name being deleted?
For reasons that, I hope, will become clear further down, there is no clear answer to this question. Your code assigns a char * blindly. You don't know where that string is stored. If it's a string constant with static storage (either global variable or const char * in main), that old name will remain in memory for as long as your application runs. If it's dynamically allocated, unsetting a pointer doesn't free the memory either. assigning a new pointer can potentially cause you to leak memory. The safest way is to copy the string (strdup), and free the pointer prior to changing the name field.
There's an underlying problem here: You can only free memory in the right way if you allocate it correctly. Strictly speaking, you are doing just that. However, a function that takes a char * shouldn't blindly assign that same pointer. The pointer might be a stack char[] that decayed into a pointer (because it was passed as an argument).
You have no idea where that string was allocated, how, and most importantly: how long the pointer will remain valid. A couple of scenario's where a char * can cause problems:
int main ( void )
{
struct Person *p = foobar();
printf("name => %s?", p->name);
Person_destroy(p);
return 0;
}
struct Person *foobar( void )
{
char bar[] = "this is a local string";
return Person_create(bar, 32, 64, 140);
}
The pointer to bar expires once foobar returns, so this is a problem (stack memory pointers).
A pointer on the heap might suffer from the same problem:
struct Person *foobar( void )
{
const char *x = "Name";
char *bar = strdup(x); // allocates on heap and copies string
// check for null's etc...
struct Person *person = Person_create(bar, 32, 64, 140);
//some more stuff happens, including:
bar = realloc(bar, strlen(x) + 255);
strncat(bar, " has been successfully allocated", 33);
return person;
}
The problem here is that realloc might memmove the original string, and return an entirely new pointer, in which case the name field of the struct will become invalid. If that doesn't happen, person->name now points to Name has been successfully allocated, which is a potential bug.
So I strongly advise you to copy the name string:
// note: const char *name
struct Person *Person_create(const char *name, int age, int height, int weight)
{
struct Person *who = malloc(sizeof *who); // shorter to write, more reliable
if (who == NULL)
exit(1);// or whatever
who->name = strdup(name); // create copy
//etc...
return who;
}
This means, of course that struct Person will need to free the name pointer:
void Person_destroy(struct Person *who)
{
free(who->name);
free(who);
}
Double indirection is a bit risky a lot of the time, but imagine someone doing something like this:
int main( void )
{
struct Person *p = Person_create("Name", 1, 2, 3);
//do stuff
Person_destroy(p);
// more stuff, eg:
printf("%p\n", (void *)p);
Person_destroy(p);
return 0;
}
This is not good,. p should be null'ed after freeing it. Freeing an invalid pointer is bad, mkay. 2 ways to make this a less common problem:
#define FREE_PERSON(p) do {\
Person_destroy(p);\
p = NULL;\
} while(0);
This macro will always set the person variable to NULL after calling Person_destroy. The downside is: it's a clunky macro, and people can (and will) bypass it.
Change Person_destroy a bit:
void Person_destroy(struct Person **p)
{
if (p == NULL)
return; // this is needed now
struct Person *tmp = *p;
free(tmp->name);
free(tmp);
*p = NULL; // set the pointer itself to NULL
}
This forces people to call Person_destroy with a pointer to their pointer, and automatically sets their pointer to NULL.
Again, good practice requires devs to do this themselves, but it's a trivial change and helps prevent problems over time.
Demo using the double-indirection approach

Storing void pointers in a struct

I'm getting segmentation fault on code that is trying to initialize a struct of pointers to 0mq context and socket. The commented out code in the main method works, but it's only using local variables. I would like to initialize them and pass them around in a struct, but my google foo is failing me on how to do this properly.
#include "zhelpers.h"
#include <stdio.h>
#include <stdlib.h>
#include <zmq.h>
struct publisher{
void *handle;
void *context;
};
void init_publisher(struct publisher *p);
void destroy_publisher(struct publisher *p);
void publish(struct publisher *p,char *msg);
void init_publisher(struct publisher *p)
{
p = (struct publisher *)malloc(sizeof(struct publisher));
p->context = malloc(sizeof(void *));
p->handle = malloc(sizeof(void *));
void *context = zmq_ctx_new();
void *handle = zmq_socket(context,ZMQ_PUB);
zmq_bind(handle, "tcp://*:5556");
zmq_bind(handle, "ipc://feed.ipc");
p->context = context;
p->handle = handle;
}
void destroy_publisher(struct publisher *p)
{
zmq_close(p->handle);
zmq_ctx_destroy(p->context);
free(p->handle);
free(p->context);
free(p);
}
void publish(struct publisher *p,char *msg)
{
s_send(p->handle, msg);
}
int main(void)
{
/**
void *context = zmq_ctx_new();
void *publisher = zmq_socket(context, ZMQ_PUB);
int rc = zmq_bind(publisher, "tcp://*:5556");
assert(rc == 0);
rc = zmq_bind(publisher, "ipc://weather.ipc");
assert(rc == 0);
printf("Started Weather Server...\n");
srandom((unsigned) time (NULL));
int zipcode, temperature, relhumidity;
zipcode = randof(100000);
temperature = randof (215) - 80;
relhumidity = randof (50) + 10;
char update[20];
sprintf(update, "%05d %d %d", zipcode, temperature, relhumidity);
s_send(publisher, update);
zmq_close(publisher);
zmq_ctx_destroy(context);
*/
struct publisher *p;
init_publisher(p);
printf("Setup pub\n");
srandom((unsigned) time (NULL));
int zipcode, temperature, relhumidity;
zipcode = randof(100000);
temperature = randof (215) - 80;
relhumidity = randof (50) + 10;
char update[20];
sprintf(update, "%05d %d %d", zipcode, temperature, relhumidity);
publish(p,update);
printf("Published Message\n");
destroy_publisher(p);
printf("Destroyed publisher\n");
return 0;
}
There appears to be nothing in this code that would make it crash. (Assuming you know how all your zmq_... stuff works.)
It would have helped if you told us precisely where the error occurs, but my guess would be that the error occurs outside of this code.
You see, you are passing struct publisher *p to your init_publisher() function, but then you are allocating memory for p inside that method, (which makes passing p pointless,) and then you are not returning p. As a result, the code that calls init_publisher() probably expects p to be initialized, but it is not. The memory pointed by p is just allocated and leaked locally within your init_publisher() function.
So, instead of passing p, just have the function declare it and return it.
Alternatively, if the caller has already allocated p, then do not allocate it all over again from within init_publisher().
Please also note that the statements p->context = malloc(sizeof(void *)); are unnecessary and they are leaking small amounts of memory, because you proceed to overwrite these struct members.
The problem is that the passed pointer and the pointer you malloc()ed are not the same. The passed pointer contains the same address of your original pointer, presumably an invalid address, but the addresses of the poninters them selves are different because in c you can only pass a variable by value and hence, the pointer is copied.
That means that when you reassign p inside the function, the p from outside the function is unaltered. It would be different if it was allocated outside and you just use the function to access it's members.
You also don't need to malloc() every pointer you want to use, the thing is that it must point to a valid address before dereferencing it. When you want to request new uninitialized memory then you use malloc() otherwise you just make the pointer point to a valid address so that dereferencing it is defined, one example of using a pointer without malloc()ing it is
int *pointer;
int value;
value = 4;
pointer = &value; // Now `pointer' points to `value's` address
*pointer = 3;
printf("%d\n", value);
One way to write the function would be
int
init_publisher(struct publisher **pp)
{
struct publisher *p;
*pp = malloc(sizeof(struct publisher));
if (*pp == NULL)
return -1;
p = *pp;
p->context = zmq_ctx_new();
p->handle = zmq_socket(context,ZMQ_PUB);
if (p->handle != NULL) /* Just in case, do not dereference a NULL pointer */
{
zmq_bind(p->handle, "tcp://*:5556");
zmq_bind(p->handle, "ipc://feed.ipc");
}
return 0;
}
and then you could use it like this
struct publisher *p;
if (init_publisher(&p) != 0)
do_something_there_was_an_error();
/* Continue using `p' */
Note that the funcion is returning a value indicating whether allocations succeeded or not. Normally malloc() will not fail, but that doesn't mean that you should ignore the possible failure.
What I mean when I say if you allocate p first, is that if you instead do this
struct publisher *p;
p = malloc(sizeof(*p));
if (p == NULL)
return handle_error();
init_publisher(p);
then init_publisher() could be
void
init_publisher(struct publisher *pp)
{
void *context;
void *handle;
p->context = zmq_ctx_new();
p->handle = zmq_socket(context,ZMQ_PUB);
if (p->handle != NULL) /* Just in case, do not dereference a NULL pointer */
{
zmq_bind(p->handle, "tcp://*:5556");
zmq_bind(p->handle, "ipc://feed.ipc");
}
}
which is probably what you was trying to do.

double pointer typedef struct array

I am new to c programming and I am stuck with this one its a typedef struct and what I would like to do is that I want to create an array from the double pointer from this structure
typedef struct
{
char* firstname;
float price;
}Name,*pName,**ppName;
typedef struct
{
ppName Names;
unsigned int numPerson;
}Book;
And my main which always give me segmentation fault dont mind the loop it is looping until the use says to quit.
int main(void)
{
Book D;
setUpCollection(&D);
while(..)
{
scanf(...);
switch(...)
{
case 1:
if(!AddNewPerson(&D))
return 1;
break;
case 2:
....
case 3:
....
default:
printf("Please enter a valid choice");
}
}
return 0;
}
void setUpCollection(Book* data){
Name name;
pName pname;
pname= malloc(MAX_PERSON* sizeof(pName));
pname= &name;
data->Names= &pname;
data->numPerson= 0;
}
BOOL AddNewPerson(Book* data){
char *title = malloc(sizeof(char));
int len;
Name name;
pName pname;
scanf(...);
len = strlen(firstname);
name.firstname = malloc(len * sizeof(char*));
name.firstname = firstname;
pname= malloc(1);
pname= &name;
data->DVDs[data->numPerson++] = pname;
printf("%0.2f", data->Names[(data->numPerson)-1]->price);
return TRUE;
}
My main problem is that I cant print all the added names and also getting segmentation fault.
There are quite a few errors in your program but let me mention a few:
Doesn't this seem odd to you:
pname= malloc(MAX_PERSON* sizeof(pName));
pname= &name;
you are creating a memory leak by first letting pname point to the array of pName then assigning to &name.
What is this:
char *title = malloc(sizeof(char)); // ?
here you allocate too less space
name.firstname = malloc(len * sizeof(char*));
it should be
name.firstname = malloc(len * sizeof(char) + 1);
or more readable:
name.firstname = malloc(len+1);
this makes no sense again:
pname= malloc(1);
pname= &name;
again you created a memory leak by first letting pname point to a heap block of 1 byte then assigning it to a local variable which you include in data - the local variable is freed up once you leave AddNewPerson() so data will point to garbage.
Instead do something like this (I am no fan of having
typedefs for pointers), also try avoiding naming types
the same way you name variables for clarity:
typedef struct
{
char *firstname;
float price;
} Name;
typedef struct
{
Name** names;
unsigned int numPerson;
} Book;
Now allocate the initial size of your array, the whole point
of having it on the heap is that the array can grow if more
records are added than MAX_PERSONS so you need to keep track
of the number of used records in the array as well as the number
of records allocated
int allocated = MAX_PERSONS;
Book D;
D.names = malloc( allocated * sizeof(Name*) );
D.numPerson = 0;
then loop over user input and add records keeping
track of how many records have been read. Since names
is an array of pointers, you need to allocate a Name
struct each time you add an entry
e.g.
D.names[i] = malloc( sizeof(Name) );
D.names[i]->firstname = strdup(userInputName);
D.names[i]->price = userInputPrice;
then at each iteration check if there is allocated memory left
++i;
if ( i == allocated )
{
// if yes you need to get more memory, use realloc for that
// get e.g. 10 more records
Name* tmp = realloc( D.names, (allocated + 10)*sizeof(Name) );
if ( tmp != NULL )
{
D.names = tmp;
allocated += 10;
}
else
{ .. some error msg .. }
}

Generic stack in plain C does not copies structures correct

So I have implemented a generic stack in Plain C. It should copy different type of data, inclusive structures. And by structures I have the problem.
So here's the structure of the stack:
/*
* Definite genStack as a structure.
* Pointer elems points to the objects lying on the stack
* The variable elemSize spiecifies the size of an element
* The variable logLength specifies the number of actually
* lying on the stack objects
* The variable allocLenght specifies the allocated size
*/
typedef struct{
void* elems;
int elemSize;
int logLength;
int allocLength;
}genStack;
Push and pop functions:
void GenStackPush(genStack *s, const void *elemAddr)
{
/* if stack is full - allocates more memory */
if (GenStackFull(s))
{
GenStackAlloc(s, s->elemSize);
}
memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr));
s->logLength++;
}
void GenStackPop(genStack *s, void *elemAddr)
{
if(GenStackEmpty(s))
{
fprintf(stderr, "Can't pop element from stack: stack is empty.\n");
} else
{
s->logLength--;
memcpy((void*) elemAddr, (s->elems)+(s->logLength), sizeof(s->elems[s->logLength]));
}
}
Simple structures test:
gentest.h:
#ifndef GENTEST1_H
#define GENTEST1_H
typedef struct {
char* name;
int age;
char gender;
}person;
#endif
gentest.c:
#include <stdio.h>
#include <stdlib.h>
#include "gentest1.h"
#include "genstacklib.h"
int main(int argc, char* argv[])
{
genStack StructStack;
person testPerson[5];
person* newPerson;
person* test;
int i;
newPerson = (void*) malloc (sizeof(person));
testPerson[0].name = "Alex";
testPerson[0].age = 21;
testPerson[0].gender = 'm';
testPerson[1].name = "Vanja";
testPerson[1].age = 20;
testPerson[1].gender = 'm';
testPerson[2].name = "sjrgsde";
testPerson[2].age = 11;
testPerson[2].gender = 'w';
testPerson[3].name = "wergsggsd";
testPerson[3].age = 99;
testPerson[3].gender = 'y';
testPerson[4].name = "adaasxx";
testPerson[4].age = 13;
testPerson[4].gender = 'g';
GenStackNew(&StructStack, sizeof(person));
printf("sizeof(person) = %lu\n", sizeof(person));
for (i = 0; i < 5; i++) {
newPerson = &testPerson[i];
GenStackPush(&StructStack, newPerson);
printf("Pushed: %s, %d, %c\n", newPerson->name, newPerson->age, newPerson->gender);
}
test = (void*) malloc (sizeof(person));
test->name = "test";
test->age = 0;
test->gender = 't';
while(!GenStackEmpty(&StructStack))
{
GenStackPop(&StructStack, test);
printf("Popped: %s, %d, %c\n", test->name, test->age, test->gender);
}
GenStackDispose(&StructStack);
return 0;
}
And here's the output I get:
./gentest1
elemSize = 16 GenStackInitialAlocationSize = 4
sizeof(person) = 16
Pushed: Alex, 21, m
Pushed: Vanja, 20, m
Pushed: sjrgsde, 11, w
Pushed: wergsggsd, 99, y
New size of alloc = 8
Pushed: adaasxx, 13, g
Popped: adaasxx, 0, t
Popped: wergsggsd, 0, t
Popped: sjrgsde, 0, t
Popped: Vanja, 0, t
Popped: Alex, 0, t
As you can see, I can receive names, but no age or gender. I've tried a lot of options, but still getting Segmentation Fault or the output from above. For moment, the output above is the finest output I get, but still not what I want.
The question is - how can I get the output I need?
Thanks in advance.
To avoid some questions:
sizeof(person) = s->elemSize
It is defined by creating the stack:
genstacklib.c:
void GenStackNew(genStack *s, int elemSize)
{
void* newElems;
/* Allocate a new array to hold the contents. */
newElems = (void*) malloc(elemSize * GenStackInitialAlocationSize);
printf("elemSize = %d\tGenStackInitialAlocationSize = %d\n",
elemSize, GenStackInitialAlocationSize);
if (newElems == NULL)
{
fprintf(stderr, "Error with allocating the stack.\n");
exit(1); /* Exit, returning error code. */
}
s->elems = newElems;
s->elemSize = elemSize;
s->allocLength = GenStackInitialAlocationSize;
s->logLength = 0; /*is empty*/
}
gentest.c:
GenStackNew(&StructStack, sizeof(person));
printf("sizeof(person) = %lu\n", sizeof(person));
your push function is copying sizeof(*elemAddr) and that is a void *, so it has the size of a pointer not the inteded size of a person struct. So you are probably copying only the first 4 bytes
As stated above the push is copying the wrong size of data. It should be elemSize.
The memcpy is also overwriting its own data. Something like this should work.
memcpy((char*) (s->elems)+(s->logLength)*elemSize, elemAddr, elemSize);
s->logLength++;
You're not using elemSize in all the relevant places...
void GenStackPush(genStack *s, const void *elemAddr)
{
...
memcpy((char*) (s->elems)+(s->logLength), elemAddr, sizeof(*elemAddr));
^^^^^^^^^^^^^^^^^
This is very wrong; the type of the expression *elemAddr is void, which is a constraint violation (sizeof may not be called on an expression of incomplete type, and void is an incomplete type). You will want to turn up the warning level on your compiler. I wrote a test program to compute sizeof on expressions of type void * and void, and I get a warning with gcc -pedantic. If I drop the -pedantic I don't get a warning, but the result I get for sizeof (void) is 1, which I'm pretty certain is not the size of a person. Why aren't you using s->elemSize here?
Secondly, why are you casting s->elems to char *?
EDIT
If I may offer some advice, I've womped up a few generic containers in the past, and here are the lessons I've come away with:
First, delegate all type-aware operations (allocation, deallocation, copy, compare, display, etc.) to separate functions, which are called via function pointers passed as parameters to the generic container's functions; i.e., a push would be defined like
GenStackPush(genStack *stack, const void *data, void *(*copy)(const void *))
{
stack->elems[++stack->logLength] = copy(data);
}
...
void *myIntCopyFunc(const void *data)
{
const int *inputData = (const int *) data;
int *copy = malloc(sizeof *copy);
if (copy)
*copy = *inputData;
return copy;
}
...
GenStackPush(&myIntStack, &intVal, myIntCopyFunc);
One issue you have with your person type is that you're not doing a deep copy of the name member; you're just copying a pointer value to the stack. In this case it's not a big deal since you're working with string literals, but if you were using, say, a local char [], you'd have problems. By writing a separate copy function for each type, you can deal with those sorts of issues, instead of trying to do a one-size-fits-all allocation in the container function itself.
Secondly, don't call your generic container functions directly; put a type-aware interface between you and the container (basically, the poor man's version of function overloading):
void pushInt(GenStack *stack, int intVal)
{
GenStackPush(stack, &intVal, myIntCopyFunc);
}
...
genStack myIntStack;
...
pushInt(&myIntStack, 5);
This gives you two benefits; first, it allows you to pass literal values as parameters (which you can't do with parameters of type void *). Secondly, it gives you a way to enforce type safety on your container. You can't accidentally push a value of the wrong type this way.
Is this a lot of extra work? Oh my yes. There's a lot of magic that has to happen under the hood for generic container types to work properly. If you're trying to replicate the same kind of functionality that you get with the C++ std::stack container type, you're going to be writing a lot of code.

Resources