Memory corruption on freeing a pointer to pointer

B

Ben Bacarisse

(e-mail address removed) writes:
char **a;
int numberOfPointers = 5;


a = (char **) malloc ( numberOfPointers * ( sizeof(char * ) ) );
if ( a!= NULL )
printf( "the array, a, points to declare space\n\n" );

char *b[] = { "one", "two", "three", "four", "five" };

a = b;
It's possible that you don't know what this assignment does. It only
replaces one pointer with another. The pointer value that used to be in
'a' is lost so you can no longer access the memory that was allocated.
in particular, you can't ever free it now.
<snip>

Ben, so a=b; , does not assign a to point to whatever b points to?

Yes, it does exactly that: after the assignment a and b point to the
same place. If that's what you wanted, you got it right, but it looks
like a very odd thing to do. Why allocate space will malloc, only to
loose the pointer forever a couple of lines further down?
it replaces?

It replaces one pointer (the pointer to the malloc'd data) that is in
'a' with another (a pointer to the first element of the array 'b').

I suspect the problem here is English not C.
I thought it was having a point to the same memory location.
How would you do that? How would you have a point to b? what is the
syntax?

Exactly as you have written it. Had I know that's what you wanted to
do, I'd have made a different remark: "why allocate that storage and
then overwrite the poimter to it without every using it?".
 
B

Barry Schwarz

Consider a concrete example as annotated in your code:

char **a;

The variable a is defined to exist at location 0x1000 and is 4 bytes
wide. Its value is indeterminate.
int numberOfPointers = 5;

The variable numberOfPointers is defined to exist at location 0x1004
and it is initialized with the value 5. On a big-endian machine, its
four bytes would contain 0x00, 0x00, 0x00, 0x05. On a little endian
machine, the bytes would contain 0x05, 0x00, 0x00, 0x00 (reading from
0x1004 to 0x1007 in both cases).
a = (char **) malloc ( numberOfPointers * ( sizeof(char * ) ) );
if ( a!= NULL )

The value of a is set to 0x2000. This is the address of a 20 byte
area that has been allocated.
printf( "the array, a, points to declare space\n\n" );

char *b[] = { "one", "two", "three", "four", "five" };

The variable b is defined to exist at location 0x1008. It is 20 bytes
wide. Each set of 4 bytes contains an address. 0x1008 contains the
value 0x3000 which is where the string literal "one" is located.
0x100C contains 0x3004 which is where the literal "two" is located.
0x1010 contains 0x3008 where "three" is located. 0x1014 contains
0x300E where "four" is located. And 0x1018 contains 0x3013 where
"five" is located. If you looked at 0x3000 in memory, you would see
the characters
one\0two\0three\0four\0five\0
in sequence.

The value in a is changed from 0x2000 to 0x1008. Notice that the
value given to a did not come from any data located in b. Rather it
is the address of b. This is the result of a special rule regarding
arrays that says an expression with type array of elements is
converted to the address of the first element. (Their are exceptions
to the rule but they don't apply to this discussion.) If b had been a
pointer instead of an array, the value of b would have been copied to
a. But since b is an array, any values of its elements are irrelevant
for this statement and the address of b[0] is what is stored in a.
This would be true even if b had not been initialized. The end result
is that a now points to the first element of b.

At the same time, the previous value of a (0x2000) has been
irretrievably lost. You can no longer access the allocated memory for
any purpose, including an effort to free it.
Ben, so a=b; , does not assign a to point to whatever b points to?
it replaces?

b is an array, not a pointer. So it is somewhat misleading to say b
points to something. The only thing being replaced is the previous
value of a. So the only question is what replaces this value. By
virtue of the rule described above, the value in a is replaced by the
address of the first element of b.
I thought it was having a point to the same memory location.

a now does point to the memory occupied by b, or to the first byte of
that memory.
How would you do that? How would you have a point to b? what is the syntax?

a can point to the first element of b because it has the correct type.
b is an array of pointers to char. Therefore b[0] is a char*.
Therefore &b[0] has type char** which matches the type of a.

a cannot point to b as a whole (even though the address of b and the
address of b[0] are "numerically" the same, that is both b and b[0]
start at the exact same byte in memory) because it has the wrong type.
The address of b (which can be expressed in source code as &b) has
type pointer to array of 5 pointers to char. To define a pointer p
that can contain such a value, you would code it as
char *(*p)[5];
and then you could code
p = &b;

You read the declaration starting from the innermost parentheses and
working your way out:
(*p) - p is a pointer
[5] - to an array of 5
char * - pointers to char
 
G

gdotone

(e-mail address removed) writes:
char **a;
int numberOfPointers = 5;
a = (char **) malloc ( numberOfPointers * ( sizeof(char * ) ) );
if ( a!= NULL )
printf( "the array, a, points to declare space\n\n" );
char *b[] = { "one", "two", "three", "four", "five" };
a = b;
It's possible that you don't know what this assignment does. It only
replaces one pointer with another. The pointer value that used to be in
'a' is lost so you can no longer access the memory that was allocated.
in particular, you can't ever free it now.
<snip>
Ben, so a=b; , does not assign a to point to whatever b points to?

Yes, it does exactly that: after the assignment a and b point to the
same place. If that's what you wanted, you got it right, but it looks
like a very odd thing to do. Why allocate space will malloc, only to
loose the pointer forever a couple of lines further down?
it replaces?
It replaces one pointer (the pointer to the malloc'd data) that is in
'a' with another (a pointer to the first element of the array 'b').
I suspect the problem here is English not C.

ok, i see, yes, because b nor a at that have stopped existing.
Exactly as you have written it. Had I know that's what you wanted to
do, I'd have made a different remark: "why allocate that storage and
then overwrite the poimter to it without every using it?".

well, actually i was trying to understand Sharwan's code snippet.
so i wrote a program consisting of small steps to see what was happening
and then i thought about his freeing of the pointer and perhaps that introduced the
problems he was having. so that's why that code is so piece meal. at one time
i had a lot of comments throughout but i removed them so it could be more
readable.

the great thing about posting that code was i got great feedback. i'm glad you and
everyone else took the time to teach me. thanks.

pointers are truly interesting. i'm learning some great software engineering points.
keith, james, yourself included seem to be true masters of C so thanks again.
 
T

Tim Rentsch

Keith Thompson said:
I'm sure part of it is that I learned Pascal before I learned C.
But when I see "if (ptr)", I have to mentally translate it to
"if (ptr != NULL)".

I find this interesting and also rather astonishing.
(Not unbelievable, just astonishing.)

To offer a point of comparison, I also learned Pascal
before I learned C, but immediately took to the C-isms
'if(ptr)' and 'if(!ptr)' without any difficulty. When
I see something like 'if (ptr != NULL)' it almost always
looks awkward or somewhat contrived. Not meaning to
imply anyone else should have this reaction, just that it
is the reaction I have myself.
 
I

Ike Naar

I know why Yoda conditions are used, and it's a valid reason.
But would you even consider writing

if (0 == strcmp(s1, s2))

if it weren't for the "==" vs. "=" issue?

There is no reason to write it that way for the "==" vs. "=" issue.

if (strcmp(s1, s2) == 0)

is already safe, because

if (strcmp(s1, s2) = 0) /* oops, mistyped "==" */

is not valid C.
 
I

Ike Naar

[...] the only argument ever presented for Yoda Conditions is
protection against the possibility of mistyping = instead of ==. Such
protection is only needed when one of the operands is an lvalue; such
protection is only possible if the other is not an lvalue. It is neither
needed nor possible in this case.

The Wikipedia page, mentioned in Keith's post that started this
discussion about Yoda conditions, presents two arguments
(although the second argument applies to Java code and not to C).

The Wikipedia article (as I understand it) defines a Yoda condition
as a condition in which the left operand is a constant and the
right operand is a variable; lvalueness is not mentioned - at
least not explicitly.
The relevant property is lvalueness.
If you're writing code so obfuscated that it leaves you uncertain which
expressions are lvalues, you've got bigger problems than can be dealt
with by using Yoda conditions.
Nothing that occurs at runtime can change whether or not a given
expression is an lvalue.

All agreed, if the relevant property is lvalueness instead of constness.
Although I'd expect that "lvalue" should actually be "modifiable lvalue".

Let me see if I understand your definition of Yoda condition correctly:

A Yoda condition (X==Y) is a condition that is protected against
accidental assignment, while the reverse condition (Y==X) is unprotected.
That means:
- X is an expression that is not a modifiable lvalue;
so that (X=Y) violates 6.5.16, constraint 2.
- Y is a modifiable lvalue;
so that (Y=X) is an accidental but valid assignment.

Examples:

int var;
int const con;
char *s1, *s2;

(42 == var); /* Yoda condition */
(con == var); /* Yoda condition */
(42 == var+1); /* not Yoda condition */
(42 == con); /* not Yoda condition */
(0 == strcmp(s1, s2)); /* not Yoda condition */

Right?
 
J

James Kuyper

On 08/27/2013 03:27 AM, David Brown wrote:
....
have to like it, but they do have live with it - just as people who
write /proper/ English have to accept that the developers of the C
language can't spell "maths" properly.

K&R C was developed in the US, and first standardized by ANSI, a US
standards organization. Oddly enough, we use US English here, a dialect
in which there is no word which is correctly spelled "maths", but there
is one that's correctly spelled "math".

If the English had not wanted to lose control of the English language,
they shouldn't have conquered and colonized so many countries and forced
them to use that language. The English now constitute a minority among
native speakers of English. There's many more native speakers of US
English alone than there are of all of the British varieties of English.
Get over it.
 
J

James Kuyper

On 08/27/2013 04:24 AM, Ike Naar wrote:
....
Let me see if I understand your definition of Yoda condition correctly:

I'd never heard of that term before this thread started, so I really
have no definition for it. However, I know of a closely related concept
that applies to every example I've seen of a Yoda condition.
A Yoda condition (X==Y) is a condition that is protected against
accidental assignment, while the reverse condition (Y==X) is unprotected.
That means:
- X is an expression that is not a modifiable lvalue;
so that (X=Y) violates 6.5.16, constraint 2.
- Y is a modifiable lvalue;
so that (Y=X) is an accidental but valid assignment.

Examples:

int var;
int const con;
char *s1, *s2;

(42 == var); /* Yoda condition */
(con == var); /* Yoda condition */
(42 == var+1); /* not Yoda condition */
(42 == con); /* not Yoda condition */
(0 == strcmp(s1, s2)); /* not Yoda condition */

Right?

All of the above correctly describes the concept that I'm thinking of.
The use of the phrase "modifiable lvalue" marks this as specific to C,
C++ and related languages. More generally, a Yoda condition is the use
X==Y rather than Y==X, despite the equivalence of those expressions, to
protect against the typo == > =, because X=Y would be invalid, but Y=X
would not be. The details of what makes X=Y invalid could vary from one
language to another, and of course the concept is meaningless for
languages which don't have both = and ==.

If the people who actually use the term "Yoda condition" want to use it
in a more restricted sense, such as the one you mentioned from
Wikipedia, I'm certainly in no position to stop them; but I would
recommend adopting this definition.
 
P

Phil Carmody

Keith Thompson said:
James Kuyper said:
On 08/24/2013 03:05 PM, Sharwan Joram wrote: [...]
if ( NULL == parameters[parametercount]){

This is what's known as a "Yoda conndition"
<http://en.wikipedia.org/wiki/Yoda_Conditions>. I know that a lot of
programmers like them, and for somewhat valid reasons, but personally I
find them jarring and unnecessary.

They are a shibolleth for me. If I see them, I am 99% sure the code
was written by a fuckwit or a newb, and I should be very very careful
about everything else from the same source. As most of the time I'm
looking at Linux kernel patches, the signs that seem to go hand in
hand with this yoda code are a complete inability to follow the
correct coding style, (such as use of butt-ugly Hungarian notation,
and obfuscatory typedefs), thousand line long functions, meaningless
abuse of volatile, incorrect locking or clear race conditions, cargo
cult atomics, useless or dangerous casts, ...

(And all of those examples were from 1 file, thank you NEZ for
throwing your wonderful ZNYV driver over the wall!)

My flatmate was explaining why his Ayuravedic diet doesn't permit
him to eat onion, garlic, or anything from that family - the
scriptures say that eating such things makes you angry and aggressive.
"But surely, if you have enough self control to follow an incredibly
strict vegetarian diet, then you have enough self control to not
get angry and aggressive after eating an onion bhaji?". "No, but..."

If you have the self control to write perverted yoda code, then
surely you've got the self control to not type '=' when you mean
'==', and to heed the new compiler warnings that you introduce when
making such a change?

Phil
 
P

Phil Carmody

Ike Naar said:
James Kuyper said:
On 08/24/2013 03:05 PM, Sharwan Joram wrote: [...]
if ( NULL == parameters[parametercount]){

This is what's known as a "Yoda conndition"
<http://en.wikipedia.org/wiki/Yoda_Conditions>. I know that a lot of
programmers like them, and for somewhat valid reasons, but personally I
find them jarring and unnecessary. Personally, I'd write that as:

if (parameters[parametercount] == NULL) {

Does it matter? The == operator is symmetric, (X==Y) == (Y==X).

If (X==Y) is jarring and unnecessary, then, for symmetry reasons
(Y==X) is unnecessary and jarring.

In simple term - bollocks.

Was 10 seconds less than the time you spent thinking before posting
your reply?

Phil
 
P

Phil Carmody

Keith Thompson said:
If your only criterion for choosing between two different ways
of writing something is that they have the same language-level
semantics, that should mean you have no preference between arr
and i[arr], or between

if (foo) {
/* ... */
}

and

if (!foo); else {
/* ... */
}


Don't stop there - this is equivalent too:

if (!!!!!!!!!!!!!foo); else {

Phil
 
P

Phil Carmody

Tim Rentsch said:
I find this interesting and also rather astonishing.
(Not unbelievable, just astonishing.)

To offer a point of comparison, I also learned Pascal
before I learned C, but immediately took to the C-isms
'if(ptr)' and 'if(!ptr)' without any difficulty. When
I see something like 'if (ptr != NULL)' it almost always
looks awkward or somewhat contrived. Not meaning to
imply anyone else should have this reaction, just that it
is the reaction I have myself.

Tally one more for that point of view here. However, I don't
physically wince when I see (ptr != NULL), unlike some other
jarring and unnecessary constructs.

Phil
 
P

Phil Carmody

Ike Naar said:
Do you find (42 + x) more or less awkward than (x + 42), and why?
If you don't care, then what's the fundamental difference between
the + operator and the == operator?

It depends entirely on context. Base plus offset would be my general
preference. So:

buffer_end = buffer_start + 0x4000;

but:

regs = 0x40008c00 + bank_offset;

Phil
 
J

James Kuyper

On 08/27/2013 09:25 AM, Phil Carmody wrote:
....
If you have the self control to write perverted yoda code, then
surely you've got the self control to not type '=' when you mean
'==',

I see no basis for the "perverted" label; it's nothing more than a style
issue based upon linguistically-based prejudices.

It takes far less self control to routinely use Yoda conditionals, than
it takes to never type '=' when you mean '=='. That's primarily because
of the difference between "routinely" and "never". I'm not sure that
there's any one remotely human who has sufficient self control to never
commit such a mistake; I'm willing to concede that you might not be
covered by that statement.
 
P

Phil Carmody

James Kuyper said:
On 08/27/2013 09:25 AM, Phil Carmody wrote:
...

I see no basis for the "perverted" label; it's nothing more than a style
issue based upon linguistically-based prejudices.

It takes far less self control to routinely use Yoda conditionals, than
it takes to never type '=' when you mean '=='. That's primarily because
of the difference between "routinely" and "never". I'm not sure that
there's any one remotely human who has sufficient self control to never
commit such a mistake; I'm willing to concede that you might not be
covered by that statement.

You notice that dangling comma at the end of the bit you quoted?
That comma implies there's more to that sentence.

Phil
 
J

James Kuyper

You notice that dangling comma at the end of the bit you quoted?
That comma implies there's more to that sentence.

True, but I cut out all the parts of you message that weren't relevant
to my response. That's not to say that there was anything wrong with the
parts that I did cut out, just that my response was not about those parts.
 
K

Keith Thompson

Phil Carmody said:
Tally one more for that point of view here. However, I don't
physically wince when I see (ptr != NULL), unlike some other
jarring and unnecessary constructs.

If all our pointers we called "ptr", I'd probably have less problem
with "if (ptr)".

What I do find jarring (and often incorrect) is "if (cond == true)".
 
G

glen herrmannsfeldt

(snip)
If the English had not wanted to lose control of the English language,
they shouldn't have conquered and colonized so many countries and forced
them to use that language. The English now constitute a minority among
native speakers of English. There's many more native speakers of US
English alone than there are of all of the British varieties of English.
Get over it.

I once knew that in Europe, English classes were taught in England
English and not US English. I suppose I would expect that from other
(than US) former English colonies, too.

Seems to me that US is louder, but maybe there aren't more of us.

-- glen
 
K

Keith Thompson

Ike Naar said:
There is no reason to write it that way for the "==" vs. "=" issue.

if (strcmp(s1, s2) == 0)

is already safe, because

if (strcmp(s1, s2) = 0) /* oops, mistyped "==" */

is not valid C.

True -- but I've seen programmers who habitually use "Yoda
conditions" even in cases where they're not necessary. I suppose
cultivating the habit of putting the constant on the left, without
necessarily thinking each time about whether it's necessary, does
avoid "=" vs. "==" problems.

I appreciate the principle behind it (I have some habits I cultivate
myself for similar reasons); I just have a mental quirk that makes
me dislike it in this case.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top