overwriting memory

R

Robbie Brown

Robbie Brown said:
On 26/01/14 13:15, Ben Bacarisse wrote:
By the way, you can reduce allocation size mistakes by using the
pattern:

var = malloc(sizeof *var * number_of_elements);

I have valgrind working and it certainly indicates a problem ... but I
don't understand the above (duh!). I think someone else mentioned
something like this before and I didn't get then either (duh! duh!)

var[1] = malloc(sizeof *var[2] * number_of_elements);

The idea is whatever is on the left is written with * in front on the
right, so I'd have put sizeof *var[1] there, myself.
1. I thought malloc returned a pointer

Yes. presumably in my example var is a pointer object. In yours,
var[1] must be a pointer object. sizeof *var[1] is the size of the type
of object pointed to var var[1].
2. If I want an int (32 bits on this machine) why ask for a pointer to
int (64 bits)

I'm baffled! sizeof can be applied to an expression. It does not evaluate the
expression, it just looks to see what type it is and uses that type to
determine the size. See below...
so

int *i = malloc(sizeof(int));
should give me a 64 bit pointer to a 32 bit int

But int *i = malloc(sizeof *i); also works. i is of type 'int *' so *i
is of type 'int'.
Sorry for being so thick, can you elucidate please.

Not knowing stuff is not at all the same as being thick. We all started
off knowing no C at all.

You're very kind :)

Anyway, I'm pretty sure I see what you mean, I'll do some experimenting
tomorrow, it's been a long day.

BTW valgrind is a wicked bit of kit, cheers for the heads up.

I was convinced my dynamically allocated LinkedList was working, and it
was but I hadn't got the freeing up of memory quite right, there was a
small leak that I was able to fix and an unitialized variable that I
hadn't caught. I would *never* have figured that out at my current level
of knowledge without.
 
K

Keith Thompson

Richard Damon said:
[2] *var, since var is a object (and not a type) requires var to have a
type of pointer to something (like we required in [1]), and evaluates
into an object of the type var points to (or Sometype here). Since it is
part of a sizeof expression, the pointer isn't actually dereferenced
(which is good since it doesn't have a real value yet), but the action
really just strips off one layer of pointer of from the type.

Note that if var was a type, then *var would be a pointer to type var,
but in this case it would need to be sizeof(*var), as for types, sizeof
requires the parenthesis. (For expressions sizeof does NOT parenthesis,
but of course if var is an expression, so is (var) ).

If var is a type, then the type "pointer to var" would be var*, not
*var. For example, sizeof (int*) is the size of a pointer to an int.
(And I hope you wouldn't name a type "var".)

To be a bit more explicit about the syntax, there are two forms of
the sizeof operator. One takes an expression as an operand:

sizeof expr

The other takes a parenthesized type name:

sizeof (type-name)

The expression can of course be a parenthesized expression, and there
are cases where it needs to be just for grouping, but in general
parentheses aren't needed unless the argument is a type name. (And in
most cases, you *want* an expression rather than a type name.)

[...]
 
K

Keith Thompson

Robbie Brown said:
2. If I want an int (32 bits on this machine) why ask for a pointer to
int (64 bits)

so

int *i = malloc(sizeof(int));
should give me a 64 bit pointer to a 32 bit int

Among other things, you seem to be assuming that an int is 32 bits and a
pointer is 64 bits. That may happen to be the case on the system you're
using, but it's not specified by the language. int can legally be as
narrow as 16 bits; 32 bits happens to be typical these days. Pointers
are typically either 32 or 64 bits, though other sizes are possible.

The language doesn't even guarantee that all pointer types have the same
size; for example, void* might be bigger than int*.
 
J

JohnF

Ben Bacarisse said:
JohnF said:
Ben Bacarisse <[email protected]> wrote:

Okay, so you're saying regardless of type (int, double, char, whatever)
ANY_TYPE *array = malloc(number_of_elements * sizeof *array);
will work, i.e., sizeof *array evaluates as sizeof ANY_TYPE ?

Yes. Although it is even more general that this. For example, to
allocate a triangular array:
double **triangle = malloc(rows * sizeof *triangle);
for (int r = 0; r < rows; r++)
triangle[r] = malloc((r + 1) * sizeof *triangle[r]);
You use * of whatever is being assigned to get the right size.

Wow, you're channeling (in the medium/spirit_guide sense)
my old contract
http://www.forkosh.com/resume.html?cmb0991
which was actually on my mind when I posted the malloc/calloc
followup here.
Had to write various binomial tree algorithms, and wrote
a library to encapsulate the low-level operations.
Considered your method above for allocation (double *talloc(n)
allocates a tree with n periods), but instead used the fact
that the triangle contains n*(n+1)/2 nodes and calloc'ed them
all at once. Indexing tree[period,node], node=0...period-1, then
done by #define tindex(period,node) ((period*(period+1)/2)+node)
so you'd write tree=talloc() and address tree[tindex()].
Your alternative certainly demonstrates current topic better,
but the other actually executed a bit faster on DECstation 5000
(based on MIPS 3000 chipset), though I don't know (and didn't
really try to find out) exactly why.
The reason it was on my mind when posting is that original
code, from 1991, ran and produced identical results at that time,
on DECstation 5000, Apollo 10000, a pretty fast VAX but I can't
recall which, and a PC running DOS with the Zortech C compiler.
But when I tried to get it running a year or two ago under linux,
got nan's all along the "edges" (nodes representing maximum
volatility), but okay numbers elsewheres. Must be something
that got initialized way back when (since it worked and was
checked incredibly thoroughly), and isn't initialized now.
But I couldn't track it down. Fortunately, no money was involved.
Just a free little online calculator I was going to write, advising
people when to refinance their mortgages (a friend was making
that decision, but not understanding how to properly go about it).
So anyway, I did a double- and triple-take when you
suggested that example. Seemed like you were reading my mind.
And just to check: I'm thinking of a number from 1 to 10...
Yeah, I guess that looks right, in principle, though I did have
to look twice (and wouldn't have been surprised if you'd told me
it didn't always work in practice).

There might be a corner case or two. I can't think of one off hand.

How about something my complex. You use arrays of three numbers to
represent some important data about things (points in space, grades in
final exams, whatever). You don't know how many things, so you need
malloc. Start with the declaration:

double (*data)[3]; // A pointer to arrays of 3 doubles

Once you know how many:

data = malloc(how_many * sizeof *data);

If you decide the data needs to float, or you need 4 pieces of per
thing, or it should really be a structure for each thing, you just
change the declaration.

Thanks for additional info and examples.
 
J

JohnF

Eric Sosman said:
[...]
Okay, thanks again. So I take it I can safely and portably say
double *array = (double *)malloc(number_of_elements * sizeof double);

`sizeof(double)' -- the parentheses are mandatory when you're
applying `sizeof' to a type name rather than to an expression. Thanks.
for ( i=0; i<number_of_elements; i++ ) array = (double)(i*i);


The cast is either unnecessary or misplaced (`(double)i * i'
might be what you meant, if `i' could be large).

Unnecessary. I almost always over-cast, maybe an artifact of
memories of Fortran II mixed-mode errors.
or some such. That is, complying malloc (and calloc) has to return
ptrs so that all such float/double operations will work okay
on array addressed in that typical way.


Right: The pointer returned by a successful call is suitably
aligned for every C data type. The pointer returned by an
*unsuccessful* call, though, is NULL -- so for "safely and
portably" you should check before plowing ahead and using it.

Also, take another look at the pattern Ben Bacarisse showed.
You've written (paraphrased)
Type *array = (Type*) malloc(N * sizeof(Type));
while he wrote
Type *array = malloc(N * sizeof *array);

There are two differences, one fairly minor and one of moderate
importance:

- It's unnecessary to cast the value returned by malloc()
(or calloc(), etc.). That value has the type `void*',
which will convert to any other data pointer type without
need for a cast.

As above, just unnecessary.
- Writing `sizeof *array' instead of `sizeof(Type)' means
it's impossible to get the type wrong and ask for `int'
elements when you really meant `int*'. Such slip-ups
occur (in my experience) either from writing the wrong
number of `*'s or when dealing with a lot of similarly-
named but distinct types, as in
I think I'd stick with sizeof(Type) for very simple allocations
like the double *array=malloc(n*sizeof(double)); example.
Just seems more readable to me, and not particularly more
error-prone than anything else. But for allocating complicated
typedef's, or such, I agree maybe your/Ben's way is better.
MessageHeader *hdr = malloc(sizeof(MessageHeader));
MessagePayload *msg = malloc(sizeof(MessageHeader)); // ?
Well, the discussion's convincing, but not really the above
example, where it seems to me that I could just have easily
blundered by writing MessagePayload *msg = malloc(sizeof *hdr); //?
 
R

Robbie Brown

Among other things, you seem to be assuming that an int is 32 bits and a
pointer is 64 bits.

Well you have to start somewhere don't you? On this machine, as I have
observed, addresses are reserved in 64 bits and ints 32. Well actually
even this appears to be incorrect. I have never seen an address that
appears larger than 48 bits ... starting with 7f so a 48 bit signed
integer which is ... er, lots of memory. I was going to say 'obviously'
next but I've learned that obvious is not a word I feel qualified to use
when discussing C, oh what the hell, obviously (I think) the reason that
addresses are stored in 64 bits is because that is the width of the data
bus so 48 bits are address and 16 bits are padding. Like I say, you have
to start somewhere. If I discover I'm wrong then I've learned something.
That may happen to be the case on the system you're
using, but it's not specified by the language. int can legally be as
narrow as 16 bits; 32 bits happens to be typical these days. Pointers
are typically either 32 or 64 bits, though other sizes are possible.

The language doesn't even guarantee that all pointer types have the same
size; for example, void* might be bigger than int*.

I don't understand this at all. An address is an address is an address
.... isn't it?.
 
S

Seebs

Well you have to start somewhere don't you?
Maybe!

On this machine, as I have
observed, addresses are reserved in 64 bits and ints 32. Well actually
even this appears to be incorrect. I have never seen an address that
appears larger than 48 bits ... starting with 7f so a 48 bit signed
integer which is ... er, lots of memory. I was going to say 'obviously'
next but I've learned that obvious is not a word I feel qualified to use
when discussing C, oh what the hell, obviously (I think) the reason that
addresses are stored in 64 bits is because that is the width of the data
bus so 48 bits are address and 16 bits are padding. Like I say, you have
to start somewhere. If I discover I'm wrong then I've learned something.

"Bits that are always zero" is not the same thing as "not part of the
object". It's most likely that the addresses are actually 64-bit, but that
nothing on this particular machine happens to have an address in some part
of that range.
I don't understand this at all. An address is an address is an address
... isn't it?.

Not necessarily. Machines have existed where you might have more bits in
a pointer to a character than in a pointer to an int. There's lots of
interesting variance out there.

-s
 
J

James Kuyper

Well you have to start somewhere don't you? ...

Yes, but the best place to start, as far as this particular issue is
concerned, is with the understanding that you don't know for certain
what the size of anything is, except insofar as your code uses sizeof to
find out. The result that sizeof gives is only guaranteed correct for
that particular implementation of C - it might be different on a
different implementation. Later on, you can learn that there are some
things you can be certain of about the sizes of various types, but the
best place to start is acknowledged ignorance, not system-specific
unjustified certainty.

....
I don't understand this at all. An address is an address is an address
... isn't it?.

The standard example of how this can come about is word-addressed
systems where the word is bigger than one byte. Pointers to word-aligned
types need only contain the address of the word where the object starts.
Pointers to types with alignment requirements that are smaller than a
word must contain both the address of a word, and an offset within the
word. Depending upon the word size and the amount of memory that needs
to be addressed, storing that extra information might require that such
pointers be larger than the word-aligned ones.
There may be other reasons why pointers to different types might have
different sizes, though I've never heard of one.

All that the C standard has to say about this is that pointers to
incompatible types are themselves incompatible types; you should avoid
writing code that assumes that any two such pointer types have the same
size, representation, or alignment requirement. Exception: void* is
guaranteed to have the same representation and alignment requirements as
pointers to char, signed char, and unsigned char.
 
J

James Kuyper

One exception to this is if you use a macro like

#define NEW(type) ((type)*)malloc(sizeof(type))

as a wrapper for malloc (It is of course a C++ism). This way if you do
something like

int* i = NEW(long);

the compiler will give you an error.

The compiler will give you an error _message_. The error itself was
already there, having been introduced by your use of that macro. And
there would not have been an error to report if it had instead been written
int *i = malloc(sizeof *i);
 
J

James Kuyper

Except that by definition, sizeof(char) == 1 (as well as unsigned char
and signed char). ...

That's an example of what I was referring to when I wrote, in the
section you snipped, that "Later on, you can learn that there are some
things you can be certain of about the sizes of various types ..."
 
R

Robbie Brown

You have to be kidding me?!?!?!

Are you auditioning for biggest Mr Spoons to post to usenet?

Jesus. Just when I thought it couldn't get more ridiculous here.

Who's Mr Spoons
Why is it ridiculous?
explain
 
K

Kenny McCormack

Robbie Brown said:
Who's Mr Spoons

Google (and IMDB) is (are) your friend(s).
Why is it ridiculous?

It's subtle, but if you stick around CLC long enough to absorb the, er,
shall we say, "unique" culture around here, you'll get it.

See above. It's hard to explain, but you'll get it in time.
Suffice to say that rgrdev is definitely someone to follow. He's got the
pulse on this group down to a T.

--
Windows 95 n. (Win-doze): A 32 bit extension to a 16 bit user interface for
an 8 bit operating system based on a 4 bit architecture from a 2 bit company
that can't stand 1 bit of competition.

Modern day upgrade --> Windows XP Professional x64: Windows is now a 64 bit
tweak of a 32 bit extension to a 16 bit user interface for an 8 bit
operating system based on a 4 bit architecture from a 2 bit company that
can't stand 1 bit of competition.
 
J

James Kuyper

Robbie Brown said:
On 27/01/14 14:31, Richard wrote: [snip]

Who's Mr Spoons
Why is it ridiculous?
explain

Please don't feed the troll.

I'm not sure he's seen enough of Richard's messages yet to justify
concluding that Richard is a troll. If he hasn't, it's premature to
criticize him for "feeding the troll". You could just tell Robbie that
"Richard is a troll, ignore him", but I think it's best to just let him
figure that out for himself.
 
K

Keith Thompson

James Kuyper said:
Robbie Brown said:
On 27/01/14 14:31, Richard wrote: [snip]

Who's Mr Spoons
Why is it ridiculous?
explain

Please don't feed the troll.

I'm not sure he's seen enough of Richard's messages yet to justify
concluding that Richard is a troll. If he hasn't, it's premature to
criticize him for "feeding the troll". You could just tell Robbie that
"Richard is a troll, ignore him", but I think it's best to just let him
figure that out for himself.

It was intended as advice, not as criticism. I more or less assumed
that all that was implicit in what I wrote. But yes, it's likely
I was insufficiently clear, and your addendum is helpful.

Robbie: Richard is a troll; I suggest ignoring him.
 
J

Jorgen Grahn

James Kuyper said:
On 27/01/14 14:31, Richard wrote:
[snip]

Who's Mr Spoons
Why is it ridiculous?
explain

Please don't feed the troll.

I'm not sure he's seen enough of Richard's messages yet to justify
concluding that Richard is a troll. If he hasn't, it's premature to
criticize him for "feeding the troll". You could just tell Robbie that
"Richard is a troll, ignore him", but I think it's best to just let him
figure that out for himself.

It was intended as advice, not as criticism. I more or less assumed
that all that was implicit in what I wrote. But yes, it's likely
I was insufficiently clear, and your addendum is helpful.

Robbie: Richard is a troll; I suggest ignoring him.

To be precise, /one/ of them is. There are two in the thread.

/Jorgen
 
J

Jorgen Grahn

.
#define NEW(type) ((type)*)malloc(sizeof(type))

as a wrapper for malloc (It is of course a C++ism).

It might be, but speaking as a C++ user I wouldn't use it. Most of the
interesting features of C++ 'new' are absent, so it would just confuse
me.

/Jorgen
 
K

Keith Thompson

Jorgen Grahn said:
To be precise, /one/ of them is. There are two in the thread.

Right, I'm referring to the Richard who posts without a last name (his
e-mail address is something like (e-mail address removed)), not (at all!) to
Richard Damon.
 
J

James Kuyper

To be precise, /one/ of them is. There are two in the thread.

There was only one that was directly relevant, having been quoted
earlier. Richard uses only a mononym as identification, - perhaps he's
Javanese? :) When it's less clear than it is in this case, people
sometimes refer to him using some variant on "Richard NoName".
 
M

Malcolm McLean

It might be, but speaking as a C++ user I wouldn't use it. Most of the
interesting features of C++ 'new' are absent, so it would just confuse
me.
Yes, it's highly confusing to use a C++ keyword in upper case, particularly
if you're stripping functionality out.
What I often to however is have a structure name in all-caps, and a
constructor for it using the same name, in lower case. So it looks rather
like a C++ call. Of course you initialise all the fields in the function.
 

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
474,075
Messages
2,570,549
Members
47,197
Latest member
NDTShavonn

Latest Threads

Top