Reputation: 29
it seems like i did not really understand how the memory allocation with pointers works.
Quick example:
I got a structure,
struct Friends{
char *firstname;
char *lastname;
};
if I do allocate memory now, it will get me
2x sizeof(char) = 2x 1Byte
but, doesnt the free memory I need depend on how many characters I fill it with?
example:
char array[10] needs 10x sizeof(char), 1byte for each character?
Everywhere I look they allocate the memory, before they know how much they will fill the structure with.
Upvotes: 2
Views: 5256
Reputation: 123468
So with types like yours above, you have basically a two-step allocation process:
First, you allocate an object of type struct Friends
, which contains space for two pointers;
Second, you allocate memory for the objects that each member of struct Friends
will point to.
Quick and dirty example:
struct Friends *addFriend( const char *firstName, const char *lastName )
{
/**
* First, allocate an instance of `struct Friends`, which will contain
* enough space to store two pointers to `char`
*/
struct Friends *f = malloc( sizeof *f ); // sizeof *f == sizeof (struct Friends)
if ( f )
{
/**
* Allocate space to store a *copy* of the contents of the firstName
* parameter, assign the resulting pointer to the firstname member of
* the struct instance.
*/
f->firstname = malloc( strlen( firstName ) + 1 );
if ( f->firstname )
strcpy( f->firstname, firstName );
/**
* Do the same for lastName
*/
f->lastName = malloc( strlen( lastName ) + 1 );
if ( f->lastname )
strcpy( f->lastname, lastName );
}
return f;
}
If we call this function as
struct Friends newFriend = addFriend( "John", "Bode" );
we get something like the following in memory:
+---+ +---+ +---+---+---+---+---+
newFriend:| | --> newFriend->firstname: | | ---->|'J'|'o'|'h'|'n'| 0 |
+---+ +---+ +---+---+---+---+---+
newFriend->lastname: | | -+
+---+ | +---+---+---+---+---+
+-->|'B'|'o'|'d'|'e'| 0 |
+---+---+---+---+---+
Here's how it plays out on my system:
Item Address 00 01 02 03
---- ------- -- -- -- --
newFriend 0x7fffe6910368 10 20 50 00 ..P.
0x7fffe691036c 00 00 00 00 ....
*newFriend 0x502010 30 20 50 00 0.P.
0x502014 00 00 00 00 ....
0x502018 50 20 50 00 P.P.
0x50201c 00 00 00 00 ....
newFriend->firstname 0x502030 4a 6f 68 6e John
newFriend->lastname 0x502050 42 6f 64 65 Bode
The newFriend
pointer variable lives at address 0x007fffe6910368
. It points to an object of type struct Friends
which lives at address 0x502010
. This object is large enough to store two pointer values; newFriend->firstname
lives at address 0x502010
1 and points to the string "John"
, which lives at address 0x502030
. newFriend->lastname
lives at address 0x502018
and points to the string "Bode"
, which lives at address 0x502050
.
To deallocate, you free the members before freeing the struct object:
void deleteFriend( struct Friends *f )
{
free( f->firstname );
free( f->lastname );
free( f );
}
It doesn't matter what order you free f->firstname
and f->lastname
with respect to each other; what matters is that you have to delete both of them before you can delete f
. Freeing f
won't free what f->firstname
and f->lastname
point to2.
Note that this all assumes I'm being given data with a known size (the firstName
and lastName
parameters in addFriend
); I use the lengths of those input strings to determine how much space I need to allocate.
Sometimes you do not know ahead of time how much space you need to set aside. The usual approach is to allocate some initial amount of storage and to extend it as necessary using the realloc
function. For example, suppose we have code to read a single line of text terminated by a newline character from an input stream. We want to be able to handle lines of any length, so we start out by allocating enough space to handle most cases, and if we need more, we double the size of the buffer as necessary:
size_t lineSize = 80; // enough for most cases
char *line = calloc( lineSize, sizeof *line );
char buffer[20]; // input buffer for reading from stream
/**
* Keep reading until end of file or error.
*/
while ( fgets( buffer, sizeof buffer, stream ) != NULL )
{
if ( strlen( line ) + strlen( buffer ) >= lineSize )
{
/**
* There isn't enough room to store the new string in the output line,
* so we double the output line's size.
*/
char *tmp = realloc( line, 2 * lineSize );
if ( tmp )
{
line = tmp;
lineSize *= 2;
}
else
{
/**
* realloc call failed, handle as appropriate. For this
* example, we break out of the loop immediately
*/
fprintf( stderr, "realloc error, breaking out of loop\n" );
break;
}
}
strcat( line, buffer );
/**
* If we see a newline in the last input operation, break out of the
* loop.
*/
if ( strchr( buffer, '\n' ) )
break;
}
struct
is the same as the address of the whole struct
object; C doesn't store any kind of metadata before the first struct
member. free
on an object that was allocated with malloc
, calloc
, or realloc
; you would not call free
on a pointer that points to a string literal or another array.
Upvotes: 1
Reputation: 84561
Everywhere I look they allocate the memory, before they know how much they will fill the structure with.
Yes and no.
struct Friends {
char *firstname;
char *lastname;
};
It all depends on how you intend to use your structure. There are multiple ways you can use struct Friends
. You can declare a static instance of the struct, and then simply assign the address of existing strings to your firstname
and lastname
member pointers, e.g.:
int main (void) {
Struct Friends friend = {{Null}, {NULL]};
/* simple assignment of pointer address
* (memory at address must remain valid/unchanged)
*/
friend.firstname = argc > 1 ? argv[1] : "John";
friend.lastname = argc > 2 ? argv[2] : "Smith";
printf ("\n name: %s %s\n\n", friend.firstname, friend.lastname);
However, in most cases, you will want to create a copy of the information and store a copy of the string for the firstname
and lastname
members. In that case you need to allocate a new block of memory for each pointer and assign the starting address for each new block to each pointer. Here you now know what your strings are, and you simply need to allocate memory for the length of each string (+1
for the nul-terminating character) e.g.:
int main (int argc, char **argv) {
/* declare static instance of struct */
struct Friends friend = {NULL, NULL};
char *first = argc > 1 ? argv[1] : "John";
char *last = argc > 2 ? argv[2] : "Smith";
/* determine the length of each string */
size_t len_first = strlen (first);
size_t len_last = strlen (last);
/* allocate memory for each pointer in 'friend' */
friend.firstname = malloc (len_first * sizeof *friend.firstname + 1);
friend.lastname = malloc (len_last * sizeof *friend.lastname + 1);
You then need only copy each string to the address for each member:
/* copy names to new memory referenced by each pointer */
strcpy (friend.firstname, first);
strcpy (friend.lastname, last);
Finally, once you are done using the memory allocated, you must free the memory with free
. note: you can only free
memory that you have previously allocated with malloc
or calloc
. Never blindly attempt to free memory that has not been allocated that way. To free the members, all you need is:
/* free allocated memory */
free (friend.firstname);
free (friend.lastname);
A short example putting all the pieces together is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Friends {
char *firstname;
char *lastname;
};
int main (int argc, char **argv) {
/* declare static instance of struct */
struct Friends friend = {NULL, NULL};
char *first = argc > 1 ? argv[1] : "John";
char *last = argc > 2 ? argv[2] : "Smith";
/* determine the length of each string */
size_t len_first = strlen (first);
size_t len_last = strlen (last);
/* allocate memory for each pointer in 'friend' */
friend.firstname = malloc (len_first * sizeof *friend.firstname + 1);
friend.lastname = malloc (len_last * sizeof *friend.lastname + 1);
/* copy names to new memory referenced by each pointer */
strcpy (friend.firstname, first);
strcpy (friend.lastname, last);
printf ("\n name: %s %s\n\n", friend.firstname, friend.lastname);
/* free allocated memory */
free (friend.firstname);
free (friend.lastname);
return 0;
}
Always compile with warnings enabled, for example:
gcc -Wall -Wextra -o bin/struct_dyn_alloc struct_dyn_alloc.c
(if not using gcc
, then your compiler will have similar options)
Anytime you dynamically allocate memory in your code, run in though a memory error checking program to insure you are not misusing the allocated blocks of memory in some way, and to confirm all memory has been freed. It is simple to do. All OS's have some type of checker. valgrind
is the normal choice on Linux. For example:
$ valgrind ./bin/struct_dyn_alloc
==14805== Memcheck, a memory error detector
==14805== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==14805== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==14805== Command: ./bin/struct_dyn_alloc
==14805==
name: John Smith
==14805==
==14805== HEAP SUMMARY:
==14805== in use at exit: 0 bytes in 0 blocks
==14805== total heap usage: 2 allocs, 2 frees, 11 bytes allocated
==14805==
==14805== All heap blocks were freed -- no leaks are possible
==14805==
==14805== For counts of detected and suppressed errors, rerun with: -v
==14805== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Upvotes: 2
Reputation: 1484
Everywhere I look they allocate the memory, before they know how much they will fill the structure with.
If I understood properly you are asking how we can allocate memory before they know how much space we gonna use.
Consider the following structure:
struct foo
{
char bar;
char foobar;
};
struct foo *temp = (struct foo *) malloc(sizeof(struct foo));
Here 2 * 1 = 2 bytes
will be allocated dynamically whether you store something in bar
and foobar
variables or not.
If you don't store anything then the variable (memory location) contains grabage value.
Upvotes: 0
Reputation: 11406
I got a structure,
struct Friends{ char *firstname; char *lastname; };
if I do allocate memory now, it will get me
2x sizeof(char) = 2x 1Byte
Instead of char
's, you are allocating pointers here sizeof(char *)
. Depending on the system, those are 4 bytes (32-bit) or 8 bytes (64-bit) each.
So, if you want data where your pointers point to, you will have to allocate that, with malloc()
for example, and free()
them later.
Or do something like:
struct Friends{
char firstname[20];
char lastname[20];
};
But make sure your strings end with a \0 char
.
Upvotes: 3
Reputation: 134336
If i understood your problem correctly, you want to see the memory allocated for firstname
and lastname
based on your requirement of the future storage.
I'm afraid, that is not possible.
When you got a variable of type struct Friends
, say struct Friends F;
, F.firstname
and F.lastname
will be pointers but they will not be pointing to any valid memory. You need to perform the allocation for F.firstname
and F.lastname
separately. Something like
F.firstname = malloc(32);
F.lastname = malloc(32); //perform NULL check too
and then you can actually make use of F.firstname
and F.lastname
Upvotes: 0