Noob question, how to malloc/free for char** ?

R

Rob Schieber

Hey guys,

If I have a pointer to a *char, how do I allocate and free memory for it?

for example, if I had:

char* pstring = "Hello\0";
char** strings = &pstring;
strings[0] = pstring;

1. Is this a valid way to set up a list of strings?
and
2. How do I malloc/free the strings variable? Or is it even necessary
since I'm initializing the pointer array to the address of the first string?

Thanks in Advance
 
R

Rob Schieber

Tom said:
If I were you I'd use a vector to store the strings or if you are using MFC
you can use CStringList or CStringArray and use CStrings. If you want to do
your own dynamic memory allocation I'd use 'new' and 'delete' instead of
malloc/free:

Thanks Tom,

I wish I could use a vector, but the problem is that I need to set the
value of a struct which is of type char** . I guess the more general
question is how do you allocate/free pointer to pointer values.
 
W

William DePalo [MVP VC++]

Rob Schieber said:
Hey guys,

If I have a pointer to a *char, how do I allocate and free memory for it?

for example, if I had:

char* pstring = "Hello\0";
char** strings = &pstring;
strings[0] = pstring;

1. Is this a valid way to set up a list of strings?

Well, if you can not use a standard container, then the first question you
need to ask yourself is whether your strings have a fixed maximum size. If
they do then the next question to ask is whether you can afford creating a
maximally sized array. If the answer to both questions is yes then you can
compute the product of the number of elements and their size (don't forget
the terminator) and then malloc the whole lot in one go.

The first string starts at offset 0, the second at offset n, the third at
offset 2n where n = the size of the buffer holding each string (not the
string length, btw).

If the answer to either question is false, then it seems to me that you
would be better off first allocating an array of pointers (LPCSTR or LPSTRs
as appropriate). Then you allocate each string as you would normally and set
the corresponding element in the array of pointers to the first byte of each
dynamically allocated string.

Regards,
Will
 
T

Tamas Demjen

Rob said:
I wish I could use a vector, but the problem is that I need to set the
value of a struct which is of type char** . I guess the more general
question is how do you allocate/free pointer to pointer values.

Even then I'd use std::string and pass .c_str() to the C call:

vector<string> strings;
strings.push_back("One");
strings.push_back("Two");
strings.push_back("Three");
// Now convert it into C-style:
vector<const char*> c_strings;
for(vector<string>::const_iterator it = strings.begin();
it != strings.end(); ++it)
{
c_strings.push_back(it->c_str());
}
CallThatOldCFunction(&c_strings[0]);

This way you're safe and don't have to allocate or free anything. It's
clear, exception safe, and as bug-free as possible (you have no control
over what the C call is doing).

You can replace vector with deque if you prefer.

If your C call is so badly designed that it doesn't support const
pointers, you need to use a trick in the for loop: Instead of s.c_str(),
which requires const-correctness, use &s[0], like this:

// Now convert it into C-style:
vector<char*> c_strings;
for(vector<string>::const_iterator it = strings.begin();
it != strings.end(); ++it)
{
c_strings.push_back(&(*it)[0]);
}
CallThatOldCFunction(&c_strings[0]);

Tom
 
R

Rob Schieber

William said:
Hey guys,

If I have a pointer to a *char, how do I allocate and free memory for it?

for example, if I had:

char* pstring = "Hello\0";
char** strings = &pstring;
strings[0] = pstring;

1. Is this a valid way to set up a list of strings?


Well, if you can not use a standard container, then the first question you
need to ask yourself is whether your strings have a fixed maximum size. If
they do then the next question to ask is whether you can afford creating a
maximally sized array. If the answer to both questions is yes then you can
compute the product of the number of elements and their size (don't forget
the terminator) and then malloc the whole lot in one go.

The first string starts at offset 0, the second at offset n, the third at
offset 2n where n = the size of the buffer holding each string (not the
string length, btw).

If the answer to either question is false, then it seems to me that you
would be better off first allocating an array of pointers (LPCSTR or LPSTRs
as appropriate). Then you allocate each string as you would normally and set
the corresponding element in the array of pointers to the first byte of each
dynamically allocated string.

Regards,
Will

Thanks for the reply Will,

So I guess if I knew my list was going to be 2 items in length, and each
item was a maximum of 50 chars, I would want to do something like this?:

char* pstring1 = "pstring1\0";
char* pstring2 = "pstring2\0";

char** strings = malloc(2 * 50);
strings[0] = string1;
strings[50] = string2;

free(strings);
 
R

Rob Schieber

Tamas said:
Rob said:
I wish I could use a vector, but the problem is that I need to set the
value of a struct which is of type char** . I guess the more general
question is how do you allocate/free pointer to pointer values.


Even then I'd use std::string and pass .c_str() to the C call:

vector<string> strings;
strings.push_back("One");
strings.push_back("Two");
strings.push_back("Three");
// Now convert it into C-style:
vector<const char*> c_strings;
for(vector<string>::const_iterator it = strings.begin();
it != strings.end(); ++it)
{
c_strings.push_back(it->c_str());
}
CallThatOldCFunction(&c_strings[0]);

This way you're safe and don't have to allocate or free anything. It's
clear, exception safe, and as bug-free as possible (you have no control
over what the C call is doing).

You can replace vector with deque if you prefer.

If your C call is so badly designed that it doesn't support const
pointers, you need to use a trick in the for loop: Instead of s.c_str(),
which requires const-correctness, use &s[0], like this:

// Now convert it into C-style:
vector<char*> c_strings;
for(vector<string>::const_iterator it = strings.begin();
it != strings.end(); ++it)
{
c_strings.push_back(&(*it)[0]);
}
CallThatOldCFunction(&c_strings[0]);

Tom

Thanks again for the help, I will try both suggestions, hopefully I can
get it to work :p
 
T

Tamas Demjen

Rob said:
char* pstring1 = "pstring1\0";
char* pstring2 = "pstring2\0";

char** strings = malloc(2 * 50);
strings[0] = string1;
strings[50] = string2;

free(strings);

No.

char** strings = static_cast<char**>(malloc(2 * sizeof(char*));
strings[0] = string1;
strings[1] = string2;
free(strings);

Remember, strings stores char* pointers, not chars.

Tom
 
W

William DePalo [MVP VC++]

Rob Schieber said:
Thanks for the reply Will,

You are welcome.
So I guess if I knew my list was going to be 2 items in length, and each
item was a maximum of 50 chars, I would want to do something like this?:

char* pstring1 = "pstring1\0";
char* pstring2 = "pstring2\0";

char** strings = malloc(2 * 50);
strings[0] = string1;
strings[50] = string2;

free(strings);

Well, no. :)

The first two lines are OK. You are declaring two pointers to strings and
setting each to point to fixed strings which the compiler stores in the data
segment. So far, so good.

The next few lines exhibit some confusion.

On the left side of this declaration
char** strings = malloc(2 * 50);

you have a pointer to a pointer to a character. Then you allocate 100 bytes
for it. Ut oh.

Since the variable strings holds a pointer to a pointer, strings[n] holds a
pointer, so these lines
strings[0] = string1;
strings[50] = string2;

are copying _pointers_ even though I _think_ that you intended for them to
copy _strings_.

To the end of this post I have appended tired old C source (definitely not
modern C++) that demonstrates handling an array of strings in three ways. In
the first I statically allocate the array. In the second I dynamically
allocate an array of fixed size strings. In the third I allocate an array of
variably sized strings.

In all cases, the size of the array is known at compile time. It gets even
uglier if the upper bound for the array is not known until runtime.

Note that I AM NOT advocating that you proceed down this path. I'd no sooner
suggest that than I'd suggest using an antique operating system whose name
ends in X. <gd&r> But you asked.

Finally note that buffer overruns are all to easy where null-terminated
strings are concerned. And exploiting this vulnerability is a favorite
technique of virus writers. If you must use this old stuff you may want to
read up on the safer variants of the old string functions here:

http://msdn2.microsoft.com/en-us/library/td1esda9

Regards,
Will

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>

#define SIZE 50
#define COUNT 10

int main()
{
unsigned int i, j;

// static allocation

{
char strings1[COUNT][SIZE + 1] = { "zero", "one", "two", "three", "four",
"five", "six", "seven", "eight",
"nine" };
printf("\nStatic Allocation\n\n");

for ( i = 0; i < COUNT; i++ )
{
printf( "String #%d is '%s', characters are ", i, strings1 );

for ( j = 0; j < strlen( strings1 ); j++ )
printf("'%c' ", strings1[j] );

printf("\n\n");
}
}

// dynamic allocation of fixed-size strings

{
char (*strings2)[COUNT][SIZE + 1];

printf("\nDynamic Allocation - Fixed Size\n\n");

strings2 = calloc(COUNT, SIZE + 1);

strcpy( (*strings2)[0], "zero");
strcpy( (*strings2)[1], "one");
strcpy( (*strings2)[2], "two");
strcpy( (*strings2)[3], "three");
strcpy( (*strings2)[4], "four");
strcpy( (*strings2)[5], "five");
strcpy( (*strings2)[6], "six");
strcpy( (*strings2)[7], "seven");
strcpy( (*strings2)[8], "eight");
strcpy( (*strings2)[9], "nine");

for ( i = 0; i < COUNT; i++ )
{
printf( "String #%d is '%s', characters are ", i, (*strings2) );

for ( j = 0; j < strlen( (*strings2) ); j++ )
printf("'%c' ", (*strings2)[j] );

printf("\n\n");
}
}

// dynamic allocation of fixed sized strings

{
char *strings3[COUNT];

printf("\nDynamic Allocation - Variable Size\n\n");

strings3[0] = malloc(strlen("zero") + 1);
strcpy(strings3[0], "zero");

strings3[1] = malloc(strlen("one") + 1);
strcpy(strings3[1], "one");

strings3[2] = malloc(strlen("two") + 1);
strcpy(strings3[2], "two");

strings3[3] = malloc(strlen("three") + 1);
strcpy(strings3[3], "three");

strings3[4] = malloc(strlen("four") + 1);
strcpy(strings3[4], "four");

strings3[5] = malloc(strlen("five") + 1);
strcpy(strings3[5], "five");

strings3[6] = malloc(strlen("six") + 1);
strcpy(strings3[6], "six");

strings3[7] = malloc(strlen("seven") + 1);
strcpy(strings3[7], "seven");

strings3[8] = malloc(strlen("eight") + 1);
strcpy(strings3[8], "eight");

strings3[9] = malloc(strlen("nine") + 1);
strcpy(strings3[9], "nine");

for ( i = 0; i < COUNT; i++ )
{
printf( "String #%d is '%s', characters are ", i, strings3 );

for ( j = 0; j < strlen( strings3 ); j++ )
printf("'%c' ", strings3[j] );

printf("\n\n");
}
}

return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top