Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are there any well-established/standardized ways to use fixed-width integers in C89?

Some background:

the header stdint.h is part of the C standard since C99. It includes typedefs that are ensured to be 8, 16, 32, and 64-bit long integers, both signed and unsigned. This header is not part of the C89 standard, though, and I haven't yet found any straightforward way to ensure that my datatypes have a known length.

Getting to the actual topic

The following code is how SQLite (written in C89) defines 64-bit integers, but I don't find it convincing. That is, I don't think it's going to work everywhere. Worst of all, it could fail silently:

/*
** CAPI3REF: 64-Bit Integer Types
** KEYWORDS: sqlite_int64 sqlite_uint64
**
** Because there is no cross-platform way to specify 64-bit integer types
** SQLite includes typedefs for 64-bit signed and unsigned integers.
*/
#ifdef SQLITE_INT64_TYPE
  typedef SQLITE_INT64_TYPE sqlite_int64;
  typedef unsigned SQLITE_INT64_TYPE sqlite_uint64;
#elif defined(_MSC_VER) || defined(__BORLANDC__)
  typedef __int64 sqlite_int64;
  typedef unsigned __int64 sqlite_uint64;
#else
  typedef long long int sqlite_int64;
  typedef unsigned long long int sqlite_uint64;
#endif
typedef sqlite_int64 sqlite3_int64;
typedef sqlite_uint64 sqlite3_uint64;

So, this is what I've been doing so far:

  • Checking that the "char" data type is 8 bits long, since it's not guaranteed to be. If the preprocessor variable "CHAR_BIT" is not equal to 8, compilation fails
  • Now that "char" is guaranteed to be 8 bits long, I create a struct containing an array of several unsigned chars, which correspond to several bytes in the integer.
  • I write "operator" functions for my datatypes. Addition, multiplication, division, modulo, conversion from/to string, etc.

I have abstracted this process in a header file, which is the best I can do with what I know, but I wonder if there is a more straightforward way to achieve this.

I'm asking because I want to write a portable C library.

like image 641
Lehonti Avatar asked Oct 06 '16 19:10

Lehonti


People also ask

Should you use fixed width integer types?

You should only use the fixed width types when you make an assumption about the width. uint8_t and unsigned char are the same on most platforms, but not on all. Using uint8_t emphasizes on the fact that you suppose an architecture with 8 bit char and wouldn't compile on others, so this is a feature.

What are fixed width integers?

The fixed-width integer types that <inttypes. h> provides, include signed integer types, such as int8_t, int16_t, int32_t, int64_t, and unsigned integer types, such as uint8_t, uint16_t, uint32_t, and uint64_t.

What is int16_t C++?

int16_t is a 16bit integer. uint16_t is an unsigned 16bit integer. Same applies for 8bit, 32bit and 64bit variables. It is mostly used for making programs cross platform. For example, Arduino integers (int) are 2 byte, but in home computers an integer is 32bit.


2 Answers

First, you should ask yourself whether you really need to support implementations that don't provide <stdint.h>. It was standardized in 1999, and even many pre-C99 implementations are likely to provide it as an extension.

Assuming you really need this, Doug Gwyn, a member of the ISO C standard committee, created an implementation of several of the new headers for C9x (as C99 was then known), compatible with C89/C90. The headers are in the public domain and should be reasonably portable.

http://www.lysator.liu.se/(nobg)/c/q8/index.html

(As I understand it, the name "q8" has no particular meaning; he just chose it as a reasonably short and unique search term.)

like image 144
Keith Thompson Avatar answered Nov 10 '22 00:11

Keith Thompson


One rather nasty quirk of integer types in C stems from the fact that many "modern" implementations will have, for at least one size of integer, two incompatible signed types of that size with the same bit representation and likewise two incompatible unsigned types. Most typically the types will be 32-bit "int" and "long", or 64-bit "long" and "long long". The "fixed-sized" types will typically alias to one of the standard types, though implementations are not consistent about which one.

Although compilers used to assume that accesses to one type of a given size might affect objects of the other, the authors of the Standard didn't mandate that they do so (probably because there would have been no point ordering people to do things they would do anyway and they couldn't imagine any sane compiler writer doing otherwise; once compilers started doing so, it was politically difficult to revoke that "permission"). Consequently, if one has a library which stores data in a 32-bit "int" and another which reads data from a 32-bit "long", the only way to be assured of correct behavior is to either disable aliasing analysis altogether (probably the sanest choice while using gcc) or else add gratuitous copy operations (being careful that gcc doesn't optimize them out and then use their absence as an excuse to break code--something it sometimes does as of 6.2).

like image 44
supercat Avatar answered Nov 10 '22 00:11

supercat