[comp.std.c] C question

mhampson@wpi.wpi.edu (Mark A. Hampson) (11/07/89)

I have a question that I have not been able to find an answer that I feel 
comfortable with regarding the following situation:

I am attempting to allocate a block of memory as follows:

int   m = 10;
int   n = 10;
void *block;
int   block_siz;

block_siz = 2*sizeof(int) + m*n*sizeof(double);
block = malloc(block_siz);

simple enough...

I now wish to put two integers at the begining of this block of memory:

block = m;
(block+sizeof(int)) = n;   <---  here is where I am running into probs.

My intention is to store m in the first int sized space in block and to 
store n in the int sized space after it.

The compiler that I am using tells me that it does not know the size of 
block and therefore cannot perform the operation block+sizeof(int).  This
makes some sence as block is of type void *.  That being the case, how can 
I perform the operation that I want to?

Thanks in advance:
Mark A. Hampson

-- 
/----------------------------------------------------------------------------\
|      We are not in an energy crisis, we are having an entropy crisis...    |
\----------------------------------------------------------------------------/

bjal_cif@uhura.cc.rochester.edu (Ben Alexander) (11/15/89)

Followups have been redirected to comp.lang.c

In article <5322@wpi.wpi.edu> mhampson@wpi.wpi.edu (Mark A. Hampson) writes:
>I have a question that I have not been able to find an answer that I feel 
>comfortable with regarding the following situation:
>
>I am attempting to allocate a block of memory as follows:
>
>int   m = 10;
>int   n = 10;
>void *block;
>int   block_siz;
>
>block_siz = 2*sizeof(int) + m*n*sizeof(double);
>block = malloc(block_siz);
>
>simple enough...
>
>I now wish to put two integers at the begining of this block of memory:
>
>block = m;
>(block+sizeof(int)) = n;   <---  here is where I am running into probs.

Actually, the line before this is the one that *should* be giving you
problems.  When you say "block = m;" you are telling the complier to make
block point to the address 10.  This means the the space allocated by
malloc is no longer accesible and that you will be accessing (probably)
sensitive areas of memory.  Bad Juju.  I would think that the complier
would give you a warning to the effect of "Non-portable pointer
assignment".

>My intention is to store m in the first int sized space in block and to 
>store n in the int sized space after it.
>
>The compiler that I am using tells me that it does not know the size of 
>block and therefore cannot perform the operation block+sizeof(int).  This
>makes some sence as block is of type void *.  That being the case, how can 
>I perform the operation that I want to?

To do what you want to do you should use the following code:
*((int *) block) = m;
*((int *) block + 1) = n;

The first line assigns the correct value to the first int-sized chunk of
block.  The (int *) typecast gives the complier the size of what block
points to, so it can do the assignment.

In the next line, the addition actually increments block to the next int
as opposed to adding 1 byte to block (that's what the typecast is for).
The indirection of the whole left-hand expression tells the compiler to
store n in the address of block started one int later.

>Thanks in advance:
>Mark A. Hampson

No problem.

Ben Alexander

P.S. I don't read comp.lang.c, so if you want to comment on this, please
e-mail me a copy of your post. 

chris@mimsy.umd.edu (Chris Torek) (11/15/89)

In article <5322@wpi.wpi.edu> mhampson@wpi.wpi.edu (Mark A. Hampson) writes:
[edited slightly]
>int   m = 10, n = 10, block_siz;
>void *block;
>block_siz = 2*sizeof(int) + m*n*sizeof(double);
>block = malloc(block_siz);

>I now wish to put two integers at the begining of this block of memory:
>
>block = m;
>(block+sizeof(int)) = n;   <---  here is where I am running into probs.
>
>My intention is to store m in the first int sized space in block and to 
>store n in the int sized space after it.

You could write:

	((int *)block)[0] = m;
	((int *)block)[1] = n;

or any equivalent combination.  This code is valid, if a bit twisty.
However, I am guessing from the value in block_siz that you then intend
to put m*n `double's in the memory region after the first two int-sized
blocks.  This is not easy, because that region may not be properly
aligned for a double.  Consider, e.g., a machine with 4-byte ints,
16-byte doubles, and 16-byte data paths, that requires 16-byte objects
to be aligned on a 16-byte boundary.  If you take the obvious approach:

	int *iptr = block;
	double *dptr = (double *)&iptr[2];
	iptr[0] = m;
	iptr[1] = n;
	dptr[0] = 1.234;

the assignment to dptr[0] will cause a run-time alignment error fault.

There is an almost-portable solution:

	struct mat2 {		/* 2 dimensional dynamic matrix */
		int	m;
		int	n;
		double	data[1];/* actually larger */
	};

	int m = 10, n = 10;	/* as before */
	size_t sz = sizeof(struct mat2) + (m * n - 1) * sizeof(double);
	register struct mat2 *mat;
	register double *d;

	if ((mat = malloc(sz)) == NULL) {
		/* do not know what printf format to use for `size_t',
		   hence the cast to unsigned long */
		panic("cannot allocate %lu bytes for matrix",
		    (unsigned long)sz);
		/* NOTREACHED */
	}
	mat->m = m;
	mat->n = n;
	d = mat->data;
	for (i = m * n; i != 0; i--)
		*d++ = 0.0;

The `sizeof(struct mat2)' includes any padding that must be inserted
between the second `int' object and the `double's that will go there.
m*n is the number of doubles, but one is already included in the
structure, so we have to subtract that off before multiplying by
sizeof(double).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

cpcahil@virtech.uucp (Conor P. Cahill) (11/15/89)

In article <5322@wpi.wpi.edu>, mhampson@wpi.wpi.edu (Mark A. Hampson) writes:
> void *block;
> block = malloc(block_siz);
> 
> simple enough...
> 
> I now wish to put two integers at the begining of this block of memory:
> 
> block = m;
> (block+sizeof(int)) = n;   <---  here is where I am running into probs.

You need to do the following:

	*((int *) block)  = m;
	*(((int *) block)+1) = n;

Note that this can cause core dumps if block does not point to a correctly
aligned address.  Your example used malloc to get the correct address, so
it will work properly.

Another solution would be to declare block as follows:

int * block;
block = (int *) malloc(...);
block[0] = m;

However this would give you problems when accessing the doubles.

A much cleaner solution would be to do the following:

struct t 
{
	int	m;
	int	n;
	double	d[1];
} * block;

int m = 10;
int n = 10;
int block_siz;

block_siz = sizeof(struct t) + ((m*n)-1)*sizeof(double);

block = (struct t *) malloc(block_siz);

Now you will be able to access your data as follows:

	block->m = m;
	block->n = n;
	block->d[15] = 15.23;
	block->d[m*n-1] = 16.32;



-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

peter@ficc.uu.net (Peter da Silva) (11/17/89)

How about this:

union padding {
	int mn[2];
	double d;
};

	block = malloc(sizeof (union padding) + whatever * sizeof (double));

	((int *)block)[0] = m;
	((int *)block)[1] = n;
	start_of_double_array = &((union padding *)block)[1];
-- 
`-_-' Peter da Silva <peter@ficc.uu.net> <peter@sugar.hackercorp.com>.
 'U`  --------------  +1 713 274 5180.
"vi is bad because it didn't work after I put jelly in my keyboard."
   -- Jeffrey W Percival (jwp@larry.sal.wisc.edu)