Is there any disadvantage to using char
for small integers in C? Are there any advantages other than the occupancy/memory benefit?
In particular, is the processor likely to cope with integer arithmetic on a char
better or worse than it would on a (long
/short
) int
?
I know this will be processor/system/compiler specific, but I'm hoping for an answer in the general case, or, at least, the general case for 32-bit Windows and Solaris, being the systems I'm currently working on. I'm also assuming that things like overflow/wraparound issues have already been dealt with.
Update: Visual Studio 6.0 doesn't actually have stdint.h
as suggested by Christoph. A little benchmarking on Windows (VS 6.0, debug build, 32-bit) with a handful of stacked loops gives int
and long
as providing similar performance, which is about twice as fast as char
. Running the same test on Linux with gcc similarly pegs int
and long
as similar, and both faster than char
, although the difference is less pronounced.
As a side note, I've not spent much time looking, but the first implementation of stdint.h
for VS 6.0 I found (via Wikipedia) defines uint_fast8_t
as unsigned char
, despite this seeming to be slower in my tests at least. Thus, the moral of the story, as Christoph rightly suggested: always benchmark!
C99 added so-called 'fastest' minimum-width integer types to solve this problem. For the range you're interested in, the types would be
int_fast8_t
anduint_fast8_t
, which can be found instdint.h
.Keep in mind that there might be no performance gain (the increase in memory consumption might even slow things down); as always, benchmark! Don't optimize prematurely or solely on potentially flawed assumptions of what should work.
Internally, processors generally perform arithmetic on machine words. This means that when calculations on other types are performed, although the calculation itself will take the same length of time, depending on the available instruction set extra work may have to be done to read inputs and to coerce calculation results into the target type (e.g. sign-extending/zero-filling, shifting/masking to avoid unaligned memory accesses, etc).
This is why C defines types and operations as it does - the size of
int
is not mandated by the standard, allowing compiler authors to make it correspond to a machine word, and expression evaluation is defined to promote smaller integer types toint
, greatly reducing the number of points at which results must be coerced to some target type.Valid reasons to use
char
for storing integer values are when the space really matters that much (not as often as you might think), and when describing some external data format / protocol that you are marshalling data to / from. Expect uses ofchar
to incur a slight loss of performance, especially on hardware such as Cell SPU where only machine word size memory accesses are available so accessing a char in memory requires several shifts and masks.Another con I can think of is that (as far as I know) "modern" processors do all their math in "full" integers, generally 32 bits. So dealing with a
char
usually means pulling a single byte out of memory, filling up with 0's in transferring to a register, doing something with it and then squeezing only the least significant bits of the result back into memory. Especially if thechar
is not aligned on a handy boundary, this memory access takes a lot more work to accomplish.Using
char
forint
is really only useful when you have a lot of numbers (i.e. a large array) and you need to conserve space.Arithmetic on chars will almost certainly actually performed using the same registers as arithmetic on ints. For example:
The addition compiles to the following with VC++:
where eax is a 32-bit register.
There is therefore no adavantage for using chars over ints when it comes to arithmetic performance.
The main con I would see is that your code is using a type that means one thing for values that mean something else -- e.g., there's a semantic problem which could be a maintenance problem. If you did it, I'd probably recommend typedefing it:
That way, A) It's clearer what you're doing, and B) You can change it easily (e.g., just the one place) if you run into trouble.
Do you have a really good reason not to use
int
?Well, the first issue is that it's not defined by the C standard whether plain
char
is signed or unsigned - so the only range you can portably rely on is 0 to 127.Other than that, in general
int
is supposed to be the type corresponding to the native word size of the architecture (but of course this isn't enforced by anything). This would tend to be the type with the best arithmetic performance, but that's about all you can say.Note that operands narrower than
int
are widened either toint
orunsigned int
during expression evaluation anyway.