Stdint.h in ANSI C (C89) - c

I have recently become interested in switching from writing C99 code to writing plain ANSI C (C89), as the new features in the language are not worth the extreme portability and reliability of writing it in ANSI C. One of the biggest features I thought I would miss making the transition from C99 to C89 would be the stdint.h standard library file; or so I thought. According to this site, there is no stdint.h file in the C89 standard, which is also what I found on Wikipedia. I wanted to make sure that this was indeed the case, so I wrote a minimal test program that I expected would not compile when providing the flags -ansi and -pedantic-errors in both GCC and Clang;
#include <stdio.h>
#include <stdint.h>
int main(void)
{
printf("The size of an int8_t is %ld.\n", sizeof(int8_t));
printf("The size of an int16_t is %ld.\n", sizeof(int16_t));
printf("The size of an int32_t is %ld.\n", sizeof(int32_t));
printf("The size of an int64_t is %ld.\n", sizeof(int64_t));
printf("The size of a uint8_t is %ld.\n", sizeof(uint8_t));
printf("The size of a uint16_t is %ld.\n", sizeof(uint16_t));
printf("The size of a uint32_t is %ld.\n", sizeof(uint32_t));
printf("The size of a uint64_t is %ld.\n", sizeof(uint64_t));
return 0;
}
However, what I found was not a compiler error, nor a warning, but a program that compiled! Since it worked just as well on either compiler, I'm under the assumption that this is not a bug in the compiler. The output, for reference, was what one would expect for a working C99 implementation:
The size of an int8_t is 1.
The size of an int16_t is 2.
The size of an int32_t is 4.
The size of an int64_t is 8.
The size of a uint8_t is 1.
The size of a uint16_t is 2.
The size of a uint32_t is 4.
The size of a uint64_t is 8.
I have a few questions about this "feature".
Should I be able to rely upon a stdint.h header being provided for a C89 program?
If not, what steps would I have to take in creating a header that functions the same as stdint.h?
How did programmers, in the time before C99, solve this problem of having reliable sizes for integers in their programs in a platform-agnostic manor?

Should I be able to rely upon a stdint.h header being provided for a C89 program?
No. You said you picked C89 for portability reasons and then the first thing you reach for is non-portable extensions...
If not, what steps would I have to take in creating a header that functions the same as stdint.h?
How did programmers, in the time before C99, solve this problem of having reliable sizes for integers in their programs in a platform-agnostic manor?
With a forest of macros like for example in this answer. If C89, you typedef all the names present in stdint.h for the given platform. Otherwise, if standard C, you just include stdint.h.
So you'll need your own "notstdint.h" which contains all of this and then you have to port it to each system where integer sizes are different. And yes, this makes C89 less portable than standard C.

In the days of C89 when writing code for microcomputers, one could simply use:
typedef unsigned char uint8;
typedef signed char int8;
typedef unsigned short uint16;
typedef signed short int16;
typedef unsigned long uint32;
typedef signed long int32;
At the time, int might be either 16 or 32 bits, but all the other types would have the indicated sizes on commonplace implementations for systems that could handle them (including all microcomputers). Compilers for 16-bit systems would allow a pointer that was freshly cast from int* to short* to access objects of type int, so there was no need to worry about whether int16_t should be short or int. Likewise those for 32-bit systems would allow pointer that was freshly cast from int* to long* to access objects of type int, so it wasn't necessary to worry about whether int32_t should be int or long.

Related

How to keep size of struct unchanged in x32 and x64?

So I have a structure with mixed data types like the below and I want to make sure that sizeof(struct a) is a multiple of the word size in x32 and x64. How can I do that? Thank you.
struct a {
vaddr_t v1;
size_t v2;
unsigned short v3;
struct b* v4;
struct a *v5;
int v6;
pthread_mutex_t lock;
};
With basic types, like ints or shorts, you could achieve this by explicitly using int32 or int16 instead of int or short. For other types like size_t or pointers, it gets more complicated. Your best bet is to use type attributes (http://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Type-Attributes.html).
If all that matters is the structure alignment in memory, align the structure itself, not its members.
I may be stepping a bit outside of my comfort zone here, however there seems to be a variation on the malloc called memalign thus :
void *memalign(size_t alignment, size_t size);
The memalign() function allocates size bytes on a specified
alignment boundary and returns a pointer to the allocated
block. The value of the returned address is guaranteed to be
an even multiple of alignment. The value of alignment must
be a power of two and must be greater than or equal to the
size of a word.
That may or may not exist on all platforms but this one seems to be very common :
int posix_memalign(void **memptr, size_t alignment, size_t size);
Seen at :
http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_memalign.html
Now I would think that the datatypes for fixed width type declarations, as proposed by the ISO/JTC1/SC22/WG14 C, committee's working draft for the revision of the current ISO C standard ISO/IEC 9899:1990 Programming language - C, ( I read that in a manpage ) would be cross platform and cross architecture stable.
So if you looked into the lower levels of your struct members then hopefully they are based on things like int32_t or uint32_t for an integer. There are POSIX types such as :
/*
* POSIX Extensions
*/
typedef unsigned char uchar_t;
typedef unsigned short ushort_t;
typedef unsigned int uint_t;
typedef unsigned long ulong_t;
So I am thinking here that perhaps it is possible to construct your structs using only types that are defined as these totally cross platform stable datatypes and the end result being that the structs are always the same size regardless where or how you compile your code.
Please bear in mind, I am making a stretch here and hoping that someone else may clarify and perhaps correct my thinking.

Difference between int32, int, int32_t, int8 and int8_t

I came across the data type int32_t in a C program recently. I know that it stores 32 bits, but don't int and int32 do the same?
Also, I want to use char in a program. Can I use int8_t instead? What is the difference?
To summarize: what is the difference between int32, int, int32_t, int8 and int8_t in C?
Between int32 and int32_t, (and likewise between int8 and int8_t) the difference is pretty simple: the C standard defines int8_t and int32_t, but does not define anything named int8 or int32 -- the latter (if they exist at all) is probably from some other header or library (most likely predates the addition of int8_t and int32_t in C99).
Plain int is quite a bit different from the others. Where int8_t and int32_t each have a specified size, int can be any size >= 16 bits. At different times, both 16 bits and 32 bits have been reasonably common (and for a 64-bit implementation, it should probably be 64 bits).
On the other hand, int is guaranteed to be present in every implementation of C, where int8_t and int32_t are not. It's probably open to question whether this matters to you though. If you use C on small embedded systems and/or older compilers, it may be a problem. If you use it primarily with a modern compiler on desktop/server machines, it probably won't be.
Oops -- missed the part about char. You'd use int8_t instead of char if (and only if) you want an integer type guaranteed to be exactly 8 bits in size. If you want to store characters, you probably want to use char instead. Its size can vary (in terms of number of bits) but it's guaranteed to be exactly one byte. One slight oddity though: there's no guarantee about whether a plain char is signed or unsigned (and many compilers can make it either one, depending on a compile-time flag). If you need to ensure its being either signed or unsigned, you need to specify that explicitly.
The _t data types are typedef types in the stdint.h header, while int is an in built fundamental data type. This make the _t available only if stdint.h exists. int on the other hand is guaranteed to exist.
Always keep in mind that 'size' is variable if not explicitly specified so if you declare
int i = 10;
On some systems it may result in 16-bit integer by compiler and on some others it may result in 32-bit integer (or 64-bit integer on newer systems).
In embedded environments this may end up in weird results (especially while handling memory mapped I/O or may be consider a simple array situation), so it is highly recommended to specify fixed size variables. In legacy systems you may come across
typedef short INT16;
typedef int INT32;
typedef long INT64;
Starting from C99, the designers added stdint.h header file that essentially leverages similar typedefs.
On a windows based system, you may see entries in stdin.h header file as
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef unsigned char uint8_t;
There is quite more to that like minimum width integer or exact width integer types, I think it is not a bad thing to explore stdint.h for a better understanding.

Datatype question

I am trying to port a C program to a SPARC architecture that has
the following type declaration
#include <stdint.h>
typedef uint32_t WORD ;
typedef uint64_t DWORD ;
The trouble is, that the compiler tells me that stdint.h cant be found. Hence,
I redefined those datatypes as follows:
unsigned int WORD;
unsigned long DWORD;
This seems for me the straightforward declaration, but the program is not expecting as it should. Did I maybe miss something?
Thanks
<stdint.h> and the types uint32_t and uint64_t are "new" in ISO/IEC 9899:1999. Your compiler may only conform to the previous version of the standard.
If you are sure that unsigned int and unsigned long are 32-bit and 64-bit respectively then you shouldn't have any problems (at least not ones due to the typedefs themselves). As you are, this may not be the case. Do you know (or can you find out) if your compiler supports unsigned long long?
I'm guessing that unsigned int is probably 32-bit, how old is your SPARC?
If your compiler/OS does not have <stdint.h> then the best thing to do is implement your own version of this header rather than not modify the code you're trying to port. You probably only need a subset of the types that are normally defined in <stdint.h>, e.g.
//
// stdint.h
//
typedef int int32_t; // signed 32 bit int
typedef unsigned long long uint64_t; // unsigned 64 bit int
(obviously you need to know the sizes of the various integer types on your particular platform to do this correctly).
So, you need an integer that's 32 bit and another that's 64 bit.
It might be that int and longs are the same on your architecture, and if your compiler supports long long, that might be 64 bit while int might be 32 bit. Check your compiler docs for what it supports, and if it has any extension (e.g. some compilers might provide an __int64 type). This could be what you need:
typedef unsigned int WORD;
typedef unsigned long long DWORD;
Anyway, I'd write a small program to verify the sizes of integers on your host, so you can pick the correct one , that is printf the sizeof(int), sizeof(long) and so on. (On a sparc host CHAR_BIT will be 8, so it's all atleast a multple of 8 bits. )
Also, since you're porting to a sparc host, make sure your code is not messing up somewhere regarding endianess

What's the data-type of C enum of Clang compiler?

I posted other question: What type should I use for binary representation of C enum?, and by the answer, I have to know my compiler's enum data-type.
What's the data-type of C enum on Clang compiler?
Like most (all, maybe) C compilers, the size of an enumerated type can vary. Here's an example program and its output:
#include <stdio.h>
typedef enum
{
val1 = 0x12
} type1;
typedef enum
{
val2 = 0x123456789
} type2;
int main(int argc, char **argv)
{
printf("1: %zu\n2: %zu\n", sizeof(type1), sizeof(type2));
return 0;
}
Output:
1: 4
2: 8
All that the standard requires is:
The choice of type is implementation-defined, but shall be capable of representing the values of all the members of the enumeration.
A quick web search didn't turn up a clang manual that specified its behaviour, but one is almost certainly out there somewhere.
In addition to Carl's answer it might even be compatible to a signed or unsigned type if anything of that fits better. E.g it could but mustn't in
enum small { m = -1; a = 127; };
enum big { b = 255; };
have small in a signed char and big in an unsigned char.
BTW the constants m, a and b are not of enumeration type but always of type int.
Edit: I just tested with values larger than int. In C99 mode gcc emits an error (which is correct) but clang only issues a warning and than has a wider type to represent the enum.
Well, a compiler may chose an integer size big enough but I guess it will chose a "native" size (a "word" = size of register, should be long for x86 32bit mode or long long for x64). For your private struct you shouldn't care, but if you want to serialize it to a file or over the network then you should explicitly use an integer type that's big enough (e.g. long) so you can pick it up with another compiler/language without headaches.
If you really wanna know, just ask the compiler:
printf("%d\n", sizeof(enum myEnumType));

How to make sure a int is 4 bytes or 2 bytes in C/C++

I want to know how to announce int to make sure it's 4 bytes or short in 2 bytes no matter on what platform. Does C99 have rules about this?
C99 doesn't say much about this, but you can check whether sizeof(int) == 4, or you can use fixed size types like uint32_t (32 bits unsigned integer). They are defined in stdint.h
If you are using C99 and require integer types of a given size, include stdint.h. It defines types such as uint32_t for an unsigned integer of exactly 32-bits, and uint_fast32_t for an unsigned integer of at least 32 bits and “fast” on the target machine by some definition of fast.
Edit: Remember that you can also use bitfields to get a specific number of bits (though it may not give the best performance, especially with “strange” sizes, and most aspects are implementation-defined):
typedef struct {
unsigned four_bytes:32;
unsigned two_bytes:16;
unsigned three_bits:3;
unsigned five_bits:5;
} my_message_t;
Edit 2: Also remember that sizeof returns the number of chars. It's theoretically possible (though very unlikely these days) that char is not 8 bits; the number of bits in a char is defined as CHAR_BIT in limits.h.
Try the INT_MAX constant in limits.h
Do you want to require it to be 4 bytes?
If you just want to see the size of int as it is compiled on each platform then you can just do sizeof(int).
sizeof (int) will return the number of bytes an int occupies in memory on the current system.
I assume you want something beyond just the obvious sizeof (int) == 4 check. Likely you want some compile-time check.
In C++, you could use BOOST_STATIC_ASSERT.
In C, you can make compile-time assertions by writing code that tries to create negatively-sized arrays on failure or that tries to create switch statements with redefined cases. See this stackoverflow question for examples: Ways to ASSERT expressions at build time in C
You can use sizeof(int), but you can never assume how large an int is. The C specification doesn't put any assumptions on the size of an int, except that it must be greater or equal to the size of a short (which must be greater or equal to the size of a char).
Often the size of an int aligns to the underlying hardware. This means an int is typically the same as a word, where a word is the functional size of data fetched off the memory bus (or sometimes the CPU register width). It doesn't have to be the same as a word, but the earliest notes I have indicated it should be the preferred size for memory transfer (which is typically a word).
In the past, there have been 18 bit ints (PDP-8) and 24 bit ints (PDP-15). There have been architectures with 36 bit word sizes (PDP-11) but I can't recall what their int size turned out to be.
On Linux platforms, you can peek in
#include <sys/types.h>
to get the actual bit count for each type.
I found last night that visual studio 2008 doesn't support C99 well, and it doesn't support stdint.h. BUT they have their own types. here is a example:
#ifdef _MSC_VER
typedef __int8 int8_t;
typedef unsigned __int8 uint8_t;
typedef __int16 int16_t;
typedef unsigned __int16 uint16_t;
typedef __int32 int32_t;
typedef unsigned __int32 uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <stdint.h>
#endif

Resources