By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
457,888 Members | 1,236 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 457,888 IT Pros & Developers. It's quick & easy.

LP64 FAQ tricks/tips/traps.

P: n/a
Hi,

Below is a collection of tips/tricks/caveats for LP64 c coding,
full text is at http://www.cs.albany.edu/~mosh/Text/c-ref.txt.

Hope it helps, corrections welkome.
-- Panchi51<et>pacbell.net

LP64 Gotcha List
----------------

1. sizeof long array can overflow on lp64.
uchar n = sizeof( 50*sizeof(long)); // 200 bytes in ilp32, 400
bytes in lp64.

2. Use u U L l suffix on large const.
long l = 1 <<32; // 0 on ilp32,lp64
long l = 1L<<32; // 2^32 on lp64, 0 on ilp32.

Solaris: see /usr/include/sys/inttypes.h
INT64_C(5) => 5ll for ILP32, and 5l for LP64.

0x80000000L ulong in lp32, long in lp64.

3. Bit fields are converted to int and sign-extended (or uint if too
large) in expr.
struct { uint b:19, r:13 }x;
uint y = x.b<<13 // x.b first converted to int and sign extended!
uint y = (uint)x.b << 13; // ok.

4. Integer Arithmetic on ptr, ptr is trucated to int32.
char *p="x";
(int)p+1=='\0' // p is trunc to int32.
(uintptr_t)p+1=='\0' // ok.

5. Unprotyped func returning ptr is trucated to int32.
// int login(...); -- implicit C decl.
char *p = getenv("HOME"); // p is trucated to int32, crash.

6. Saving ptr in int, losses upper 4 bytes of ptr.
void *q;
int p = (int)q; // truncated to int32

7. Unions with long.
union {
long x;
int y; // on lp64, y gets lsb of x.
}

8. Print format specifier
char *p="x";
printf("p=0x%08x",p); // losses msb.
printf("p=%p", p); // ok.

long x=LONG_MAX;
printf("x=%d ",x); // truncated, wrong.
printf("x=%ld",x); // ok.

9. Casting *int into *long.
int *i;
long *p;
p+1; // p+=4 on ilp32
p+1; // p+=8 on lp64
p = (long*) i; // p misaligned pointing to *i and *(i+1).
i = ( int*) p; // i pointing to lsb of p.

10. Unsigned bit fields (default sign and alignment).
struct { b:12, int:0, d:5 } x; // b is unsigned in lp64, signed
in ilp32.
// :0 affects alignment in ilp32, no effect in lp64.

11. LP64 Perf Problems
Larger exe, need more TLB, cache, mem (wrt same src compiled as
ILP32).
Long div slow in lp64, eg. 1L/9.
Int Array indexes are sign-ext on every use, need more instruction
in LP64, eg. int i; a[i];

12. Expression sizing:

Example.
int x,y;
long z = x + y; // x+y done in 32bit, then assignment will resize
result for z.

Example.
ulong y = (1 <<32); // overflows in lp32 and lp64.
ulong y = (1L<<32); // overflows in lp32, ok 2^32 in lp64.
13. Promotions:

result = a op b;
// 1. type of tmp is of max of a and b.
// 2. tmp = a op b
// 3. promote tmp to type of result, ptmp.
// 4. result = ptmp;

Example.
long l=-1; uint i=1;
if( l > i ) printf("ilp32"); // -1 is promoted to uint32.
else printf("lp64"); // both promoted to signed 64.

if( l > (long)i ) // fix
...;

Example.
main(){ // k is -1 in LP32, 4294967295 in LP64
int i = -2;
unsigned int j = 1;
long k = i+j;
printf("int %d+ uint %ld= long k is %ld\n",i,j,k);
}

15. Prototype:
// #include <stdlib.h> missing.
int *i = malloc(sizeof(*i));
// Ok in lp32, Crash on lp64 because
// malloc will return 32bit int value because of missing
prototype.
}

////////////////////////////////////////////////////
Examples
cc +DA2.0W +w1 +M2 enquire.c # +w1 for warnings, +M2 for 64bits

long double=128 bits

# Sort LP64 warning by problem: make 2>&1 | grep LP64 | xsort 'warning:\s+\S+:' | align warn > lp64.log vim lp64.log :set isk+=46 " Make . part of tag, so file.c is a keyword for
tags"

emacs lp64.log
M-x compilation-minor-mode
M-x next-error ; if cant find files, set
; (setq compilation-search-path '("dir1" "dir2")) C-xC-e

/714: Function "malloc" called with no prototype or definition in
scope
/724: Initialization converts default int return type to pointer

/722:.*cast converts 32 bit constant to pointer
/725:.*cast converts 32 bit integer to pointer
/727:.*cast truncates pointer into 32 bit integer
/729:.*converts int* to long*
/732:.*different types treated as unsigned
/740:.*casting to a smaller size may truncate
/530:.*casting from loose to strict alignment
/720:.*Assignment may overflow
// Example
catt -n x.c


1 void main(){
2 int *x, *q, i;
3 long l=-1;
4 x = malloc(sizeof(int)*10);
5 (int*)i;
6 (int) &i;
7 (long*)&i;
8 i = l;
9 }

hp64> cc +DA2.0W +M2 +w1 -Aa x.c

/*
line 4: warning 714: Function "malloc" called with no prototype or
definition in scope.
line 4: warning 724: LP64 migration: Assignment converts default int
return type to pointer "x".
line 5: warning 725: LP64 migration: Cast converts 32 bit integer to
pointer.
line 6: warning 727: LP64 migration: Cast truncates pointer into 32
bit integer.
line 7: warning 530: LP64 migration: Casting from loose to strict
alignment: Resulting pointer may be misaligned.
line 7: warning 729: LP64 migration: Cast converts int* to long*.
line 8: warning 720: LP64 migration: Assignment may overflow integer
"i".
*/

/////////////////////////////////////////////////
A. Sizes
Ansi sizeof() is always unsigned, try:
if( 0-sizeof(int) > 0 ) printf("See: 0-sizeof(int) is
unsigned!\n");
ILP32: win32 hp11 solaris linux

ANSI C Integer Requirements, see C book. moshtag=ansi_int
sizeof(int) >= sizeof(short) >= 2.
sizeof(long) >=sizeof(int)
sizeof(long) >= 4.

LP64: hp11_64 solaris_64 (assume default for 64bit).
New: sizeof(long)==sizeof(void*)==8, rest unchanged.

P64: win64
New: sizeof(void*)==8, rest unchanged.

//////////////////////////////////////////////////////////

Linux64 moshtag=linux64
char = 8 bits, signed
short=16 int=32 long=64 float=32 double=64 bits
long double=128 bits
Type size_t is unsigned long
ALIGNMENTS
char=1 short=2 int=4 long=8 float=4 double=8 long double=16
char*=8 int*=8 func*=8
short: BA
int: DCBA
long: HGFEDCBA
Maximum long = 9223372036854775807 (= 2**63-1)
Minimum long = -9223372036854775808
Maximum unsigned short = 65535
Maximum unsigned int = 4294967295
Maximum unsigned long = 18446744073709551615
PROMOTIONS
unsigned short promotes to signed int
long+unsigned gives signed long

HP64 ALIGNMENTS
char=1 short=2 int=4 long=8 float=4 double=8 long double=16
char*=8 int*=8 func*=8

//////////////////////////////////////////////////////////
Nov 13 '05 #1
Share this Question
Share on Google+
2 Replies


P: n/a
On 13 Jul 2003 15:20:07 -0700, pa******@pacbell.net (Panchi51) wrote
in comp.lang.c:
Hi,

Below is a collection of tips/tricks/caveats for LP64 c coding,
full text is at http://www.cs.albany.edu/~mosh/Text/c-ref.txt.
And it's off-topic. Standard C is independent of the representation
of underlying types, barring certain details specifically stated to be
implementation-defined.
Hope it helps, corrections welkome.
-- Panchi51<et>pacbell.net

LP64 Gotcha List
----------------

1. sizeof long array can overflow on lp64.
uchar n = sizeof( 50*sizeof(long)); // 200 bytes in ilp32, 400
bytes in lp64.
Actually, the result of the expression is sizeof(size_t), since the
integer constant 50 is promoted to the same type as the size_t yielded
by sizeof(long). And assuming "uchar" is a typedef for unsigned char,
it can't overflow by definition. It will wrap around, however, as
defined in the C standard.

Not to mention the fact that, IMHO, anyone who tries to stuff the size
of something into a character type on a 32 or 64 bit platform might do
well to rethink.

2. Use u U L l suffix on large const.
long l = 1 <<32; // 0 on ilp32,lp64
This is undefined behavior on any platform where long has 32 bits. So
the result is meaningless.
long l = 1L<<32; // 2^32 on lp64, 0 on ilp32.
Still undefined behavior of long has 32 bits.
Solaris: see /usr/include/sys/inttypes.h
INT64_C(5) => 5ll for ILP32, and 5l for LP64.

0x80000000L ulong in lp32, long in lp64.

3. Bit fields are converted to int and sign-extended (or uint if too
large) in expr.
struct { uint b:19, r:13 }x;
uint y = x.b<<13 // x.b first converted to int and sign extended!
If an unsigned int bitfield is converted to int and sign-extended when
assigned to an unsigned long, that's a compiler defect, not a language
issue. Contact the compiler vendor.
uint y = (uint)x.b << 13; // ok.

4. Integer Arithmetic on ptr, ptr is trucated to int32.
char *p="x";
(int)p+1=='\0' // p is trunc to int32.
Assuming int has 32 bits, yes the cast to int must truncate to 32
bits. Or, since you are assigning a pointer to an integer type too
small to hold the value, you could get undefined behavior.
(uintptr_t)p+1=='\0' // ok.
That's what these types were defined for.
5. Unprotyped func returning ptr is trucated to int32.
// int login(...); -- implicit C decl.
char *p = getenv("HOME"); // p is trucated to int32, crash.
Prototypes were added to the C language almost 14 years ago. Anyone
calling unprototyped functions today is not a professional. Anyone
invoking a compiler without telling it to emit a warning, or break
compilation, on a call to an unprototyped function is a dangerous
prima donna, not a professional programmer.
6. Saving ptr in int, losses upper 4 bytes of ptr.
void *q;
int p = (int)q; // truncated to int32
Undefined behavior if the pointer has more bits than the int.
Production quality code doesn't do this anyway.
7. Unions with long.
union {
long x;
int y; // on lp64, y gets lsb of x.
}
If you store a value into x and then access it via y, what you get is
undefined behavior.
8. Print format specifier
char *p="x";
printf("p=0x%08x",p); // losses msb.
Passing a pointer to char to printf with any conversion specifier
other than "%s" is undefined behavior anyway.
printf("p=%p", p); // ok.
Passing a pointer to char to printf with any conversion specifier
other than "%s" (and that includes "%p"), is undefined behavior.
long x=LONG_MAX;
printf("x=%d ",x); // truncated, wrong.
Passing a long to printf with a "%d" conversion specifier on any
platform is undefined behavior, even if int and long happen to share
the identical representation.
printf("x=%ld",x); // ok.

9. Casting *int into *long.
int *i;
long *p;
p+1; // p+=4 on ilp32
p+1; // p+=8 on lp64
p = (long*) i; // p misaligned pointing to *i and *(i+1).
i = ( int*) p; // i pointing to lsb of p.
Anyone who puns pointers like this gets what he deserves.
10. Unsigned bit fields (default sign and alignment).
struct { b:12, int:0, d:5 } x; // b is unsigned in lp64, signed
in ilp32.
// :0 affects alignment in ilp32, no effect in lp64.
The code above has been illegal in C since the 1999 update to the
standard, nearly 4 years ago. Implict int is no longer part of the
language.

As for whether bitfields defined as "plain" int are signed or
unsigned, that's implementation-defined. A compiler can do either, so
long as it documents its behavior.

As for this:

struct { int b:12, int:0, int d:5 } x;

....on a compiler where CHAR_BIT is 8, if the compiler does not place
AT LEAST 4 bits between b and d, the compiler is broken. Take this up
with the vendor.

11. LP64 Perf Problems
Larger exe, need more TLB, cache, mem (wrt same src compiled as
ILP32).
Long div slow in lp64, eg. 1L/9.
Int Array indexes are sign-ext on every use, need more instruction
in LP64, eg. int i; a[i];
Performance differences between different compilers are not a language
issue.
12. Expression sizing:

Example.
int x,y;
long z = x + y; // x+y done in 32bit, then assignment will resize
result for z.
An expression involving two ints is always performed at int precision
and always produces an int result. What you may do with the result
afterwards has no bearing on this fact. There's nothing surprising
here, C has worked like this for more than 25 years.
Example.
ulong y = (1 <<32); // overflows in lp32 and lp64.
Undefined behavior if int has 32 bits.
ulong y = (1L<<32); // overflows in lp32, ok 2^32 in lp64.
Undefined behavior if long has 32 bits.
13. Promotions:

result = a op b;
// 1. type of tmp is of max of a and b.
// 2. tmp = a op b
// 3. promote tmp to type of result, ptmp.
// 4. result = ptmp;
Not correct. If a and b are character or short int types, they will
both be promoted to int or unsigned int even though neither of them
was that large to start with.
Example.
long l=-1; uint i=1;
if( l > i ) printf("ilp32"); // -1 is promoted to uint32.
else printf("lp64"); // both promoted to signed 64.
You're making assumptions about the specific compilers that you are
familiar with. In fact, this code would output "lp64" on any compiler
that I know of for a 16 bit platform.

Also the fact that long has a greater range than int does not prove
any of the following:

1. long is 64 bits
2. pointer is 64 bits
3. long and pointer have the same size
if( l > (long)i ) // fix
...;
Example.
main(){ // k is -1 in LP32, 4294967295 in LP64
Implicit int is no longer legal in C.
int i = -2;
unsigned int j = 1;
long k = i+j;
printf("int %d+ uint %ld= long k is %ld\n",i,j,k);
The conversion specifier for the unsigned int j should be "%u", not
"%ld".
}

15. Prototype:
// #include <stdlib.h> missing.
int *i = malloc(sizeof(*i));
Calling a non-variadic function without a declaration in scope is now
illegal in C. Calling a function without a declaration in scope that
returns anything other than an int has been undefined behavior in C
for more than 25 years.

In any case, if there is not a prototype or at least a declaration of
malloc() in scope at the point of this call, this is a constraint
violation, because malloc() is assumed to return an int and you can't
assign an int to a pointer to int
// Ok in lp32, Crash on lp64 because
No, not OK in lp32. There are some compilers that return ints and
pointers differently, even if they have the same number of bits. The
behavior is undefined.
// malloc will return 32bit int value because of missing
prototype.
No, malloc() will return a pointer to void, like it always does. Lack
of a prototype can't change that. The problem is that the compiler
will generate code assuming that malloc() will return an int.
}

////////////////////////////////////////////////////
Examples
> cc +DA2.0W +w1 +M2 enquire.c # +w1 for warnings, +M2 for 64bits

long double=128 bits

# Sort LP64 warning by problem:
> make 2>&1 | grep LP64 | xsort 'warning:\s+\S+:' | align warn >

lp64.log
> vim lp64.log

:set isk+=46 " Make . part of tag, so file.c is a keyword for
tags"

> emacs lp64.log
> M-x compilation-minor-mode
> M-x next-error

; if cant find files, set
; (setq compilation-search-path '("dir1" "dir2")) C-xC-e

/714: Function "malloc" called with no prototype or definition in
scope
/724: Initialization converts default int return type to pointer

/722:.*cast converts 32 bit constant to pointer
/725:.*cast converts 32 bit integer to pointer
/727:.*cast truncates pointer into 32 bit integer
/729:.*converts int* to long*
/732:.*different types treated as unsigned
/740:.*casting to a smaller size may truncate
/530:.*casting from loose to strict alignment
/720:.*Assignment may overflow
// Example
catt -n x.c


1 void main(){


"void main()" has never been legal C.
2 int *x, *q, i;
3 long l=-1;
4 x = malloc(sizeof(int)*10);
5 (int*)i;
6 (int) &i;
7 (long*)&i;
8 i = l;
9 }

hp64> cc +DA2.0W +M2 +w1 -Aa x.c

/*
line 4: warning 714: Function "malloc" called with no prototype or
definition in scope.
line 4: warning 724: LP64 migration: Assignment converts default int
return type to pointer "x".
line 5: warning 725: LP64 migration: Cast converts 32 bit integer to
pointer.
line 6: warning 727: LP64 migration: Cast truncates pointer into 32
bit integer.
line 7: warning 530: LP64 migration: Casting from loose to strict
alignment: Resulting pointer may be misaligned.
line 7: warning 729: LP64 migration: Cast converts int* to long*.
line 8: warning 720: LP64 migration: Assignment may overflow integer
"i".
*/

/////////////////////////////////////////////////
A. Sizes
Ansi sizeof() is always unsigned, try:
if( 0-sizeof(int) > 0 ) printf("See: 0-sizeof(int) is
unsigned!\n");
ILP32: win32 hp11 solaris linux

ANSI C Integer Requirements, see C book. moshtag=ansi_int
sizeof(int) >= sizeof(short) >= 2.
ANSI C does not now, and never has, required that sizeof(int) or
sizeof(short) be >= 2. They are required to contain at least 16 bits,
but there is nothing that says that there can't be more than 8 bits in
a byte.
sizeof(long) >=sizeof(int)
This one's OK.
sizeof(long) >= 4.
Nope. I'm writing C code for a Texas Instruments DSP right now with
16 bit characters. The processor literally can't access less than 16
bits at a time. There is no 8-bit memory addressability in the
hardware. In this architecture:

sizeof(char) == sizeof(short) == sizeof(int) == 1 (16 bits).
sizeof(long) == 2 (32 bits).

And this is strictly conforming implementation.

In the past I've worked with an Analog Devices DSP that only dealt
with 32 bits. All the integer types, from char through long, had
sizeof() equal to 1. And that was conforming as well.
LP64: hp11_64 solaris_64 (assume default for 64bit).
New: sizeof(long)==sizeof(void*)==8, rest unchanged.

P64: win64
New: sizeof(void*)==8, rest unchanged.

//////////////////////////////////////////////////////////

Linux64 moshtag=linux64
char = 8 bits, signed
short=16 int=32 long=64 float=32 double=64 bits
long double=128 bits
Type size_t is unsigned long
ALIGNMENTS
char=1 short=2 int=4 long=8 float=4 double=8 long double=16
char*=8 int*=8 func*=8
short: BA
int: DCBA
long: HGFEDCBA
Maximum long = 9223372036854775807 (= 2**63-1)
Minimum long = -9223372036854775808
Maximum unsigned short = 65535
Maximum unsigned int = 4294967295
Maximum unsigned long = 18446744073709551615
PROMOTIONS
unsigned short promotes to signed int
long+unsigned gives signed long

HP64 ALIGNMENTS
char=1 short=2 int=4 long=8 float=4 double=8 long double=16
char*=8 int*=8 func*=8

//////////////////////////////////////////////////////////


It would have been useful to post a short message indicating that you
had put together a web page about some 64 versus 32 bit issues on some
particular compilers.

Going into all these compiler-specific details here is way off-topic.

Every issue you mention is a problem that can be caused by sloppy
coding, or some of them appear to be compiler defects. Most of your
examples exhibit some kind of undefined behavior, or unwitting
reliance on implementation-defined behavior, and the fact that the
code does what the programmer intended is strictly accidental.

We went this pain once, when the general population of common desk top
platforms transitioned from 16 bit to 32 bit ints. We are going to go
through it again as the population evolves to 64 bits.

The reason we are going to go through it again, sadly, is that the
average C programmer does not know the actual language, write sloppy
code, and does not take advantage of compiler diagnostics, let along
tools like lint.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq
Nov 13 '05 #2

P: n/a
Jack Klein wrote:

On 13 Jul 2003 15:20:07 -0700, pa******@pacbell.net (Panchi51) wrote
in comp.lang.c:

ulong y = (1 <<32); // overflows in lp32 and lp64.


Undefined behavior if int has 32 bits.


.... or less than 32 bits,
as was more common when C was new.

--
pete
Nov 13 '05 #3

This discussion thread is closed

Replies have been disabled for this discussion.