All references to the type int in C code should be changed to INT_BIG. This is a reference to a macro which can be defined in a system-dependent header file or on the compiler command line (and hence typically in the mk environment variable CFLAGS). For a 32-bit build it would normally be defined at build time as int and for a 64-bit build as long. As well as declarations of variables and functions, this applies to almost any other syntactically significant occurrence of the identifier int, for instance casts and arguments of sizeof. References to the types short int and long int, which are just synonyms for short and long respectively, should not be changed. Type unsigned INT_BIG (or, redundantly, signed INT_BIG) may be used. There are some places where an int declaration is implicit, and here an INT_BIG must be inserted.
As regards calling functions from external libraries, the approach described in section 3.2 of recoding all the functions in the library will of course work in C as well as in Fortran. However, this is not always necessary. If the function's ANSI C-style declaration (prototype) is in scope when the function is called, the compiler will normally arrange for conversion of each actual argument to the declared type of the corresponding formal argument in the declaration before passing it by value. Hence the following code
is correct whatever integral type INT_BIG is defined as, since i and j are converted to type int before add() sees them. If the prototype of add() were not in scope however, the conversion would not take place and the code would be in error.2The lesson is to make sure that header files are included. Of course this relies on having ANSI C-style function prototypes; if old-style function declarations are used then no argument type conversions are made. For code which uses old-style function declarations the best thing is to convert it, or at least a corresponding header file, to ANSI C style.int add( int a, int b ); INT_BIG i, j; add( i, j );
However this does not solve all problems. Where a function has a variable argument list (as declared in the prototype using ellipsis `...' and handled in the function using the stdarg.h macros), the function prototype is not able to specify the types of all arguments, and so the type of the actual argument must match the type of the formal argument for correctness. If it is impractical to recode the function (as in the case of printf), the best solution is to cast the variable arguments to the type which is expected where the function is called.
A more difficult problem is when the address of an argument is passed so that the contents of that address can be changed by the function. In this case if the called function has a different idea of the length of the object being pointed to it will write to the wrong amount of memory, possibly overwriting other data. Consider this function:
and this code:void zero( int *a ) { *a = 0; }
If INT_BIG is a 64 bit type and int is 32 bits then only half the bits in the variable x will be zeroed by this call. If the function declaration is in scope when the call is made, the compiler will issue a pointer mismatch warning about this sort of thing; again, make sure that the appropriate header files are included. Again, in the case of variable argument lists, the compiler can't spot it.INT_BIG x; zero( &x );
To summarise, external functions should be declared before use by including the appropriate header files. If this is done, then the only problems associated with calling functions which have not been converted to use INT_BIG instead of int should be:
External libraries which code may have to link against can be split into a few categories:
In the case of int arguments, if the actual argument might be too large to be represented in an int, then an l should be inserted after the `%' sign to indicate that a long argument is being supplied and the corresponding argument should be cast to long. If it will definitely be possible to store the value in an int then the format specifier may be left alone and the argument cast to int. Arguments passed using the c or * specifiers should be cast to int, and not modified with an l character. In the case of int * arguments, intermediate variables have to be used.
Here is an example. If after simple substitution of INT_BIG for int a piece of code reads:
then the scanf call must be replaced by something like this:extern INT_BIG triple( INT_BIG x ); INT_BIG i, j; char c; scanf( "%i %c", &i, &c ); j = triple( i ); printf( "Integer tripled is %i; Character is '%c'\n", j, c );
and the printf call by something like this:{ long tmp; scanf( "%li %c", &tmp, &c ); i = tmp; }
printf( "Integer tripled is %li; Character is '%c'\n", (long) j, c );
There are a few other issues which arise from replacing int type with INT_BIG:
the symbols sub, x, y and z all have type int so that the converted code should readsub( x ) { static y; signed z; }
INT_BIG sub( INT_BIG x ) { static INT_BIG y; signed INT_BIG z; }
The program crepint, and the corresponding driver script do-crepint, is provided for making some of these changes. It replaces all references to int type, with a few exceptions, by INT_BIG type, modifies explicit declarations which are implicitly of type int, and warns about constructs which might need further attention. Full details of which of the above constructs it fixes, and which it warns about, are given in the appendix.
A construction which crepint misses altogether is finding implicit declarations in function prototypes, which are implicitly of type int. These can be spotted by suitable use of the C compilers. Given a file sub.c which reads:
then running gcc with the -Wimplicit flag generates warnings for such declarations:sub( x ) { return x; }
The -proto flag of Tru64 Unix's C compiler is a little more effort to use, but produces more concise output. Running% gcc -fsyntax-only -Wimplicit-function-declaration -Wstrict-prototypes sub.c sub.c:1: warning: return-type defaults to `int' sub.c:1: warning: function declaration isn't a prototype
will produce a file called sub.H which reads:% cc -protois -noobject sub.c
Occurrences of int in the output file sub.H should be changed to read INT_BIG wherever the function is declared or defined in the source code (typically in a source file and maybe a header file), so that sub.c should end up reading:extern int sub(int x);
By runningINT_BIG sub( INT_BIG x ) { return x; }
and attending to any int declarations in the resulting .H files, it should be possible to find any offending implicit declarations. Note however that this only writes function prototypes from function definitions, it does not normalise existing prototypes, so it cannot usefully be applied to header files.% cc -protois -noobject -DINT_BIG=long *.c