C Static char* Array Question

N

NvrBst

static char* DataThatCanChange[4] = {
"Default S1",
"Default S2",
"Default S3",
"Default S4:"
};


I have the above. Sometimes the Default value is changed during the
program, and I can change it doing the following:

//myCharStar is Equal "New String"
DataThatCanChange[2] = (char*)malloc((strlen(myCharStar) + 1) * sizeof
(char));


My question is how do I clean up the old value? When I do "free
(DataThatCanChange[2]);" (before chaning) it throws an exception:
*** glibc detected *** /home/administrator/eclipse/workspace/test/
Debug/test: double free or corruption (out): 0x080497a8 ***


I'm kind of assuming that the compiler is doing something with the
strings (pooling them or something), and I'm not suppose to free them,
or compiler is freeing it automatically after I change the value?
However, I still need to clean up the 2nd value if I changed it a 3rd
time?

I'm using Ubuntu with GCC 4.3.2, and latest Eclipse. Sorry if there
is something very obvious I'm missing. Thanks in advance.



Note: I can think of two other ways to work around this.
A) Initialize the "DataThatCanChange" manually at the start of the
program with malloc.
B) Set a size limit for the Values, IE "char[32]", and just don't free
it.

I'm wondering if there is some kind of C99 Syntax that will let me
initialize the array statically (like the example), but also let me
change the char* when I need to update the value; and what is the
right way to clean-up in this case.
 
K

Keith Thompson

NvrBst said:
static char* DataThatCanChange[4] = {
"Default S1",
"Default S2",
"Default S3",
"Default S4:"
};


I have the above. Sometimes the Default value is changed during the
program, and I can change it doing the following:

//myCharStar is Equal "New String"
DataThatCanChange[2] = (char*)malloc((strlen(myCharStar) + 1) * sizeof
(char));


My question is how do I clean up the old value?

You don't.

Any time you have a string literal in your program, the compiler
creates a static array object containing the value. This object's
lifetime is the entire execution of your program, and there's no way
to get rid of it (no portable way, and very likely no non-portable
way).

Since each pointer in your DataThatCanChange array points directly to
one of these implicit static array object, there's nothing to clean
up.

A couple of suggestions:

String literals (more precisely, the static array objects that
correspond to string literals) should be treated as read-only. You
can and should enforce this by using the "const" keyword. Also, the
constant 4 in the declaration isn't necessary; the compiler will
figure it out from the initializer:

static const char *DataThatCanChange[] = {
...
};

Casting the result of malloc is unnecessary and inadvisable.
sizeof(char) is 1 by definition. So your assignment above can be
written as:

DataThatCanChange[2] = malloc(strlen(myCharStar) + 1);

followed, presumably, by:

strcpy(DataThatCanChange[2], myCharStar);
 
N

NvrBst

Since each pointer in your DataThatCanChange array points directly to
one of these implicit static array object, there's nothing to clean
up.

So to confirm, I don't have to clean up the first time I change it,
however, I need to clean up the 2nd/3rd/nth time I change it? Or I
never have to clean up no matter how much I change it with malloc?

Reason I ask is the code becomes more confusing adding logic to know
if the data has been changed before; would need a dirty flag stored.
So just want to be 100% sure.


I'll change the casting of malloc :) I used to have the "const" in
"static const char* DataThatCanChange[] = { ", but eclipse gives me a
lot of warning at the "strcpy(DataThatCanChange[2], myCharStar);";
line

Eclipse Warning: passing argument 1 of strcpy discards qualifiers from
pointer target type.


I changed it to the "static char* DataThatCanChange[] = {" to fix
these warning. I wasn't able to fix them with casting, IE "strcpy
((const char*)DataThatCanChange[2], myCharStar);".

Should I just ignore the warnings? Stick with "char*", or is there a
way to use strcpy with a "const char*" without a warning?
 
N

NvrBst

Also forgot to say thanks very much for your time and help :) Didn't
mention it in the last reply -_-
 
K

Keith Thompson

Please don't snip attribution lines. (I wrote the above.)
So to confirm, I don't have to clean up the first time I change it,
however, I need to clean up the 2nd/3rd/nth time I change it? Or I
never have to clean up no matter how much I change it with malloc?

Reason I ask is the code becomes more confusing adding logic to know
if the data has been changed before; would need a dirty flag stored.
So just want to be 100% sure.


I'll change the casting of malloc :) I used to have the "const" in
"static const char* DataThatCanChange[] = { ", but eclipse gives me a
lot of warning at the "strcpy(DataThatCanChange[2], myCharStar);";
line

Eclipse Warning: passing argument 1 of strcpy discards qualifiers from
pointer target type.

Right. You didn't have a strcpy call in your original article.

Your initializer causes each DataThatCanChange to point to a region
of memory that you're not allowed to modify. For historical reasons,
string literals are not const, *but* any attempt to modify them
invokes undefined behavior. (Most modern implementations will
probably trap any attempt to modify a string literal, but in some
cases it might just be quietly allowed, with possibly unpredictable
consequences.)

By using strcpy(), you're copying a sequence of characters into that
unmodifiable region of memory. With the const, you get a warning;
without it, it's just as bad but the compiler doesn't warn you about
it. So leave the const in place and find some other way to correct
the problem that the warning is telling you about.

Note that, even if the memory were modifiable, using strcpy would
still be risky, because you couldn't easily guarantee that there's
enough room for the string you're copying into it. So your safest bet
is always to assign the pointer to point to a new string, not to
attempt to modify the existing one. Using "const" helps you enforce
this.
I changed it to the "static char* DataThatCanChange[] = {" to fix
these warning. I wasn't able to fix them with casting, IE "strcpy
((const char*)DataThatCanChange[2], myCharStar);".

Should I just ignore the warnings? Stick with "char*", or is there a
way to use strcpy with a "const char*" without a warning?

No, you absolutely should not ignore the warnings.

Let's step back a bit. You have an array of several pointers to
strings. (Terminology: a "pointer to a string" is a pointer to the
first character of the string.) In order to manage your storage, you
need to know, at any given moment, how the string was created. If
it's a string literal, as it is initially, you don't need to
deallocate it. If it was allocated with malloc (or calloc, or
realloc), then you need to free it when you're done with it.

One approach is to keep extra information, a flag for each pointer
that tells you how it was allocated. (There's no good way to get
this information just from the pointer itself.)

Another is to make sure that the strings are always allocated the same
way. Rather than initializing your strings to point to string
literals, use malloc from the start. For example:

char *dupstr(const char *s) {
char *result = malloc(strlen(s) + 1);
if (result != NULL) {
strcpy(result, s);
}
return result;
};

const char* DataThatCanChange[] = {
dupstr("Default S1"),
dupstr("Default S2"),
dupstr("Default S3"),
dupstr("Default S4:")
};

(The dupstr() function is intended to be a copy of the non-standard
strdup() function.)

If you want to change one of the strings:

free(DataThatCanChange[2]);
DataThatCanChange[2] = dupstr("Some new value");

You'll have to tweak this code to make it work. In particular, you
can't have function calls in the initializer for a static object. You
can put the initialization code into a separate function and call it
once to set things up.
 
G

Guest

static char* DataThatCanChange[4] = {
   "Default S1",
   "Default S2",
   "Default S3",
   "Default S4:"

};

I have the above.  Sometimes the Default value is changed during the
program, and I can change it doing the following:

//myCharStar is Equal "New String"
DataThatCanChange[2] = (char*)malloc((strlen(myCharStar) + 1) * sizeof
(char));

the cat is unnecessary and may hide an error. sizeof(char) is always 1
so some people omit it.

DataThatCanChange[2] = malloc(strlen(myCharStar) + 1);

I might wrap this in a function

void *change_data (int i, const char *new_data)
{
DataThatCanChange = malloc (strlen(new_data) + 1);
if (DataThatCanChange != NULL)
strcpy (DataThatCanChange, new_data);
return DataThatCanChange;
}

then have a default array

static char* defaultdata[] = {
"Default S1",
"Default S2",
"Default S3",
"Default S4:"
};

then at startup initialise your datathatcanchange

for (i = 0; i < ARRAY_SIZE(DataThatCanChange); i++)
change_data (i, defaultdata);

oops. now I need a second function to do the cleanup.

void *clean_change_data (int i, const char *new_data)
{
free (DataThatCanChange);
change_data (i, new_data);
}

this could be done rather cleaner- but I think thats the basic idea.


<snip>
 

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

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top