IS this a proper way of freeing memory with free()

K

Keith Thompson

Ian Collins said:
Richard said:
Ian Collins said: [...]
So the code in question is written in the subset of C that is valid
C++.

With identical semantics? Are you *sure*? And once you've answered in
the affirmative, ask yourself whether you are *really* sure.
Yes, and we back that up with acceptance tests that run against the
simulation and the final product.

Acceptance tests can find bugs; they can't prove the absence of bugs.
It's at least conceivable that you have some bug caused by a piece of
code that's legal C and legal C++, but with different semantics, and
that your acceptance tests aren't quite thorough enough to find it.
Consistently compiling your driver code as pure C, as I suggested in
another response in this thread, might avoid such bugs.

I'm not saying that this is likely or arguing that your approach is
wrong, just suggesting a possible improvement to your methodology.
(Or there might be a good reason why you can't do what I suggested.)
 
I

Ian Collins

Keith said:
Ian Collins said:
Richard said:
Ian Collins said:
[...]
So the code in question is written in the subset of C that is valid
C++.

With identical semantics? Are you *sure*? And once you've answered in
the affirmative, ask yourself whether you are *really* sure.

Yes, and we back that up with acceptance tests that run against the
simulation and the final product.


Acceptance tests can find bugs; they can't prove the absence of bugs.

Nor do I claim that they do. But I will claim that the code developed
in the host environment is better tested that if it had been developed
purely on the embedded target simple because the testing is much easier
and is therefore more rigorous. Some aspects of driver operation are
extremely difficult to unit test consistently on real hardware,
especially if the hardware is deeply embedded.
It's at least conceivable that you have some bug caused by a piece of
code that's legal C and legal C++, but with different semantics, and
that your acceptance tests aren't quite thorough enough to find it.
Consistently compiling your driver code as pure C, as I suggested in
another response in this thread, might avoid such bugs.
I agree. There is also a real chance that one or other of the compiler
have a bug which can lead to behavioural differences. In my experience,
the benefits gained form this approach outweigh the risks.
I'm not saying that this is likely or arguing that your approach is
wrong, just suggesting a possible improvement to your methodology.
(Or there might be a good reason why you can't do what I suggested.)
See may comments of the use of C++ objects for device registers.
 
I

Ian Collins

Keith said:
Interesting.

You need your drivers to be callable from C++ on the host system, so
you write them to be compilable as either C or C++, restricting
yourself to the intersection of the two languages. It seems to me
your requirement is perfectly reasonable, but I'm not convinced you've
found the best solution. Have you considered instead compiling your
drivers as C on the host system, and calling them from C++ using C++'s
'extern "C"' feature? Your headers would still have to be valid as
both C and C++ (possibly using "#ifdef __cplusplus" here and there),
but the actual implementation of the drivers could be pure C.
The problem is the other way round. I want the C code to work directly
with C++ objects (pretend bits of hardware).
 
Y

Yevgen Muntyan

Keith said:
Ok, I can *sort of* see your point here. Without the cast, the
expression is of type void*, which will be implicitly converted to any
pointer-to-object type. It's less safe in the sense that if you apply
sizeof to the wrong thing, the compiler won't warn you about it:

(A):
double *p1;
int *p2;
p1 = malloc(sizeof *p2);

Whereas if you use the type, then getting the type wrong will give you
a constraint error and a required diagnostic:

(B):
double *p1;
int *p2;
p1 = (int*)malloc(sizeof(int)); /* Compiler catches error */

But if you have the correct type in the cast, but the wrong type in
the sizeof argument, you're back to another error that the compiler
won't catch:

(C):
double *p1;
int *p2;
p1 = (double*)malloc(sizeof(int));

What you're doing is basically case (B) with the two occurrences of
the type name kept in synch by using a macro.

Yes, given that (B) is *exactly* quoted
((Type*) malloc (sizeof (Type))).
And (C) is irrelevant (but it adds to that many potential errors I am
going to step onto, yes?)
But if you want to use a macro, why not apply the same technique to
case (A), giving you just as much safety?

(D):
#define NEW_OBJ(ptr) ((ptr) = malloc(sizeof *(ptr)))
#define NEW_ARR(ptr, count) ((ptr) = malloc(count * sizeof *(ptr)))

Perhaps I want to do

Foo **array;
*array++ = CALLOC_OBJ(Foo);

with CALLOC_OBJ doing calloc(sizeof(Foo), 1) in this case (it's g_new0()
in glib-using code). In any case I don't know why I would want to use
NEW_OBJ(ptr) which modifies ptr just to use The Right (or how people say
"idiomatic in c.l.c") 'sizeof *ptr'.
double *p1;
int *p2;
NEW_OBJ(p1);
if (p1 == NULL) {
/* allocation failed */
}
NEW_ARR(p2, 10);
if (p2 == NULL) {
/* allocation failed */
}

Personally, I'm content to keep the sizeof argument consistent
manually, but you can use macros to do this for you *without* using
unnecessary casts.

Of course I can. As well I can avoid using goto, I can have single
return in any function, or (going other direction) I can avoid using
stream methods from stdio.h and use raw open/close/read/write, or...
But why? I have never claimed it's necessary to use casts. I did claim
using casts is nice and most importantly harmless in some situations.
You may not agree with "nice" part but you have to prove yet cast
can harm there.

Keith, please tell, did I claim I can't do without casts? If no,
why do you invent extra macros to demonstrate how I can do that?
I could just use malloc(whatever_is_right_in_c_l_c). If yes,
please quote that.

People again and again tell why I don't have to use cast or something.
I am refusing to follow the advice and then they tell gods will
punish me, and to prove that they show all kinds of errors possible
elsewhere in other situations. Why?
 
K

Keith Thompson

Ian Collins said:
The problem is the other way round. I want the C code to work directly
with C++ objects (pretend bits of hardware).

So, for example, a piece of C code in the driver running on the actual
hardware that says:
*ptr = 42;
will simply write 42 into the specified location -- but since that
location might have some special meaning in the target hardware,
there could be some hardware-specific side effects.

Whereas that same piece of code, compiled as C++, running in the host
emulator, might invoke an overloaded "*" function that emulates the
same side effects.

Is that (an oversimplification of) what's going on, or am I missing
the whole point?

I'm beginning to think that the number of posters who have good
reasons to compile the same code as C and as C++ has increased from 1
to 2.
 
I

Ian Collins

Keith said:
So, for example, a piece of C code in the driver running on the actual
hardware that says:
*ptr = 42;
will simply write 42 into the specified location -- but since that
location might have some special meaning in the target hardware,
there could be some hardware-specific side effects.

Whereas that same piece of code, compiled as C++, running in the host
emulator, might invoke an overloaded "*" function that emulates the
same side effects.

Is that (an oversimplification of) what's going on, or am I missing
the whole point?
Yes, that is what's going on.
I'm beginning to think that the number of posters who have good
reasons to compile the same code as C and as C++ has increased from 1
to 2.
:)
 
K

Keith Thompson

Yevgen Muntyan said:
Yevgen Muntyan <[email protected]> writes:
[snip]
[snip]

Keith, please tell, did I claim I can't do without casts? If no,
why do you invent extra macros to demonstrate how I can do that?
I could just use malloc(whatever_is_right_in_c_l_c). If yes,
please quote that.

You didn't say in so many words that you "can't do without casts", but
you did say, in so many words, that you "need" casts. In fact, you
don't *need* to use casts.
People again and again tell why I don't have to use cast or something.
I am refusing to follow the advice and then they tell gods will
punish me, and to prove that they show all kinds of errors possible
elsewhere in other situations. Why?

I've never claimed the gods will punish you, so I can't answer that.
I do question your claim that adding a cast can make for a "safer
expression". The expression (the result of malloc()) has a type,
namely void*. (Admittedly void* is in some sense a weaker type than,
say, foo*.) It acquires another type whether you use a cast or not.

But I'm already tired of this thread, even though I've hardly posted
to it, so I don't plan to debate the point any further.
 
Y

Yevgen Muntyan

Keith said:
Yevgen Muntyan said:
Yevgen Muntyan <[email protected]> writes:
[snip]
You're not kidding, are you? I said *I need* cast because I want a safer
expression. If I apply cast, the expression acquires a type.
[snip]

Keith, please tell, did I claim I can't do without casts? If no,
why do you invent extra macros to demonstrate how I can do that?
I could just use malloc(whatever_is_right_in_c_l_c). If yes,
please quote that.

You didn't say in so many words that you "can't do without casts", but
you did say, in so many words, that you "need" casts. In fact, you
don't *need* to use casts.

*I* do need them because they help *me* avoid stupid bugs.
I might have misused the English word "need", I do not feel
exact sense it carries when I say "I need". I guess "I want"
is better here, but if I used it, it would be immediately
interpreted as I want that cast in the same way as I want
ice cream, without any rational base.
I've never claimed the gods will punish you, so I can't answer that.

You didn't. You just added to the pool of all those errors I
should expect in my code by providing your example (C), even
though that's a type of error which is *impossible* if I use
the macro instead of blind casts here and there (the casts
which I don't use).
I do question your claim that adding a cast can make for a "safer
expression". The expression (the result of malloc()) has a type,
namely void*. (Admittedly void* is in some sense a weaker type than,
say, foo*.) It acquires another type whether you use a cast or not.

Yes, and malloc() is also not blind since it doesn't have eyes.
If you didn't understand what I meant by "typeless", I apologize
for poor wording. I doubt it though.
But I'm already tired of this thread, even though I've hardly posted
to it, so I don't plan to debate the point any further.

You hardly posted, but you did add to collective wisdom's "Wrong.",
didn't you.
 
R

Richard Heathfield

Keith Thompson said:
Ian Collins said:
Richard said:
Ian Collins said: [...]
So the code in question is written in the subset of C that is valid
C++.

With identical semantics? Are you *sure*? And once you've answered
in the affirmative, ask yourself whether you are *really* sure.
Yes, and we back that up with acceptance tests that run against the
simulation and the final product.

Acceptance tests can find bugs; they can't prove the absence of bugs.

Whilst I agree with your general line of argument, Keith, I think it
bears mentioning that acceptance tests are generally not intended even
to find bugs (let alone prove their absence, which they can't do anyway
as you rightly say), but rather to determine whether the software as a
whole performs substantially according to its specification: not "is
this program bug-free as far as we can tell?" but "is this program good
enough for our requirements?"

Personally, I'm of the opinion that any bug is one bug too many, but
many people seem to think that they'd rather use the software some time
this side of eternity and are therefore quite happy to put up with the
odd bug.
 
K

Keith Thompson

Richard Heathfield said:
Keith Thompson said:
Ian Collins said:
Richard Heathfield wrote:
Ian Collins said: [...]
So the code in question is written in the subset of C that is valid
C++.

With identical semantics? Are you *sure*? And once you've answered
in the affirmative, ask yourself whether you are *really* sure.

Yes, and we back that up with acceptance tests that run against the
simulation and the final product.

Acceptance tests can find bugs; they can't prove the absence of bugs.

Whilst I agree with your general line of argument, Keith, I think it
bears mentioning that acceptance tests are generally not intended even
to find bugs (let alone prove their absence, which they can't do anyway
as you rightly say), but rather to determine whether the software as a
whole performs substantially according to its specification: not "is
this program bug-free as far as we can tell?" but "is this program good
enough for our requirements?"

It reduces to the same thing if you define "bug" as "failure to meet
requirements". The trick then is to write bug-free requirements.
Regress until dizzy.
Personally, I'm of the opinion that any bug is one bug too many, but
many people seem to think that they'd rather use the software some time
this side of eternity and are therefore quite happy to put up with the
odd bug.

Well, not happy, but tolerant. Good point, though.
 
R

Richard Bos

Ian Collins said:
I'm not sure if you are being deliberately obtuse.

My point is there is sometimes value in having C code which is valid
C++. The driver code in question is compiled as C for the embedded
target, but compiled with the host C++ compiler for testing in a
hardware simulator.

In other words, you're testing something different from what you're
sending to your end users. Is that wise?
So the code in question is written in the subset of C that is valid C++.

Valid, but slightly different. Do you want to bet on the slightly being
slight enough, or do you want to compile both as the same language and
make sure?

Richard
 
I

Ian Collins

Richard said:
In other words, you're testing something different from what you're
sending to your end users. Is that wise?
I think I've answered that elsethread, the code is developed and tested
in a host environment and the acceptance tests are run (very often as
they are automated) on both the simulation and target.
Valid, but slightly different. Do you want to bet on the slightly being
slight enough, or do you want to compile both as the same language and
make sure?
I don't bet, I test!
 

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,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top