Question about memory allocation??

S

sethukr

Hi everybody,

While running the following program in GCC, i'm very much screwed.

main()
{
char *ptr1;
char arr[1];
int i;
char *ptr2;

ptr1 = (char *)malloc(1);
ptr2 = (char *)malloc(1);


printf("\n & ptr1 : %u\n", &ptr1); /* I got outputs
like, 000053

printf("\n address of arr : %u\n", arr); /* shows 000052

printf("\n address of i : %u\n", &i); /* shows 000045

printf("\n & ptr2 : %u\n", &ptr2); /* shows 000041
}

**Why the memory is allocated from bottom to top?? ( in the order
ptr2, i , arr, and ptr1)

**Integer takes only 4-bytes, it occupies 000045 to 000048.

**What happened to the memory between 000049 to 000051 here??

Can anybody tell me why this magic is happened??

Thanks in advance,
Sethu
 
G

Guest

Hi everybody,

While running the following program in GCC, i'm very much screwed.

That's not possible. You compile your program with GCC, you run it in
whatever operating system you are using.

Old-style definition. Better to be explicit: int main(void)
{
char *ptr1;
char arr[1];
int i;
char *ptr2;

ptr1 = (char *)malloc(1);
ptr2 = (char *)malloc(1);

Don't cast malloc's result unless you know what you're doing. You
don't. The proper way to get rid of the warning that you get without it
is to include said:
printf("\n & ptr1 : %u\n", &ptr1);

If you use printf, you need a proper prototype. Include <stdio.h>.
After fixing that, %u is the format specifier for unsigned int. It is
not the format specifier for any type of pointer. The way to print
pointers is to convert them to void *, and then use printf's %p format
specifier.
/* I got outputs
like, 000053

/* comments require a matching */.
printf("\n address of arr : %u\n", arr); /* shows 000052

printf("\n address of i : %u\n", &i); /* shows 000045

printf("\n & ptr2 : %u\n", &ptr2); /* shows 000041
}

**Why the memory is allocated from bottom to top?? ( in the order
ptr2, i , arr, and ptr1)

Because that is the way your operating system works. As for why your
operating system works that way, it could be because that happens to be
the most efficient method for your processor type, it could be for
compatibility with other systems, it could be because it was easier to
implement, or it could be for any number of other reasons.
**Integer takes only 4-bytes, it occupies 000045 to 000048.

It's possible but extremely unlikely that the addresses used are
actually 45 through 48. What's more likely is that you get misleading
output because you lied to the compiler. Do you get different addresses
with %p?
**What happened to the memory between 000049 to 000051 here??

It's not used.
Can anybody tell me why this magic is happened??

It's hardly magic. It's because your compiler decided it would be a
better idea to waste three bytes of memory than to deal with the
problems caused by using it. These problems vary from processor to
processor, and even from operating system to operating system, but
generally speaking, programs can crash or run much more slowly if the
compiler does not make sure variables of certain types are not stored
at specific addresses. Which types and which addresses are again
specific to your processor and operating system.
 
R

Richard Heathfield

(e-mail address removed) said:
Hi everybody,

While running the following program in GCC, i'm very much screwed.

main()
{
char *ptr1;
char arr[1];
int i;
char *ptr2;

ptr1 = (char *)malloc(1);

Undefined behaviour: you forgot the prototype for malloc. See recent
comp.lang.c discussion in the thread entitled "malloc".
ptr2 = (char *)malloc(1);


printf("\n & ptr1 : %u\n", &ptr1); /* I got outputs
like, 000053

Undefined behaviour: (a) you forgot the prototype for printf, and (b) you
gave a pointer value to printf when it was expecting an unsigned int. Each
of these is sufficient cause for the program to behave in any way
whatsoever.
**Why the memory is allocated from bottom to top?? ( in the order
ptr2, i , arr, and ptr1)

Why shouldn't it be?
**Integer takes only 4-bytes, it occupies 000045 to 000048.

**What happened to the memory between 000049 to 000051 here??

Compilers will often align pointers on boundaries that are multiples of a
given (typically small) integer.
Can anybody tell me why this magic is happened??

When you break the rules of C, anything can happen.
 
C

Chris Dollin

Hi everybody,

While running the following program in GCC, i'm very much screwed.

I don't see why. ("Screwed" in this context usually means "it broke,
and this was a Very Bad Thing".)

`int main(void)` is preferred; and the `int` is /necessary/ for C99.
{
char *ptr1;
char arr[1];
int i;
char *ptr2;

ptr1 = (char *)malloc(1);

You should #include <stdlib.h> if you're mallocating. And the `(char*)` is
unnecessary (and unwise). I'd also use `malloc(sizeof(*ptr1))` because that's
part of a general safe pattern.
ptr2 = (char *)malloc(1);


printf("\n & ptr1 : %u\n", &ptr1); /* I got outputs
like, 000053

You should #include <stdio.h> if you're printfing.

You were unlucky. `&ptr1` is a pointer value, not an integer: you
should use the `%p` format and cast to `(void*)` to ensure safe
portability.

Otherwise you get Undefined Behaviour: all bets about your code's
behaviour are off.
printf("\n address of arr : %u\n", arr); /* shows 000052

printf("\n address of i : %u\n", &i); /* shows 000045

printf("\n & ptr2 : %u\n", &ptr2); /* shows 000041
Dittoes.

}

**Why the memory is allocated from bottom to top?? ( in the order
ptr2, i , arr, and ptr1)

Why not? The order of placement is the compiler's business. It can
allocate in whatever order it finds convenient. Maybe it's allocating
on a "stack" which "grows downwards". Who cares? [1]
**Integer takes only 4-bytes, it occupies 000045 to 000048.

**What happened to the memory between 000049 to 000051 here??

I expect it's been eaten by alignment issues.
Can anybody tell me why this magic is happened??

This is C. There is /no magic/ (except as permitted by Undefined
Behavior).

The compiler will allocate local variables as convenient and
efficient as directed. Usually you don't need to care.

[1] /Some/ people care. Usually they're obliged to use some specific
implementation and know exactly why they care.
 
J

Jens Thoms Toerring

While running the following program in GCC, i'm very much screwed.

Missing headers:

#include <stdio.h>
#include said:

Make that

int main( void )
{
char *ptr1;
char arr[1];
int i;
char *ptr2;
ptr1 = (char *)malloc(1);

Don't cast the return value of malloc() - it won't buy you anything
but will keep the compiler from complaining if you forgot to include
ptr2 = (char *)malloc(1);
printf("\n & ptr1 : %u\n", &ptr1); /* I got outputs
like, 000053

The correct way to print pointers is to use "%p", used with the
pointer cast to a void pointer, e.g.

printf("\n & ptr1 : %p\n", ( void * ) &ptr1);
printf("\n address of arr : %u\n", arr); /* shows 000052
printf("\n address of i : %u\n", &i); /* shows 000045
printf("\n & ptr2 : %u\n", &ptr2); /* shows 000041

You forgot to return an int here as it is expected of main().
**Why the memory is allocated from bottom to top?? ( in the order
ptr2, i , arr, and ptr1)

That memory isn't "allocated", at least not in the sense of
functions like malloc() etc. The compiler must make sure that
memory is available for local variables when the function is
called. How it does that is completely up to the implementation.
And the addresses of these variables can be wherever the compiler
likes them to be - it could even have different areas of memory
for ints, chars, pointers etc. (it also might "optimize out"
some of the local variables and just keep them in CPU registers).
If you plan to use some tricks because you believe you know how
your compiler is doing it, that's up to you, but you won't writ
C programs anymore that are portable - you're rather likely to
invoke undefined behaviour.
**Integer takes only 4-bytes, it occupies 000045 to 000048.
**What happened to the memory between 000049 to 000051 here??

That's strictly for the compiler to decide which also has to take
into account alignment issues.
Can anybody tell me why this magic is happened??

The only way to find out about your compiler and the architecture
you're on (but mind, a different compiler will may do it differently,
and even the same compiler may do it differently when the program
gets compiled for a different arcitecture!) is to either read the
code for the compiler or ask its writers.

Regards, Jens
 
K

Kohn Emil Dan

Hi everybody,

While running the following program in GCC, i'm very much screwed.

You must #include said:
main()
{
char *ptr1;
char arr[1];
int i;
char *ptr2;

ptr1 = (char *)malloc(1);
ptr2 = (char *)malloc(1);

The correct printf() format specifier for printing pointers is %p, not %u!
printf("\n & ptr1 : %u\n", &ptr1); /* I got outputs
like, 000053

printf("\n address of arr : %u\n", arr); /* shows 000052

printf("\n address of i : %u\n", &i); /* shows 000045

printf("\n & ptr2 : %u\n", &ptr2); /* shows 000041
}

**Why the memory is allocated from bottom to top?? ( in the order
ptr2, i , arr, and ptr1)

Because the C standard does not disallow this. Most likely it's easier for
the compiler to allocate variables this way if the machine uses a stack
that grows towards lower addresses. Try to understand that you cannot rely
on this in your programs. Even for gcc, passing different optimization
flags (e.g. -O3 (optimize for speed) vs -Os (optimize for space)) will
give different results.


**Integer takes only 4-bytes, it occupies 000045 to 000048.

**What happened to the memory between 000049 to 000051 here??

Perhaps the compiler decided that pointers have to be aligned at an
address which is a multiple of 4. Nowhere in the standard says that
variables have to be allocated one after the other, or even in the order
in which they are defined, and aligning variables in general results
better runtime performance.

Can anybody tell me why this magic is happened??


The definite answer can be found by studying the gcc internals manual or
even better the gcc source.
Thanks in advance,
Sethu
Emil
 
K

Kenneth Brody

Chris said:
(e-mail address removed) wrote: [...]
Can anybody tell me why this magic is happened??

This is C. There is /no magic/ (except as permitted by Undefined
Behavior).
[...]

Well, one could say that there is "magic", at least as far as the
application is concerned.

Q: How does free() know how much memory was allocated?
A: Magic.

--
+-------------------------+--------------------+-----------------------+
| Kenneth J. Brody | www.hvcomputer.com | #include |
| kenbrody/at\spamcop.net | www.fptech.com | <std_disclaimer.h> |
+-------------------------+--------------------+-----------------------+
Don't e-mail me at: <mailto:[email protected]>
 
G

goose

And the addresses of these variables can be wherever the compiler
likes them to be - it could even have different areas of memory
for ints, chars, pointers etc. (it also might "optimize out"
some of the local variables and just keep them in CPU registers).

I'd be interested to know how an implementation plans to return
the address of a register.

goose,
 
J

Jens Thoms Toerring

I'd be interested to know how an implementation plans to return
the address of a register.

If you take the address of a variable the compiler knows that it
can't "optimize out" the variable - but you usually don't print
out addresses, this was only done by the OP in order to find out
where the compiler puts the local variables.

Regards, Jens
 
K

Keith Thompson

While running the following program in GCC, i'm very much screwed.

main()
{
char *ptr1;
char arr[1];
int i;
char *ptr2;

ptr1 = (char *)malloc(1);
ptr2 = (char *)malloc(1);


printf("\n & ptr1 : %u\n", &ptr1); /* I got outputs
like, 000053

printf("\n address of arr : %u\n", arr); /* shows 000052

printf("\n address of i : %u\n", &i); /* shows 000045

printf("\n & ptr2 : %u\n", &ptr2); /* shows 000041
}

Here's a corrected version of your program:

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

int main(void)
{
char *ptr1;
char arr[1];
int i;
char *ptr2;

ptr1 = malloc(1);
ptr2 = malloc(1);

printf("&ptr1 = %p\n", (void*)&ptr1);
printf("arr = %p\n", (void*)arr);
printf("&i = %p\n", (void*)&i);
printf("&ptr2 = %p\n", (void*)&ptr2);

return 0;
}

What I've done:

You're calling printf(), so the "#include <stdio.h>" is mandatory.
You're calling malloc(), so the "#include <stdlib.h>" is mandatory.

(Well, not *quite*; there are other ways to acheive the same
effect, but there's absolutely no point in using them. The
"#include" is the right way to do this.

Don't cast the result of malloc(). Doing so can mask certain errors,
such as omitting the required "#include <stdlib.h>" or incorrectly
using a C++ compiler to compile C code.

A good pattern for using malloc() is:

ptr = malloc(sizeof *ptr);

or, if you want to allocate an array:

ptr = malloc(COUNT * sizeof *ptr);

I've used the simpler "malloc(1)" here since you're only allocating a
single character (and sizeof(char) is 1 by definition). But if you're
going to be changing the code in the future, the lines:

ptr1 = malloc(1);
ptr2 = malloc(1);

should probably be written as:

ptr1 = malloc(sizeof *ptr1);
ptr2 = malloc(sizeof *ptr2);

The way to print a pointer value with printf is to convert it to void*
(one of the few cases where a cast is correct and necessary) and use
the "%p" format. The result of the "%p" format is
implementation-specific; it can be a number in hexadecimal, decimal,
octal, or even something other than a number. On the implementations
I use, it's a hexadecimal number corresponding to the representation
of the address. But remember that addresses *are not* numbers;
they're addresses.

I don't know why you called malloc(). You allocate one byte each for
ptr1 and ptr2 to point to, but you never use that allocated memory.
(You're printing the addresses of the pointer objects themselves,
*not* the addresses of what they point to.) If you are going to use
the allocated memory, you should *always* check the result of
malloc(); it returns a null pointer value on failure. (There's not
always a good way to handle this failure, but even aborting the
program with an error message is likely to be better than blindly
ignoring the problem.)
**Why the memory is allocated from bottom to top?? ( in the order
ptr2, i , arr, and ptr1)

Why not? Who cares?

It's the compiler's job to worry about where and how variables should
be allocated. It does this so you don't have to. It will allocate
local variables in whatever way the compiler's author decided made the
most sense. There may or may not be a consistent order. There may or
may not be gaps to allow for alignment. There *will* be a unique and
valid address for each declared variable, and that's really all you
need to know. If you write code that depends on the details of how
things are allocated, that code is non-portable, and it will break
when you try it with another compiler.

You're likely to find the comp.lang.c FAQ illuminating; it's an
excellent resource, thanks to a lot hard work by Steve Summit. It's
at <http://www.c-faq.com/>. Don't expect to be able to read the whole
thing in one sitting. If you like, you can start by reading bits and
pieces of it -- and you should always check it before posting a
question here.

But the FAQ is a set of answers to specific questions, not a tutorial.
You should also have a good book or other C tutorial. K&R2 (Kernighan
& Ritchie, _The C Programming Language_, 2nd edition) is generally
acknowledged to be the best tutorial, but it helps to have some
programming experience. For other resources, see the FAQ.
 
M

malc

Keith Thompson said:
While running the following program in GCC, i'm very much screwed.
[..snip..]

Don't cast the result of malloc(). Doing so can mask certain errors,
such as omitting the required "#include <stdlib.h>" or incorrectly
using a C++ compiler to compile C code.

A good pattern for using malloc() is:

ptr = malloc(sizeof *ptr);

or, if you want to allocate an array:

ptr = malloc(COUNT * sizeof *ptr);

Opinions on whether this is a good pattern vary:
http://lkml.org/lkml/2005/9/18/29

Perhaps you can add something in support of the 'good pattern' argument
not expressed in the aforementioned thread?

[..snip..]
 
K

Keith Thompson

goose said:
I'd be interested to know how an implementation plans to return
the address of a register.

On some hardware, registers have addresses. (This isn't the case on
most modern systems.)

More commonly, a compiler might allocate a memory location for a
variable, but keep its value in a register for part of its lifetime,
storing it from the register to memory only when its address is
needed. This isn't quite "optimizing it out", but it's close. (And
no, this doesn't really involve returning the address of a register,
so it's not *quite* responsive to your question.)
 
K

Keith Thompson

malc said:
Keith Thompson said:
While running the following program in GCC, i'm very much screwed.
[..snip..]

Don't cast the result of malloc(). Doing so can mask certain errors,
such as omitting the required "#include <stdlib.h>" or incorrectly
using a C++ compiler to compile C code.

A good pattern for using malloc() is:

ptr = malloc(sizeof *ptr);

or, if you want to allocate an array:

ptr = malloc(COUNT * sizeof *ptr);

Opinions on whether this is a good pattern vary:
http://lkml.org/lkml/2005/9/18/29

Perhaps you can add something in support of the 'good pattern' argument
not expressed in the aforementioned thread?

[..snip..]

The cited web page does raise some interesting arguments. It talks
about kmalloc rather than malloc, but the point is the same. To
summarize, the author (one Russell King) argues that
p = malloc(sizeof(struct foo));
is superior to
p = malloc(sizeof *p);

Why? Because for certain code changes, you're going to want to search
the code base for references to type "struct foo"; using the "sizeof *p"
form effectively hides the allocation from such a search.

(But please don't depend on my summary; since I don't necessarily
agree with the argument, I'm likely to present it incorrectly.)

In my opinion, the "sizeof *p" form is still to be preferred. To
someone reading the code, it's obviouly correct *without* searching to
find the declaration of p. The line:
p = malloc(sizeof(struct foo));
will be happily accepted by the compiler, but it's incorrect if p
happens to be of type struct bar* rathar than struct foo*. In either
case, you have to maintain consistency between the pointer
(particularly what it points to) and the argument passed to malloc().
The "sizeof *p" form makes it easy to do this, because it only has to
be consistent within a single line. To verify that the "sizeof(struct
foo)" form is correct, you have to track down the declaration of p.
Some tools may do this for you. Are any compilers capable of
recognizing the "sizeof(struct foo)" idiom and issuing a warning if
the target is of a different type?

As far as the maintenance argument goes, a search for "struct foo"
should take you to the declaration of "p" (which will have a much
better, name, *right?*). From there, you still need to track down
uses of p to make sure you've accounted for any changes. It's not
clear to me that using the type name in the malloc() call is all that
helpful. But then again, it's not entirely clear to me that it isn't.

Again, though, the cited web page does raise some interesting
arguments, and I wouldn't mind seeing some feedback from the other
regulars here.
 
G

goose

malc said:
Opinions on whether this is a good pattern vary:
http://lkml.org/lkml/2005/9/18/29

Perhaps you can add something in support of the 'good pattern' argument
not expressed in the aforementioned thread?

I'll address the authors points in the same order he presents
them:
1. He says -
"1. The above implies that the common case is that we are changing
the names of structures more frequently than we change the contents
of structures. Reality is that we change the contents of
structures
more often than the names of those structures.


Why is this relevant? If you change the contents of structures,
they need checking for initialisation. How do you find all the
locations that need initialisation checked? Via grep. The problem
is that:

p = kmalloc(sizeof(*p), ...)

is not grep-friendly, and can not be used to identify potential
initialisation sites. However:

p = kmalloc(sizeof(struct foo), ...)

is grep-friendly, and will lead you to inspect each place where
such a structure is allocated for correct initialisation."

I say -
This is certainly a valid argument but rather than using
grep to find all the malloc calls, I'd rather find all
declarations for "struct foo *p;" and double-check initialisation
(which will not occur far from the point) of p and, while I'm
at it, check that all serialisation of the struct is correct.

The reason is that, assuming we religiously follow the authors
convention, there will still be quite a few structs (and pointers
to structs) which are initialised *without* malloc. So we
will still have to hunt down all objects of type "struct foo" and
"struct foo *" anyway.
-------------------------------------
2. He says -
"2. in the rare case that you're changing the name of a structure,
you're
grepping the source for all instances for struct old_name, or doing
a search and replace for struct old_name. You will find all
instances
of struct old_name by this method and the bug alluded to will not
happen."

I say -
Correct; but you will have less to change by doing
grep -d recurse old_name | grep -v malloc
There is no need to make malloc dependent on artificial
restraints in order to function correctly, so why do so?

-------------------------------------
3. He says -
"3. if you are changing the name of a structure, in order to ensure
that
everyone gets fixed up correctly, you do not want to keep an old
declaration of the structure around, unless you have a very very
good
reason to do so. This will ensure that any missed old structure
names (eg, because of merging of independent concurrent threads of
development) get caught. As a result, any sizeof(struct) also gets
caught."

I say -
Here the author is just grasping at straws; he makes the argument
that "sizeof (struct old_name)" is better because all references
to old_name must be fixed when changing structure to "struct
new_name".
By avoiding even using old_name, there is nothing to "catch".

There is, in all of that above, only one halfway good justification,
but
the author *does* go on to say that the assertion that "malloc (sizeof
*p)"
method is better is flawed.

goose,


-------------------------------------
 
M

MJ_India

**Why the memory is allocated from bottom to top?? ( in the order
ptr2, i , arr, and ptr1)

Though behavior may differ with change of system and compiler, but when
I checked this with my pentium machine and tcc (int is 16 bit)
compiler, observation was as follows:

variables ptr1, arr, i and ptr2 were lodged at addresses BP - 2, BP -
4, BP - 6 and BP - 8 respectively. And thus the result. (BP is Base
pointer, usually all local variables are refereced with some offset of
this base pointer i.e. "BP - some offset" on X86 architecture. and thus
all local variable can be seen as a stack in memory starting from
address stored in BP. BP itself points to the return address of current
routine. (due to end of routine or return statement))

But Sethu the result was different on my redhat linux pentium machine.

About integer variable (int i) address, behavior differs with
endian-ness of machine as well.

Hope you got what I say -MJ
 
R

Randy Howard

Keith Thompson wrote
(in article said:
The cited web page does raise some interesting arguments. It talks
about kmalloc rather than malloc, but the point is the same. To
summarize, the author (one Russell King) argues that
p = malloc(sizeof(struct foo));
is superior to
p = malloc(sizeof *p);

Why? Because for certain code changes, you're going to want to search
the code base for references to type "struct foo"; using the "sizeof *p"
form effectively hides the allocation from such a search.

(But please don't depend on my summary; since I don't necessarily
agree with the argument, I'm likely to present it incorrectly.)

They're more interested in grep than code correctness, or at
least that particular guy was. The fact is, if you want code
that needs to be found on wholesale changes to a structure to be
found, mark it with a comment like:

/* GREPME: I must be updated here if struct foo changes */

and stop being a lazy knob...

ymmv.
 

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,961
Messages
2,570,131
Members
46,689
Latest member
liammiller

Latest Threads

Top