I have structs:
typedef struct accont
{
char **tel;//list of tel
char **email;//list of emails
}acc;
and
typedef struct _strcol
{
int count; //total of accounts
acc **list;
} strcol ;
I access the structure with a pointer:
strcol index;
contato *p;
p = (index.list + index.count);
the question, how i use malloc() in this function?
i try:
(*p)->tel = (char **) malloc(i * sizeof (char*))
p.tel = (char **) malloc(i * sizeof (char*))
&(*p)->tel = (char **) malloc(i * sizeof (char*))
and then as I do the second malloc to save data email or tel
my first post, excuse anything
So this:
(*p)->tel = (char **) malloc(i * sizeof (char*))
allocates space to store i pointers to char - so you can have i telephone number strings. But you don't actually have any space allocated to store those telephone number strings themselves yet. To do that, you need (for the first telephone number):
(*p)->tel[0] = malloc(j);
If this call to malloc() succeeds, you can now store nul-terminated string of length j-1 in the space pointed to by (*p)->tel[0]. You can then do the same for the other pointers in (*p)->tel up to (*p)->tel[i-1].
Using malloc() is simple if code follows:
some_type *p;
p = malloc(number_of_elements * sizeof *p);
if (p == NULL) Handle_OutOfMemory();
So with p.tel,
// p.tel = (char **) malloc(i * sizeof (char*));
p.tel = malloc(i * sizeof *(p.tel));
if (p.tel == NULL) exit(EXIT_FAILURE);
I'm going to assume 'p' is acc *p; (i have no idea what 'contato' is).
Anyway ... the point is to show how memory can be allocated & tel/email data stored/accessed ... Also copied tel #/email id simply to demonstrate ...
Regarding casting void pointer returns from malloc, I've seen arguments for/against ... i cast (malloc's about the only case where i cast).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct accont
{
char **tel; //list of tel
char **email; //list of emails
}acc;
typedef struct _strcol
{
int count; //total of accounts
acc **list;
}strcol;
int main()
{
int iNumAccounts = 5; // Assume there are 5 Accounts.
int iNumTels = 2; // Each Account has 2 Tel #s.
int iNumEmails = 3; // Each Account has 3 Email ids.
strcol index;
acc *p = NULL;
index.list = (acc **)malloc(5 * sizeof(acc *)); // Master list
// of 'acc' pointers i.e. pointer to a set of pointers.
int i, j;
for(i=0; i<iNumAccounts; i++) // Go through the 5 Accounts, one at
// a time ... and allocate & store tel #s/email ids.
{
index.list[i] = (acc *)malloc(sizeof(acc));
p = index.list[i];
p->tel = (char **)malloc(iNumTels * sizeof(char*));
for(j=0; j<iNumTels; j++)
{
p->tel[iNumTels] = (char *)malloc(11 * sizeof (char)); // 10 digit tel # + 1 byte for '\0' ...
strcpy(p->tel[iNumTels], "1234567890");
}
p->email = (char **)malloc(iNumEmails * sizeof(char*));
for(j=0; j<iNumEmails; j++)
{
p->email[iNumEmails] = (char *)malloc(51 * sizeof(char)); // 50 char long email id + 1 byte for '\0' ...
strcpy(p->email[iNumEmails], "kingkong#ihop.yum");
}
}
for(i=0; i<iNumAccounts; i++) // Go through the 5 Accounts, one at a time ... and display.
{
p = index.list[i];
for(j=0; j<iNumTels; j++)
{
printf("Tel # is: %d\n", p->tel[iNumTels]);
}
for(j=0; j<iNumEmails; j++)
{
printf("Email id is: %s\n", p->email[iNumEmails]);
}
printf("----------\n");
}
}
If I've understood the case correct, a stack implementation will be best suited in this case. You can use the standard stack library header (of gcc) or create your own stack implementation suited for your own need.
An example may be something like the code below but you'd better follow the Jerry Cain's videos about the stack procedures (you'll find these videos on youtube: Stanford - Programming Paradigms videos. Stack session should be between video number 6 to 8). link from here
note: be careful! Killing stack elements (via StackPop) will not kill the char strings created by strdup. You'll need to free them individually. These are explained in the videos but I don't exactly remember how (again, you'd find some valuable info in those videos for your case).
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
typedef struct {
char *tel;
char *email;
} account;
typedef struct {
int *ptrElement; // starting address of the stack
int sizeAllocat; // total size allocated
int sizeCurrent; // current size
int sizeElement; // byte length of the stack element
} Stack;
// create a new stack pointer
void StackNew (Stack *s, int sizeElement) {
assert (s->ptrElement > 0);
s->sizeElement = sizeElement;
s->sizeCurrent = 0;
s->sizeAllocat = 4;
s->ptrElement = malloc (4 * sizeElement);
assert (s->ptrElement != NULL);
}
// kills a stack pointer
void StackDispose (Stack *s) {
free (s->ptrElement);
}
// expands stack space
static void StackGrow (Stack *s) {
s->sizeAllocat *= 2;
s->ptrElement = realloc (s->ptrElement, s->sizeAllocat * s->sizeElement);
}
// insert new stack pointer (of type account for example)
void StackPush (Stack *s, void *ptrElement) {
if (s->sizeCurrent == s->sizeAllocat) {
StackGrow (s);
}
void *target = (char *) s->ptrElement + s->sizeCurrent * s->sizeElement;
memcpy (target, ptrElement, s->sizeElement);
s->sizeCurrent++;
}
// pops (deletes) an element from stack
void StackPop (Stack *s, void *ptrElement) {
void *source = (char *) s->ptrElement +
(s->sizeCurrent - 1) * s->sizeElement;
memcpy (ptrElement, source, s->sizeElement);
s->sizeCurrent--;
}
// relocate stack element
void StackRotate (void *first, void *middle, void *after) {
int foreSize = (char *) middle - (char *) first;
int backSize = (char *) after - (char *) middle;
char tmp [foreSize];
memcpy (tmp, first, foreSize);
memmove (first, middle, backSize);
memcpy ((char *) after - foreSize, tmp, foreSize);
}
int main () {
Stack s;
account *acc;
StackNew (&s, sizeof (acc));
// your code here
// example
// acc->tel = strdup("some number");
// acc->email = strdup("some text");
// StackPush(&s, &acc);
...
// StackPop(&s, &acc);
...
...
StackDispose (&s);
return 0;
}
Related
I am experiencing a problem in shrinking the size of a stack in a personal implementation of the data structure.
I suppose is due to bad usage of realloc(). When the execution comes it (spop(), empty()) (If I remove the realloc and decrement the number of elements, the implementation works fine), the program just ends (crash).
What would be a better way to use the function in my implementation, or what might the problem be?
stack.h
/*Stack.h*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
typedef struct Stack{
char **storage; //Elements container;
size_t capacity; //Total amount of elements POSSIBLE in the stack;
size_t size; //Total amount of elements within the stack;
}Stack;
Stack *salloc(size_t);
void spush(Stack *, char *);
char *spop(Stack *);
void speek(Stack *);
void empty(Stack *);
void print_stack(Stack *); //Useful but non-conventional
stack.c
/*Stack.c*/
#include "stack.h"
Stack *salloc(size_t size){
Stack *s = (Stack *)malloc(sizeof(s));
s->storage = (char **)malloc(sizeof(char *) * size);
s->capacity = size;
s->size = 0;
}
static int expand_stack(Stack *s){
s->storage = realloc(s->storage, (s->capacity * 2));
}
static void shrink_stack(Stack *s){
s->storage = realloc(s->storage, (s->capacity / sizeof(char *)));
}
void spush(Stack *s, char *elem){
char *p = elem;
int k = (s->capacity-1) - s->size; //Next free position
if(s->size == s->capacity)
expand_stack(s);
s->storage[k] = (char *)malloc(sizeof(char) * (strlen(p) + 1));
memcpy(s->storage[k], p, (strlen(p) + 1));
// *(s->storage[k] + (strlen(p) + 1)) = '\0';
s->size++;
}
char *spop(Stack *s){
int k = s->capacity - s->size;
if(s->size == 0)
return NULL;
free(s->storage[k]);
s->size--;
shrink_stack(s);
}
void speek(Stack *s){
int k = s->capacity - s->size;
printf("'%s'\n", s->storage[k]);
}
void empty(Stack *s){
s->storage = realloc(s->storage, 0);
s->capacity = 0;
s->size = 0;
}
void print_stack(Stack *s){
printf("[STACK] = {\n");
int k = s->capacity - s->size;
for(int i = k; i <= s->capacity-1; i++)
printf(" '%s'\n", s->storage[i]);
printf("}\n");
}
main.c
#include "stack.h"
#define COM1 "echo"
#define COM2 "start"
#define COM3 "sort"
int main(){
Stack *s = salloc(5);
spush(s, COM1);
spush(s, COM2);
spush(s, COM3);
// speek(s);
print_stack(s); //Full Stack
spop(s);
print_stack(s);
spush(s, "cd");
print_stack(s);
empty(s);
print_stack(s);
}
Stack *salloc(size_t size){
Stack *s = (Stack *)malloc(sizeof(s));
s->storage = (char **)malloc(sizeof(char *) * size);
s->capacity = size;
s->size = 0;
}
Your first malloc call only allocates enough space for a Stack*, not enough space for the actual Stack structure. You want:
Stack *s = (Stack *)malloc(sizeof(*s));
or
Stack *s = (Stack *)malloc(sizeof(Stack));
There are quite a few issues in your code:
salloc is missing return s.
spop does not return anything (except for the NULL case).
salloc is not allocating enough memory for a Stack object (sizeof(s) is the size of the pointer, not the Stack object).
In all the calls in the form: s->storage = realloc(...) - the result from realloc (void*) should be cast to char**.
expand_stack is defined to return an int but nothing is actually returned. Should probably be changed to return void.
shrink_stack is not calculating the size properly. As a result in your case realloc can actually allocate a 0 size memory (Note:: this is a cause for an access violation exception in print_stack after calling spop). I suggest you use a debugger to catch this bug.
There's a lot of problems here. Both in design and in implementation, but all lessons worth learning.
Here are a summary of the changes:
salloc - We should store the initial capacity. empty was just freeing all storage which would make the stack unusable. By storing initial capacity, shrink_stack can avoid shrinking below that.
expand_stack - capacity must be modified after the expansion or we lose track of what the actual allocation is. Also, we wouldn't be able to add beyond the initial capacity without running into problems. Going by the int return, I suspect you intended to return the new capacity (which should be size_t.
shrink_stack should not just keep dividing the capacity, or eventually we hit zero. So using the initial_capacity we keep things no smaller than at the outset. It also needs to only shrink if the size has dropped to the point where there is value doing so.
spush - I don't know why you chose to allocate from the end of the storage but this fundamentally would break when the capacity increased and expansion occurred. Much simpler to add according to size, and pop off from size back towards zero. Note that const is added or some compilers will complain about passing a non-const pointer to a string literal, which is dangerous.
spop - As per spush, pop from size - 1 back towards zero. The other bug here was that the string is stored in a malloc'd buffer, so we should be freeing that, and that means we can't just return the pointer, but need the spop signature to provide a buffer and max size in which to put it. Given that we are dealing with null terminated strings, this can be an strncpy. Note that the return value is the passed address, or NULL if there is no element to pop. There are other ways to perhaps handle this that help remove the risk of elem == NULL etc.
speek - Again, just use size - 1
empty - Uses spop to remove all elements without discarding the initial capacity storage.
print_stack - From zero to size.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
typedef struct Stack{
char **storage; //Elements container;
size_t capacity; //Total amount of elements POSSIBLE in the stack;
size_t size; //Total amount of elements within the stack;
size_t initial_capacity;
}Stack;
Stack *salloc(size_t size){
Stack *s = (Stack *)malloc(sizeof(s));
s->storage = (char **)malloc(sizeof(char *) * size);
s->capacity = s->initial_capacity = size;
s->size = 0;
return s;
}
static int expand_stack(Stack *s){
s->storage = (char **)realloc(s->storage, (sizeof(char *) * s->capacity * 2));
s->capacity = s->capacity * 2;
return s->capacity;
}
static void shrink_stack(Stack *s){
if (s->capacity > s->initial_capacity && s->size < s->capacity / 2) {
s->storage = (char **)realloc(s->storage, (sizeof(char *) * (s->capacity / 2)));
s->capacity = s->capacity / 2;
}
}
void spush(Stack *s, char const * elem){
if(s->size == s->capacity)
expand_stack(s);
size_t size = strlen(elem) + 1;
s->storage[s->size] = (char *)malloc(sizeof(char) * size);
memcpy(s->storage[s->size], elem, size);
s->size++;
}
char *spop(Stack *s, char * elem, size_t size){
if(s->size == 0)
return NULL;
if (size > 0) {
strncpy(elem, s->storage[s->size - 1], size);
}
free(s->storage[s->size - 1]);
s->storage[s->size - 1] = NULL;
s->size--;
shrink_stack(s);
return elem;
}
void speek(Stack *s){
printf("'%s'\n", s->storage[s->size - 1]);
}
void empty(Stack *s){
char notused;
while (spop(s, ¬used, 0) != NULL);
}
void print_stack(Stack *s){
printf("[STACK] = {\n");
for(int i = 0; i < s->size; i++)
printf(" '%s'\n", s->storage[i]);
printf("}\n");
}
#define COM1 "echo"
#define COM2 "start"
#define COM3 "sort"
int main(){
Stack *s = salloc(5);
spush(s, COM1);
spush(s, COM2);
spush(s, COM3);
// speek(s);
print_stack(s); //Full Stack
char string[64];
spop(s, string, sizeof(string)/sizeof(string[0]));
print_stack(s);
spush(s, "cd");
print_stack(s);
empty(s);
print_stack(s);
}
I have a structure:
struct student{
int roll_no;
char *name = malloc(25 * sizeof(char));;
char *phone_no = malloc(10 * sizeof(char));;
char *dob = malloc(10 * sizeof(char));;
}*s1;
int main(){
s1 = malloc(5 * sizeof(student)); //array of student
//.....
}
What is appropriate code for the complete loop for allocating an array of student of size 'n' and then de-allocating it afterwards?
Note: The question here deals with allocation and de-allocation of elements of the instance of a structure.
This...
typedef struct student{
int roll_no; // (the following illegal syntax commented out)
char *name; // = malloc(25 * sizeof(char));;
char *phone_no; // = malloc(10 * sizeof(char));;
char *dob; // = malloc(10 * sizeof(char));;
}*s1;
...from what is being described as the need, (minus the illegal assignment statements) could probably better be formed as:
typedef struct {
int roll_no;
char *name; //needs memory
char *phone; //needs memory
char *dob; //needs memory
}STUDENT;
Then, use the new variable type: STUDENT, to create the instances of the struct as needed. Your OP indicates you need 5:
STUDENT s[5]; //Although this array needs no memory, the
//members referenced by it do
//(as indicated above)
Now, all that is necessary is to create memory for the 3 members that require it, in each of the 5 instances.
for(i=0;i<5;i++)
{
s[i].name = calloc(80, 1); //calloc creates AND initializes memory.
s[i].phone = calloc(20, 1); //therefore safer than malloc IMO.
s[i].dob = calloc(20, 1); //Also, change values as needed to support actual
//length needs for name, phone and dob
}
// Use the string members of s[i] as you would any other string, But do not
// forget to free them when no longer needed.
...
for(i=0;i<5;i++)
{
free(s[i].name);
free(s[i].phone);
free(s[i].dob);
}
Note, because of the way the array s is created in this example, i.e. with memory on the stack instead of the heap, there is no need to free it.
One other note, the example code above focused on a method to create memory for the char * members of your struct array, but when actually coding for keeps, the return of [m][c][re]alloc should always be checked that memory was created before trying to use the variable. For example:
s[i].name = calloc(80, 1);
if(!s[i].name) //checking that memory was created
{
;//if failed, then handle error.
}
...
In addition to ryyker's answer, if you want to do it dynamically:
#include <stdlib.h>
struct student{
int roll_no;
char *name;
char *phone;
char *dob;
};
int main()
{
int i, student_count = 5;
struct student ** s = malloc(sizeof(struct student *) * student_count);
if (s)
{
for (i = 0; i < student_count; ++i)
{
s[i] = malloc(sizeof(struct student));
if (s[i])
{
//set up student's members
}
}
for (i = 0; i < student_count; ++i)
{
//free student's members before the next line.
free(s[i]);
}
free(s);
}
return 0;
}
You must free everything you malloc, and as mentioned in the comments you cannot malloc inside the struct.
#include <stdio.h>
#include <stdlib.h>
#define NUM_STUDENTS 5
struct student{
int roll_no;
char *name;
char *phone;
char *dob;
};
int main(void)
{
int i;
// if this was me, I would simply replace this with
// struct student s[NUM_STUDENTS];, but the goal here is to illustrate
// malloc and free
struct student* s = malloc(sizeof(struct student) * NUM_STUDENTS);
if (s == NULL) // handle error
for (i=0; i<NUM_STUDENTS; i++)
{
// sizeof(char) is guaranteed to be 1, so it can be left out
s[i].name = malloc(25);
if (s[i].name == NULL) // handle error
s[i].phone = malloc(10);
if (s[i].phone == NULL) // handle error
s[i].dob = malloc(10);
if (s[i].dob == NULL) // handle error
}
// do stuff with with the data
....
// time to clean up, free in the reverse order from malloc
for (i=0; i<NUM_STUDENTS; i++)
{
// the dob, phone, name order here isn't important, just make sure you
// free each struct member before freeing the struct
free(s[i].dob);
free(s[i].phone);
free(s[i].name);
}
// now that all the members are freed, we can safely free s
free(s);
return 0;
}
User Abhijit gave an answser that was in the right direction, but not complete. His answer should have been:
typedef struct STUDENT{
int roll_no;
char *name;
char *phone;
char *dob;
}student;
void example(int n_students)
{
student **s;
int i;
s= malloc(n_students * sizeof(student *));
for (i=0; i<n_students; i++)
{
s[i]= malloc(sizeof(student));
s[i]->name= malloc(25);
s[i]->phone= malloc(10);
s[i]->dob= malloc(10);
}
// now free it:
for (i=0; i<n_students; i++)
{
free(s[i]->name);
free(s[i]->phone);
free(s[i]->dob);
free(s[i]);
}
free(s);
}
I am working a function that needs to be re-entrant - the function is given a memory buffer as an argument and should use such buffer for all its memory needs. In other words, it can't use malloc, but rather should draw the memory the supplied buffer.
The challenge that I ran into is how to overlay an array of strings over a char array of given size (the buffer is supplied as char *), but my result is array of strings (char **).
Below is a repro:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 100
#define INPUT_ARRAY_SIZE 3
char *members[] = {
"alex",
"danny",
"max"
};
int main() {
// this simulates a buffer that is presented to my func
char *buffer = malloc(BUFFER_SIZE);
char *orig = buffer;
memset(buffer, NULL, BUFFER_SIZE);
// pointers will be stored at the beginning of the buffer
char **pointers = &buffer;
// strings will be stored after the pointers
char *strings = buffer + (sizeof(char *) * INPUT_ARRAY_SIZE);
for(int i = 0; i < INPUT_ARRAY_SIZE; i++) {
strncpy(strings, members[i], (strlen(members[i]) + 1));
// Need to store pointer to string in the pointers section
// pointers[i] = strings; // This does not do what I expect
strings += ((strlen(members[i]) + 1));
}
for (int i=0; i < BUFFER_SIZE; i++) {
printf("%c", orig[i]);
}
// Need to return pointers
}
With the problematic line commented out, the code above prints:
alex danny max
However, I need some assistance in figuring out how to write addresses of the strings at the beginning.
Of course, if there an easier way of accomplishing this task, please, let me know.
Here take a look at this.
/* conditions :
*
* 'buffer' should be large enough, 'arr_length','arr' should be valid.
*
*/
char ** pack_strings(char *buffer, char * arr[], int arr_length)
{
char **ptr = (char**) buffer;
char *string;
int index = 0;
string = buffer + (sizeof(char *) * (arr_length+1)); /* +1 for NULL */
while(index < arr_length)
{
size_t offset;
ptr[index] = string;
offset = strlen(arr[index])+1;
strcpy(string,arr[index]);
string += offset;
++index;
}
ptr[index] = NULL;
return ptr;
}
usage
char **ptr = pack_strings(buffer,members,INPUT_ARRAY_SIZE);
for (int i=0; ptr[i] != NULL; i++)
puts(ptr[i]);
I am trying to make a dynamic array of structs, and I can successfully add one struct to it. But any more structs I add cause a segmentation fault. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 4
struct Person {
char *first_name;
char *last_name;
unsigned int age;
};
int add_person(struct Person **people, size_t *people_size, size_t *population, struct Person p) {
if ((sizeof(struct Person) * *population) > *people_size) {
return -1;
}
if ((sizeof(struct Person) * (*population + 1)) >= *people_size) {
*people_size = *people_size + sizeof(struct Person) * PEOPLE_BLOCK;
*people = realloc(*people, *people_size);
if (!*people) {
return -1;
}
}
*people[*population] = p;
++*population;
return 0;
}
int main(int argc, char const *argv[]) {
size_t population;
size_t people_size;
struct Person *people, timn, batman;
population = 0;
people_size = sizeof(struct Person) * PEOPLE_BLOCK;
people = malloc(people_size);
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add_person(&people, &people_size, &population, timn);
printf("Person 0's first name: %s\n", people[0].first_name);
batman.first_name = "Bat";
batman.last_name = "Man";
batman.age = 42;
add_person(&people, &people_size, &population, batman);
printf("Person 1's first name: %s\n", people[1].first_name);
free(people);
return 0;
}
I'd appreciate any help on why this is happening, thanks!
The problem resides with this line :
*people[*population] = p;
Change it to:
(*people)[*population] = p;
Why are the parenthesis requried?
The compiler has rules of operator precedence. When applying them, it sees your code as this:
*(people[*population]) = p;
which is not what you intended. Given a pointer-to-pointer Type **pp,
*pp[n] = value;
means "take the n'th pointer starting at pp, and assign value at the location dereferenced from the address that pointer holds. In other words, it means essentially this:
Type *p = pp[n];
*p = value;
What you really want is something that does this:
Type *p = *pp;
p[n] = value;
and that is what (*pp)[n], distinguishing the dereference of the pointer to pointer, gives you. Without that, you're using an invalid pointer, leading to your fault.
Not sure whether this answer will help, but anyway.
I don't understand your code, what you are trying to do.
You directly use the number of elements, a pointer to the first person, and the maximum number of elements. You'll probably have a lot of problems passing that all around.
You're storing literal strings directly in your structs, which means that in a real case (using no literals) that would result in memory leaks.
Here is my take. I've made PEOPLE_BLOCK smaller for testing reasons.
Hope this helps.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PEOPLE_BLOCK 2
typedef struct _Person {
char *first_name;
char *last_name;
unsigned int age;
} Person;
typedef struct _VectorPeople {
Person * people;
size_t num;
size_t max;
} VectorPeople;
void init(VectorPeople *v)
{
v->max = PEOPLE_BLOCK;
v->num = 0;
v->people = (Person *) malloc( sizeof(Person) * v->max );
}
void clear(VectorPeople *v)
{
// Clear persons
Person * it = v->people;
while( ( it - v->people ) < v->num ) {
free( it->first_name );
free( it->last_name );
++it;
}
// Clear vector
v->max = v->num = 0;
free( v->people );
v->people = NULL;
}
void add(VectorPeople *v, Person *p)
{
// Reserve
if ( v->num >= v->max ) {
v->max += PEOPLE_BLOCK;
// Realloc
v->people = realloc( v->people, v->max * sizeof(Person) );
if ( v->people == NULL ) {
exit( -1 );
}
}
// Copy strings
p->first_name = strdup( p->first_name );
p->last_name = strdup( p->last_name );
// Insert
v->people[ ( v->num )++ ] = *p;
}
int main(int argc, char const *argv[]) {
VectorPeople vp;
Person timn;
Person batman;
Person bond;
Person superman;
init( &vp );
timn.first_name = "Timn";
timn.last_name = "Timothy";
timn.age = 38;
add( &vp, &timn );
batman.first_name = "Batn";
batman.last_name = "Man";
batman.age = 42;
add( &vp, &batman );
bond.first_name = "James";
bond.last_name = "Bond";
bond.age = 45;
add( &vp, &bond );
superman.first_name = "Super";
superman.last_name = "Man";
superman.age = 45;
add( &vp, &superman );
int i = 0;
for(; i < vp.num; ++i ) {
printf( "Person: %s, %s.\n", vp.people[ i ].last_name, vp.people[ i ].first_name );
}
clear( &vp );
return 0;
}
There were a number of errors in your code. One thing to keep in mind, when you dynamically allocate memory, you are responsible for keeping track of it and freeing it when you no longer need it (otherwise, you will leak memory like a sieve).
In your code, you attempt to create an array of structs holding pointer to an array of characters. The char * pointers are NOT allocated and cannot simply be assigned in the manner you attempt. strdup can help, but you have just allocated memory, so free it when you are done with it.
Attempting to allocate an array of structs with varying (unknown) lengths of first_name and last_name requires that you keep track of every allocation. In some sense, you are better off declaring people as pointer to pointer to Person This allows iteration over your people without having to store the population somewhere allowing you to iterate until the first NULL pointer is encountered.
Likewise, creating a typedef to your struct can greatly cut down on the number of times you write sizeof (struct Person). It keeps the code clean and helps you think though the pointer haze.
Here is an example using a pointer-to-pointer-to-struct of what I think you intended to do. It is followed below by an implementation using only a pointer to struct. Evaluate both and decide which implementation you prefer:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max);
Person **realloc_person (Person **ppl, size_t *n);
void free_person (Person *p);
void free_person_names (Person *p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person **people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (&timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0]->first_name, people[0]->last_name, people[0]->age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (&batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1]->first_name, people[1]->last_name, people[1]->age);
for (i = 0; i < population; i++)
free_person (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person ***ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
if (!((*ppl)[*pop] = malloc (sizeof ***ppl)))
return NULL;
size_t i = (*pop)++;
(*ppl)[i]-> first_name = strdup (p.first_name);
(*ppl)[i]-> last_name = strdup (p.last_name);
(*ppl)[i]-> age = p.age;
return (*ppl)[i];
}
/* realloc an array of pointers to Person setting memory to 0. */
Person **realloc_person (Person **ppl, size_t *n)
{
Person **tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free memory for a Person */
void free_person (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
free (p);
}
/* free only names of Person (for temp structs) */
void free_person_names (Person *p)
{
if (!p) return;
if (p->first_name) free (p->first_name);
if (p->last_name) free (p->last_name);
}
Note: updated to correct ppl start address on reallocation.
Using only Array of Person
While not inherently different than using a pointer to pointer to Person using a simple pointer to Person eliminates the ability to iterate over your array until a NULL or (empty) pointer is encountered. The following is an implementation of the same code using only an array of Person:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXPOP 128
typedef struct {
char *first_name;
char *last_name;
unsigned char age;
} Person;
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max);
Person *realloc_person (Person *ppl, size_t *n);
void free_person_names (Person p);
int main (void) {
size_t population = 0;
size_t maxp = MAXPOP;
size_t i = 0;
Person timn, batman;
Person *people = calloc (MAXPOP, sizeof *people);
if (!people) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
timn.first_name = strdup ("Timn");
timn.last_name = strdup ("Timothy");
timn.age = 38;
add_person (&people, timn, &population, &maxp);
free_person_names (timn);
printf("\nPerson 0\n first name: %s\n last name : %s\n age : %hhu\n",
people[0].first_name, people[0].last_name, people[0].age);
batman.first_name = strdup ("Bat");
batman.last_name = strdup ("Man");
batman.age = 42;
add_person (&people, batman, &population, &maxp);
free_person_names (batman);
printf("\nPerson 1\n first name: %s\n last name : %s\n age : %hhu\n",
people[1].first_name, people[1].last_name, people[1].age);
for (i = 0; i < population; i++)
free_person_names (people[i]);
free (people);
return 0;
}
/* add a person to an array of pointers to Person */
Person *add_person (Person **ppl, Person p, size_t *pop, size_t *max)
{
if (*pop == *max)
*ppl = realloc_person (*ppl, max);
size_t i = (*pop)++;
(*ppl)[i].first_name = strdup (p.first_name);
(*ppl)[i].last_name = strdup (p.last_name);
(*ppl)[i].age = p.age;
return ppl[i];
}
/* realloc an array Person setting memory to 0. */
Person *realloc_person (Person *ppl, size_t *n)
{
Person *tmp = realloc (ppl, 2 * *n * sizeof *ppl);
if (!tmp) {
fprintf (stderr, "Error: struct reallocation failure.\n");
// return NULL;
exit (EXIT_FAILURE);
}
ppl = tmp;
memset (ppl + *n, 0, *n * sizeof *ppl); /* memset new ptrs 0 */
*n *= 2;
return ppl;
}
/* free only names of Person (for temp structs) */
void free_person_names (Person p)
{
if (p.first_name) free (p.first_name);
if (p.last_name) free (p.last_name);
}
Output
$ ./bin/struct_add_person
Person 0
first name: Timn
last name : Timothy
age : 38
Person 1
first name: Bat
last name : Man
age : 42
One problem is the last argument of add_person() to be specific, the argument '(struct Person) p'. When 'timn' and 'batman' are passed into the add_person() function, they are passed as a copy of the original structure. In the add_person() structure, that data is actually on the stack and is volatile outside the scope of the function. Try changing the last argument to a pointer.
I'm used to PHP, but I'm starting to learn C. I'm trying to create a program that reads a file line by line and stores each line to an array.
So far I have a program that reads the file line by line, and even prints each line as it goes, but now I just need to add each line to an array.
My buddy last night was telling me a bit about it. He said I'd have to use a multidimensional array in C, so basically array[x][y]. The [y] part itself is easy, because I know the maximum amount of bytes that each line will be. However, I don't know how many lines the file will be.
I figure I can make it loop through the file and just increment an integer each time and use that, but I feel that there might be a more simple way of doing it.
Any ideas or even a hint in the right direction? I appreciate any help.
To dynamically allocate a 2D array:
char **p;
int i, dim1, dim2;
/* Allocate the first dimension, which is actually a pointer to pointer to char */
p = malloc (sizeof (char *) * dim1);
/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars
* within each of these arrays are chars
*/
for (i = 0; i < dim1; i++)
{
*(p + i) = malloc (sizeof (char) * dim2);
/* or p[i] = malloc (sizeof (char) * dim2); */
}
/* Do work */
/* Deallocate the allocated array. Start deallocation from the lowest level.
* that is in the reverse order of which we did the allocation
*/
for (i = 0; i < dim1; i++)
{
free (p[i]);
}
free (p);
Modify the above method. When you need another line to be added do *(p + i) = malloc (sizeof (char) * dim2); and update i. In this case you need to predict the max numbers of lines in the file which is indicated by the dim1 variable, for which we allocate the p array first time. This will only allocate the (sizeof (int *) * dim1) bytes, thus much better option than char p[dim1][dim2] (in c99).
There is another way i think. Allocate arrays in blocks and chain them when there is an overflow.
struct _lines {
char **line;
int n;
struct _lines *next;
} *file;
file = malloc (sizeof (struct _lines));
file->line = malloc (sizeof (char *) * LINE_MAX);
file->n = 0;
head = file;
After this the first block is ready to use. When you need to insert a line just do:
/* get line into buffer */
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1));
n++;
When n is LINE_MAX allocate another block and link it to this one.
struct _lines *temp;
temp = malloc (sizeof (struct _lines));
temp->line = malloc (sizeof (char *) * LINE_MAX);
temp->n = 0;
file->next = temp;
file = file->next;
Something like this.
When one block's n becomes 0, deallocate it, and update the current block pointer file to the previous one. You can either traverse from beginning single linked list and traverse from the start or use double links.
There's no standard resizable array type in C. You have to implement it yourself, or use a third-party library. Here's a simple bare-bones example:
typedef struct int_array
{
int *array;
size_t length;
size_t capacity;
} int_array;
void int_array_init(int_array *array)
{
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_free(int_array *array)
{
free(array->array);
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_push_back(int_array *array, int value)
{
if(array->length == array->capacity)
{
// Not enough space, reallocate. Also, watch out for overflow.
int new_capacity = array->capacity * 2;
if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX / sizeof(int))
{
int *new_array = realloc(array->array, new_capacity * sizeof(int));
if(new_array != NULL)
{
array->array = new_array;
array->capacity = new_capacity;
}
else
; // Handle out-of-memory
}
else
; // Handle overflow error
}
// Now that we have space, add the value to the array
array->array[array->length] = value;
array->length++;
}
Use it like this:
int_array a;
int_array_init(&a);
int i;
for(i = 0; i < 10; i++)
int_array_push_back(&a, i);
for(i = 0; i < a.length; i++)
printf("a[%d] = %d\n", i, a.array[i]);
int_array_free(&a);
Of course, this is only for an array of ints. Since C doesn't have templates, you'd have to either put all of this code in a macro for each different type of array (or use a different preprocessor such as GNU m4). Or, you could use a generic array container that either used void* pointers (requiring all array elements to be malloc'ed) or opaque memory blobs, which would require a cast with every element access and a memcpy for every element get/set.
In any case, it's not pretty. Two-dimensional arrays are even uglier.
Instead of an array here, you could also use a linked list, The code is simpler, but the allocation is more frequent and may suffer from fragmentation.
As long as you don't plan to do much random access (Which is O(n) here), iteration is about as simple as a regular array.
typedef struct Line Line;
struct Line{
char text[LINE_MAX];
Line *next;
};
Line *mkline()
{
Line *l = malloc(sizeof(Line));
if(!l)
error();
return l;
}
main()
{
Line *lines = mkline();
Line *lp = lines;
while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){
lp->next = mkline();
lp = lp->next;
}
lp->next = NULL;
}
If you are using C you will need to implement the resizing of the array yourself. C++ and the SDL has this done for you. It is called a vector. http://www.cplusplus.com/reference/stl/vector/
While a multidimensional array can solve this problem, a rectangular 2D array would not really be the natural C solution.
Here is a program that initially reads the file into a linked list, and then allocates a vector of pointers of the right size. Each individual character does then appear as array[line][col] but in fact each row is only as long as it needs to be. It's C99 except for <err.h>.
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct strnode {
char *s;
struct strnode *next;
} strnode;
strnode *list_head;
strnode *list_last;
strnode *read1line(void) {
char space[1024];
if(fgets(space, sizeof space, stdin) == NULL)
return NULL;
strnode *node = malloc(sizeof(strnode));
if(node && (node->s = malloc(strlen(space) + 1))) {
strcpy(node->s, space);
node->next = NULL;
if (list_head == NULL)
list_head = node;
else
list_last->next = node;
list_last = node;
return node;
}
err(1, NULL);
}
int main(int ac, char **av) {
int n;
strnode *s;
for(n = 0; (s = read1line()) != NULL; ++n)
continue;
if(n > 0) {
int i;
strnode *b;
char **a = malloc(n * sizeof(char *));
printf("There were %d lines\n", n);
for(b = list_head, i = 0; b; b = b->next, ++i)
a[i] = b->s;
printf("Near the middle is: %s", a[n / 2]);
}
return 0;
}
You can use the malloc and realloc functions to dynamically allocate and resize an array of pointers to char, and each element of the array will point to a string read from the file (where that string's storage is also allocated dynamically). For simplicity's sake we'll assume that the maximum length of each line is less than M characters (counting the newline), so we don't have to do any dynamic resizing of individual lines.
You'll need to keep track of the array size manually each time you extend it. A common technique is to double the array size each time you extend, rather than extending by a fixed size; this minimizes the number of calls to realloc, which is potentially expensive. Of course that means you'll have to keep track of two quantities; the total size of the array and the number of elements currently read.
Example:
#define INITIAL_SIZE ... // some size large enough to cover most cases
char **loadFile(FILE *stream, size_t *linesRead)
{
size_t arraySize = 0;
char **lines = NULL;
char *nextLine = NULL;
*linesRead = 0;
lines = malloc(INITIAL_SIZE * sizeof *lines);
if (!lines)
{
fprintf(stderr, "Could not allocate array\n");
return NULL;
}
arraySize = INITIAL_SIZE;
/**
* Read the next input line from the stream. We're abstracting this
* out to keep the code simple.
*/
while ((nextLine = getNextLine(stream)))
{
if (arraySize <= *linesRead)
{
char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp);
if (tmp)
{
lines = tmp;
arraySize *= 2;
}
}
lines[(*linesRead)++] = nextLine;
)
return lines;
}