[comp.lang.c] FAQ - malloc array - clarify

jdm5548@diamond.tamu.edu (James Darrell McCauley) (09/07/90)

Could someone lend a hand? Topic is FAQ #19, allocating memory
for an array. The problem is that 'lint' doesn't like what I'm 
doing.
Here's some source:
#include<stdio.h>
#include<malloc.h>
main()
{
  int i, nrows=10, ncolumns=10;
  double **array;
 
  array = (double **)malloc(nrows * ncolumns * sizeof(double *));
  for(i = 0; i < nrows; i++)
    array[i] = (double *)malloc(ncolumns * sizeof(double));
}
/* 
Code from answer to #19 in FAQ list (dated Aug 3 by 'ls -l'):

int **array = (int **)malloc(nrows * ncolumns * sizeof(int *));
         for(i = 0; i < nrows; i++)
                 array[i] = (int *)malloc(ncolumns * sizeof(int));

*/
/* output from lint:
test.c(8): warning: possible pointer alignment problem
test.c(10): warning: possible pointer alignment problem
malloc, arg. 1 used inconsistently	llib-lc(383)  ::  test.c(8)
malloc, arg. 1 used inconsistently	llib-lc(383)  ::  test.c(10)
*/

Why am I getting these warnings?  I get them using lint that 
(I assume) is bundled with the OS.  SunOS Release 4.0.3c on a Sparc.
If I run them on my Sun 3/60 with SunOS Release 4.0.3, I don't
get the warnings. ('diff "4.0.3" "4.0.3c"' ?)  Do I have a broken
lint on the Sparc?

Should the argument to malloc be cast (unsigned) (and FAQ list changed)?
If I make this change, lint doesn't scream about arguments being used
incorrectly.

Darrell McCauley

cpcahil@virtech.uucp (Conor P. Cahill) (09/08/90)

In article <8056@helios.TAMU.EDU> jdm5548@diamond.tamu.edu (James Darrell McCauley) writes:
>  int i, nrows=10, ncolumns=10;
>  double **array;
> 
>  array = (double **)malloc(nrows * ncolumns * sizeof(double *));
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
While this has nothing to do with your lint problems, I think this should be:

   array = (double **)malloc(nrows * sizeof(double *));

>  for(i = 0; i < nrows; i++)
>    array[i] = (double *)malloc(ncolumns * sizeof(double));
>}
>/* 
>/* output from lint:
>test.c(8): warning: possible pointer alignment problem
>test.c(10): warning: possible pointer alignment problem

This you will always get from lint.  The problem is that malloc is defined
as returning a pointer to char which has looser alignment requirements than
pointer to pointer.  Since malloc guarrantees that the result is suitably 
alligned for all data types, you can ignore this message.

>malloc, arg. 1 used inconsistently	llib-lc(383)  ::  test.c(8)
>malloc, arg. 1 used inconsistently	llib-lc(383)  ::  test.c(10)

This is caused by the argument to malloc being unsigned, not signed.  A
cast in your code will fix this.




-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

chris@mimsy.umd.edu (Chris Torek) (09/09/90)

In article <1990Sep08.022034.8444@virtech.uucp> cpcahil@virtech.uucp
(Conor P. Cahill) writes:
[re the lint error from `ptr = malloc(expr * sizeof(sometype))':
 malloc, arg. 1 used inconsistently]

>This is caused by the argument to malloc being unsigned, not signed.  A
>cast in your code will fix this.

Note that the cast is only required in old, broken compilers where the
result of `sizeof' is an int instead of a size_t (typically unsigned
int).  These compilers are remarkably widespread (and, unfortunately,
fixing them breaks some existing code, which is why I have not fixed
ours here yet).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

hp@vmars.tuwien.ac.at (Peter Holzer) (09/10/90)

cpcahil@virtech.uucp (Conor P. Cahill) writes:

>In article <8056@helios.TAMU.EDU> jdm5548@diamond.tamu.edu (James Darrell McCauley) writes:

>   array = (double **)malloc(nrows * sizeof(double *));

>>  for(i = 0; i < nrows; i++)
>>    array[i] = (double *)malloc(ncolumns * sizeof(double));

>>malloc, arg. 1 used inconsistently	llib-lc(383)  ::  test.c(8)
>>malloc, arg. 1 used inconsistently	llib-lc(383)  ::  test.c(10)

>This is caused by the argument to malloc being unsigned, not signed.  A
>cast in your code will fix this.

No! Don't fix your code, which is correct. Fix the lint library.
The argument to malloc should be the type returned by sizeof (): size_t
(an unsigned integral type).
--
|    _	| Peter J. Holzer			| Think of it	|
| |_|_)	| Technische Universitaet Wien		| as evolution	|
| | |	| hp@vmars.tuwien.ac.at			| in action!	|
| __/  	| ...!uunet!mcsun!tuvie!vmars!hp	|     Tony Rand	|

cpcahil@virtech.uucp (Conor P. Cahill) (09/11/90)

In article <1803@tuvie> hp@vmars.tuwien.ac.at (Peter Holzer) writes:
>cpcahil@virtech.uucp (Conor P. Cahill) writes:
>
>>This is caused by the argument to malloc being unsigned, not signed.  A
>>cast in your code will fix this.
>
>No! Don't fix your code, which is correct. Fix the lint library.
>The argument to malloc should be the type returned by sizeof (): size_t
>(an unsigned integral type).

The argument to malloc is defined in the library (i.e. where malloc()
is encoded).  running lint against the source builds the lint library and
therefore the lint library will correctly reflect what the code has (besides,
without code, you can rebuild a lint library).

The problem that the original poster ran into is that on his system
the type of sizeof() is an integer.  However, the type of malloc's 
argument is an unsigned integer.

-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

hp@vmars.tuwien.ac.at (Peter Holzer) (09/11/90)

cpcahil@virtech.uucp (Conor P. Cahill) writes:

>In article <1803@tuvie> hp@vmars.tuwien.ac.at (Peter Holzer) writes:
>>cpcahil@virtech.uucp (Conor P. Cahill) writes:
>>
>>>This is caused by the argument to malloc being unsigned, not signed.  A
>>>cast in your code will fix this.
>>
>>No! Don't fix your code, which is correct. Fix the lint library.
>>The argument to malloc should be the type returned by sizeof (): size_t
>>(an unsigned integral type).

>The argument to malloc is defined in the library (i.e. where malloc()
>is encoded).  running lint against the source builds the lint library and
>therefore the lint library will correctly reflect what the code has (besides,
>without code, you can rebuild a lint library).

The lint libraries are ASCII files on our system. Are they in a compiled 
form on yours ?

>The problem that the original poster ran into is that on his system
>the type of sizeof() is an integer.  However, the type of malloc's 
>argument is an unsigned integer.

We have the same problem on our machines (DECstations, Ultrix 2.1, cc 1.31),
but I use gcc, so I didn't run across it before:

In <sys/types.h>:
	typedef int size_t;	/* returned by sizeof */
				/* this is a compiler bug.
				   should be unsigned	*/
In /usr/lib/lint/llib-lc:
	char *    malloc(n) unsigned n; {static char c; return(&c);}

Exactly the situation described. Now if I want to shut up lint about
foop = malloc (sizeof (struct foo));
what should I cast the argument to malloc to?
*   Unsigned is not a good choice. On the next system I want to port my program
    to, malloc might be declared char * malloc (unsigned long n); and if int
    and long are different sizes I might not get what I want. 
*   Unsigned long is even worse. It might not even work on the machine I use
    now.
*   Size_t is the type I have already.
*   Any other ideas ?

So what I propose is the following:
Check if the non-negative values of int have the same representation as the 
corresponding values of unsigned ints (This is true for all machines I know,
and is guarantueed for ANSI C). If this holds, it does not matter if you are
passing a non-negative int or an unsigned to a function expecting unsigned.

Because of this you can change the above line in llib-lc to:
char *    malloc(n) int n; {static char c; return(&c);}

I know this is a hack, but it is a hack that does not force you to write
non-portable programs, as the cast-workaround does.

(The other possibility is not to use lint, which I am forced to do, because
lint gags on my prototypes, but in general, I would not recommend that).
--
|    _	| Peter J. Holzer			| Think of it	|
| |_|_)	| Technische Universitaet Wien		| as evolution	|
| | |	| hp@vmars.tuwien.ac.at			| in action!	|
| __/  	| ...!uunet!mcsun!tuvie!vmars!hp	|     Tony Rand	|

karl@haddock.ima.isc.com (Karl Heuer) (09/12/90)

In article <1806@tuvie> hp@vmars.tuwien.ac.at (Peter Holzer) writes:
>cpcahil@virtech.uucp (Conor P. Cahill) writes:
>>>No! Don't fix your code, which is correct. Fix the lint library...
>>The argument to malloc is defined in the library (i.e. where malloc()
>>is encoded).  running lint against the source builds the lint library and
>>therefore the lint library will correctly reflect what the code has (besides,
>>without code, you can rebuild a lint library).
>
>The lint libraries are ASCII files on our system. Are they in a compiled 
>form on yours ?

Common practice on modern machines is to supply a compiled form (llib-lc.ln)
as well as the ASCII form (llib-lc).  You probably have both, and just never
noticed.

>[The problem is that sizeof() incorrectly returns a signed int, and size_t is
>defined that way in <sys/types.h>, but malloc() expects an unsigned int.]
>Now if I want to shut up lint about [malloc(sizeof(x))], what should I cast
>the argument to malloc to?

Don't cast it at all!  The idea is to make the code *correct*, not to get rid
of spurious warnings caused by a known compiler/lint bug.

>[You could change the lint library to declare the argument to be int]

This would create a new warning for correct code (malloc(n*sizeof(T)), where n
is an unsigned variable).  Better to fix the compiler, if possible.

>(The other possibility is not to use lint, which I am forced to do, because
>lint gags on my prototypes, but in general, I would not recommend that).

Lint is commonly implemented as a shell script.  Find the place where it does
something like "cpp | lint1", and insert a filter that converts ANSI-isms to
forms that are acceptable to K&R-based lint.  (I currently use a sed script to
change "void *" to "char *", and a deprotoizer that's tuned to my personal
coding style.)

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint

chris@mimsy.umd.edu (Chris Torek) (09/12/90)

In article <1806@tuvie> hp@vmars.tuwien.ac.at (Peter Holzer) writes:
[part of an aside:]
>The lint libraries are ASCII files on our system. Are they in a compiled 
>form on yours ?

Yes; they are built from ASCII files which are also installed.  The
situation differs under some versions of System V.

>In <sys/types.h>:
>	typedef int size_t;	/* returned by sizeof */
>				/* this is a compiler bug.
>				   should be unsigned	*/

The `size_t' you find in <sys/types.h> is *NOT* the `size_t' meant by
ANSI C.  <sys/types.h>'s `size_t' is the `size of a text, data, or
stack segment in clicks', not the `size of a contiguous memory block
in bytes'.  They merely (unfortunately) happen to have the same name.

In 4.3BSD-reno, the `size_t' in <sys/types.h> has been renamed `segsz_t'
and a proper (unsigned) size_t is defined in the proper ANSI <std*.h>
files.

One can fix this on binary-only Unix systems by changing that one line
in <sys/types.h> to read

	typedef int segsz_t;
	#ifdef KERNEL
	#define size_t segsz_t
	#endif

Unfortunately, you have to remember to reinstall this change with every
system upgrade until your vendor catches on.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

hp@vmars.tuwien.ac.at (Peter Holzer) (09/12/90)

karl@haddock.ima.isc.com (Karl Heuer) writes:

>In article <1806@tuvie> hp@vmars.tuwien.ac.at (Peter Holzer) writes:
>>The lint libraries are ASCII files on our system. Are they in a compiled 
>>form on yours ?

>Common practice on modern machines is to supply a compiled form (llib-lc.ln)
>as well as the ASCII form (llib-lc).  You probably have both, and just never
>noticed.

Yes. Now that you told me, I found them.

>>cpcahil@virtech.uucp (Conor P. Cahill) writes:
>>>In article <1803@tuvie> hp@vmars.tuwien.ac.at (Peter Holzer) writes:
>>>>cpcahil@virtech.uucp (Conor P. Cahill) writes:
>>>>>This is caused by the argument to malloc being unsigned, not signed.  A
>>>>>cast in your code will fix this.
>>>>No! Don't fix your code, which is correct. 
>>[The problem is that sizeof() incorrectly returns a signed int, and size_t is
>>defined that way in <sys/types.h>, but malloc() expects an unsigned int.]
>>Now if I want to shut up lint about [malloc(sizeof(x))], what should I cast
>>the argument to malloc to?
>> [Various types and why they don't work deleted]

>Don't cast it at all!  The idea is to make the code *correct*, not to get rid
>of spurious warnings caused by a known compiler/lint bug.

Just what I said.

>>[You could change the lint library to declare the argument to be int]

>This would create a new warning for correct code (malloc(n*sizeof(T)), where n
>is an unsigned variable).  Better to fix the compiler, if possible.

Ok, I hereby withdraw my proposal.
New proposal:
	Complain to your compiler/lint vendor and wait for a new
	release.

>>(The other possibility is not to use lint, which I am forced to do, because
>>lint gags on my prototypes, but in general, I would not recommend that).

>Lint is commonly implemented as a shell script.  Find the place where it does
>something like "cpp | lint1", and insert a filter that converts ANSI-isms to
>forms that are acceptable to K&R-based lint.  (I currently use a sed script to
>change "void *" to "char *", and a deprotoizer that's tuned to my personal
>coding style.)

I will try it. My deprotoizer is a little less strict about white space
than yours (but twice the size), but it doesn't handle pointers to
functions either, so there will be some programs where it won't work.
If it does work in most cases I'll be happy, though.

The best solution would be a GNU lint, which does understand ANSI C and
GNU extensions (e.g. volatile void exit (int);).

PS: I like "Heuer's Law". If I hadn't seen it in some other
guy's signature already, I would have put it into mine.

--
|    _	| Peter J. Holzer			| Think of it	|
| |_|_)	| Technische Universitaet Wien		| as evolution	|
| | |	| hp@vmars.tuwien.ac.at			| in action!	|
| __/  	| ...!uunet!mcsun!tuvie!vmars!hp	|     Tony Rand	|

dricejb@drilex.UUCP (Craig Jackson drilex1) (09/14/90)

A poster recently wondered why a statement like

double *ptr = (double *) malloc(ncols * sizeof(double));

gave lint complaints about the argument to malloc().

People here have been blathering on about broken compilers, erroneous lint
libraries, etc, thinking that 'sizeof()' is returning an int on that machine.

Wouldn't a more reasonable explanation be that 'ncols' is an int, and that
particular compiler treats 'int * unsigned' as 'int' (or 'long int')?  
I don't have all of the rules about value-preserving vs 
unsignedness-preserving burned into my head, but such an explanation 
sure makes sense to me...
-- 
Craig Jackson
dricejb@drilex.dri.mgh.com
{bbn,axiom,redsox,atexnet,ka3ovk}!drilex!{dricej,dricejb}

karl@haddock.ima.isc.com (Karl Heuer) (09/15/90)

In article <15495@drilex.UUCP> dricejb@drilex.UUCP (Craig Jackson drilex1) writes:
>People here have been blathering on about broken compilers, erroneous lint
>libraries, etc, thinking that 'sizeof()' is returning an int on that machine.
>
>Wouldn't a more reasonable explanation be that 'ncols' is an int, and that
>particular compiler treats 'int * unsigned' as 'int' (or 'long int')?  

There is a well-known implementation where sizeof() returns a signed int.
(Understandable, since K&R 1 didn't say.)  I've never heard of one that thinks
int*uint is signed.  (Which would be harder to defend, since both Classic and
ANSI C agree that it isn't.)

Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint