Can I do ANYTHING on allocated memory?

S

Sourcerer

Does any of the following constitute undefined behavior? Once I allocate memory,
can I do with it whatever I want (as in the example, use memory allocated as
char *, as memory where I store integers)?

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

int main(void) {
char *a;
int *x, *y;
a = (char *) malloc(2 * sizeof(int));
// here's the part I'm wondering about
x = (int *) a;
y = (int *) a + sizeof(int);
*x = 10;
*y = 20;
printf ("%d %d\n", *x, *y);
return 0;
}

--
"It is easy in the world to live after the world's opinion; it easy in solitude
to live after our own; but the great man is he who in the midst of the crowd
keeps with perfect sweetness the independence of solitude."
Ralph Waldo Emerson, Self-reliance 1841
http://pinpoint.wordpress.com/
 
M

Malcolm McLean

Sourcerer said:
Does any of the following constitute undefined behavior? Once I allocate
memory, can I do with it whatever I want (as in the example, use memory
allocated as char *, as memory where I store integers)?

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

int main(void) {
char *a;
int *x, *y;
a = (char *) malloc(2 * sizeof(int));
// here's the part I'm wondering about
x = (int *) a;
y = (int *) a + sizeof(int);
*x = 10;
*y = 20;
printf ("%d %d\n", *x, *y);
return 0;
}
You are playing fast and loose with the C type system.
It is there to help you avoid alignment problems and other nasties, however
for backwards compatibility and certain low-level operations, C also gives
you the freedom to mess about with it.
Technically I think you are OK because pointers to char can be converted to
any other type. This is a hangover from the days when there were no void
pointers, so char *s were used instead.
However had you cast to a short *, and back, it would have been
implentation-defined behaviour. On the vast majority of machines int * and
short *s are the same and the cast is a no op. However it is just possible
that the short * throws some bits away which an int * needs, so a conforming
implementation could do anything it wants in response.
 
R

Richard Tobin

Sourcerer said:
Once I allocate memory,
can I do with it whatever I want (as in the example, use memory allocated as
char *, as memory where I store integers)?

You didn't allocate it as char *:
a = (char *) malloc(2 * sizeof(int));

You just allocated some memory, and (unnecessarily) cast it to char *
before assigning it to a. The memory returned by malloc() doesn't
have a type.

You can store whatever you like in it, provided that it fits and you
respect the alignment rules.
x = (int *) a;

Fine.

But you have a mistake here:
y = (int *) a + sizeof(int);

You have converted a to an integer pointer, then added sizeof(int) to
it. If sizeof(int) is 4, you have just added four int sizes to it,
i.e. 16 bytes. Adding to a pointer adds in units of the size of the
pointed-to object, so you want

y = (int *) (a + sizeof(int));
or
y = (int *) a + 1;

-- Richard
 
R

Richard Tobin

Malcolm McLean said:
You are playing fast and loose with the C type system.

No, apart from his mistake with the addition, he is using pointers
correctly.
Technically I think you are OK because pointers to char can be converted to
any other type. This is a hangover from the days when there were no void
pointers, so char *s were used instead.

void * has replaced char * as the generic pointer type, but you can't
do arithmetic on void * pointers. char * (or unsigned char *) is the
type used to access memory as bytes, which are the units of C storage.

C guarantees that void * and char * have the same representation, but
the OP is not relying on that.

-- Richard
 
K

Keith Thompson

Sourcerer said:
Does any of the following constitute undefined behavior? Once I
allocate memory, can I do with it whatever I want (as in the example,
use memory allocated as char *, as memory where I store integers)?

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

int main(void) {
char *a;
int *x, *y;
a = (char *) malloc(2 * sizeof(int));
// here's the part I'm wondering about
x = (int *) a;
y = (int *) a + sizeof(int);
*x = 10;
*y = 20;
printf ("%d %d\n", *x, *y);
return 0;
}

Apart from the addition of sizeof(int), which actually adds
sizeof(int)*sizeof(int) bytes to the pointer, your code looks ok as
far as I can tell. But it could be simplified considerably (and made
more robust):

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

int main(void)
{
int *x, *y;
x = malloc(2 * sizeof *x);
if (x == NULL) {
fprintf(stderr, "malloc failed\n");
exit(EXIT_FAILURE);
}
y = x + 1;
*x = 10;
*y = 20;
printf ("%d %d\n", *x, *y);
return 0;
}
 
S

Sourcerer

Richard Tobin said:
You didn't allocate it as char *:


You just allocated some memory, and (unnecessarily) cast it to char *
before assigning it to a. The memory returned by malloc() doesn't
have a type.

Why is the cast unnecessary? malloc() returns void *
You can store whatever you like in it, provided that it fits and you
respect the alignment rules.


Fine.

But you have a mistake here:

Sorry, this should've been y = (int *) (a + sizeof(int));

--
"It is easy in the world to live after the world's opinion; it easy in solitude
to live after our own; but the great man is he who in the midst of the crowd
keeps with perfect sweetness the independence of solitude."
Ralph Waldo Emerson, Self-reliance 1841
http://pinpoint.wordpress.com/
 
M

Martin Ambuhl

Sourcerer said:
Does any of the following constitute undefined behavior? Once I allocate
memory, can I do with it whatever I want (as in the example, use memory
allocated as char *, as memory where I store integers)?

Please try to indent code so it is readable. Luckily, your example is
small enough this time, so we can proceed with cleaning that up.
#include <stdio.h>
#include <stdlib.h>

int main(void) {
char *a;
int *x, *y;
a = (char *) malloc(2 * sizeof(int));
^^^^^^^^
This C++-ism is unnecessary and bad practice in C.
// here's the part I'm wondering about
^^
C++-style comments introduced with '//' will not work with C89
compilers, and are a bad idea for code that you post, since your only
protection against line-wrap problems is keeping comments short, as you
have done.
x = (int *) a;

Because malloc returns space suitable aligned for any type, the above
will fly, but
y = (int *) a + sizeof(int);

Did you check to see where this actually pointed? (int *)a is an int
pointer; the next int will be at (int *)a + 1.
*x = 10;
*y = 20;

You may not own the place y currently points.
printf ("%d %d\n", *x, *y);
return 0;
}

Try this modified version of your code to see what the above comments
reflect:
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
char *a;
int *x, *y;
a = (char *) malloc(2 * sizeof(int));

x = (int *) a;

printf("The pointer-to-char a contains: %p\n"
"The pointer-to-int x = (int *)a contains: %p\n"
"The value of (int *)a + sizeof(int) is %p\n"
"The value of (int *)a + 1 is %p\n",
(void *) a, (void *) x, (void *) ((int *) a + sizeof(int)),
(void *) ((int *) a + 1));

y = (int *) a + sizeof(int);
*x = 10;
#if 0
/* I am not even going to try to assign to *y, as assigned above */
*y = 20;
/* or try to print its value */
printf("%d %d\n", *x, *y);
#endif
/* but this would be ok */
y = x + 1;
*y = 20;
printf("%d %d\n", *x, *y);

return 0;
}


[The results on one implementation]
The pointer-to-char a contains: 20d90
The pointer-to-int x = (int *)a contains: 20d90
The value of (int *)a + sizeof(int) is 20da0
The value of (int *)a + 1 is 20d94
10 20
 
J

Jack Klein

You are playing fast and loose with the C type system.

No, he is not, although he is risking undefined behavior if the
allocation fails and malloc() returns a null pointer, and as others
have pointed out his arithmetic for calculating the value to assign to
'y' is incorrect unless he happens to be on a platform where
sizeof(int) is 1.
It is there to help you avoid alignment problems and other nasties, however
for backwards compatibility and certain low-level operations, C also gives
you the freedom to mess about with it.
Technically I think you are OK because pointers to char can be converted to
any other type. This is a hangover from the days when there were no void

malloc() returns a pointer to void, and pointers to void and to char
are guaranteed to have the same size and representation. So assigning
the returned address to a pointer to char does not change it at all.
 
B

Barry Schwarz

Why is the cast unnecessary? malloc() returns void *

Because in C there is defined an implicit conversion in either
direction between a pointer to void and any other pointer to object
type.


Remove del for email
 
M

Malcolm McLean

Richard Tobin said:
C guarantees that void * and char * have the same representation, but
the OP is not relying on that.
Yes he is.
He is converting from void * to type x * to int *.
This only works if type x is a char *. Otherwise the type x * could throw
away information contained in the void * which is needed by the int *.
Very unlikely, but it is allowed.
 
R

Richard Tobin

C guarantees that void * and char * have the same representation, but
the OP is not relying on that.
[/QUOTE]
Yes he is.
He is converting from void * to type x * to int *.
This only works if type x is a char *.

It does not rely on void * and char * haaving the same representation,
just that you can convert any pointer to and from char * safely.

-- Richard
 
G

Guest

Yes he is.
He is converting from void * to type x * to int *.
This only works if type x is a char *.

It does not rely on void * and char * haaving the same representation,
just that you can convert any pointer to and from char * safely.[/QUOTE]

It doesn't even rely on that. You can safely convert any result from
malloc to and from any non-function pointer type. The only time pointer
casts can lose information (or worse) is if the pointer is not properly
aligned, but the pointer is the result from a call to malloc, so it
must be properly aligned.
 
M

Malcolm McLean

Harald van D?k said:
It doesn't even rely on that. You can safely convert any result from
malloc to and from any non-function pointer type. The only time pointer
casts can lose information (or worse) is if the pointer is not properly
aligned, but the pointer is the result from a call to malloc, so it
must be properly aligned.
You are confusing what will happen on any current implementation with what
is guaranteed by the standard.
Currently any exta bits in a pointer will represent an offset, so a cast
back can reconstruct them as zero. However it is not guaranteed that this is
the only purpose of extra bits.
 
G

Guest

Malcolm said:
You are confusing what will happen on any current implementation with what
is guaranteed by the standard.

The standard is a bit underspecified regarding pointer conversions, but
does clearly address this part in 6.3.2.3p7: "A pointer to an object or
incomplete type may be converted to a pointer to a different object or
incomplete type. If the resulting pointer is not correctly aligned57)
for the pointed-to type, the behavior is undeï¬ned. Otherwise, when
converted back again, the result shall compare equal to the original
pointer."
 
M

Malcolm McLean

Harald van D?k said:
You are confusing what will happen on any current implementation with what
is guaranteed by the standard.

*The standard is a bit underspecified regarding pointer conversions, but
*does clearly address this part in 6.3.2.3p7: "A pointer to an object or
*incomplete type may be converted to a pointer to a different object or
*incomplete type. If the resulting pointer is not correctly aligned57)
*for the pointed-to type, the behavior is unde?ned. Otherwise, when
*converted back again, the result shall compare equal to the original
*pointer."

You are actaully correct, because of that that phrase "correctly aligned".
As you say, it is a bit under-specified. At the moment all pointers consist
of hardware (1)address plus, occasionally, alignment bits to implement
sub-hardware byte chars. That might change because of technical developments
in the future, though I couldn't think of an obvious scenario.
(1) for the pedants, sometimes a translated hardware address.
 

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,780
Messages
2,569,608
Members
45,241
Latest member
Lisa1997

Latest Threads

Top