3

When programming in C, sometimes my functions receive numeric values which will be always in range 0 to 4. An 32-bit int can hold up to 2,147,483,647. That's a lot of allocated memory which I'm not using. The lowest-range type which I know is char (which holds from 0 to 255, right?). It's a good practice to use it to hold low-range numeric values? Is there any other type which I can use?

cdonts
  • 9,304
  • 4
  • 46
  • 72
  • How much memory do you consider "a lot"? – hookenz Apr 14 '14 at 00:21
  • This is only really an issue if you are talking about allocating a lot of memory! you're referring to function argument passing. 4 bytes vs 1 isn't much when you've even got 100 arguments passed in. – hookenz Apr 14 '14 at 00:23
  • 1
    `char` has 2**`CHAR_BIT` values, a minimum of 256, but it can be `signed` or `unsigned`. Thus, your range is wrong. Notthat it matters for such low unsigned numbers. – Deduplicator Apr 14 '14 at 00:26
  • Also, did you mean 0-4 (which will take 3 bits) or 0-3 (which is only two bits), not that it matters much in the grand scheme of things – Foon Apr 14 '14 at 00:26

7 Answers7

4

If it's a scalar function argument it'll certainly be promoted to at least a 32-bit type in practice when passed to the function, so using char would make no practical difference. I think it's better to go for readability, and int is the obvious "general integer value" type.

However, if you potentially have a large-ish array of values, using char will enable the values to be packed more tightly in memory. If you're really pressed on memory you can pack them even tighter by using just 3 or 4 bits per value (3 is minimum, 4 aligns more nicely). However this will be certainly less efficient to process.

Matti Virkkunen
  • 63,558
  • 9
  • 127
  • 159
  • Remember `char` has impleentation defined signedness. – Deduplicator Apr 14 '14 at 00:24
  • 1
    also, the OP never said he is on a modern desktop system. Maybe he is working on a microcontroller board... – Deduplicator Apr 14 '14 at 00:42
  • Every answer comes with some assumptions, @Deduplicator. I'm sure OP will comment if they weren't correct. It would also be prudent to tag one's question accordingly if the platform is unexpected. – Matti Virkkunen Apr 14 '14 at 00:50
  • 1
    So, you think everything is a desktop? There is no need to make that assumption for the answer, so why try to guess? – Deduplicator Apr 14 '14 at 00:53
  • Thanks for your answer. I think I'll go for readability, there's no need to save on space, it was just I'm a little perfectionist. – cdonts Apr 15 '14 at 20:19
4

If you really need to save on space, you can use bitfields. Note however that you will likely take more cycles to access the data. (Even accessing a char on modern hardware may take more cycles than native integer sizes)

(edit to give example:

struct packed_values
{
     unsigned int val1       : 3; /* ranges from 0-7 */
     unsigned int val2       : 3;
     unsigned int val3       : 3; 
     unsigned int val4       : 3;
     unsigned int val5       : 3;
     unsigned int val6       : 3;
     unsigned int val7       : 3;
     unsigned int val8       : 3;
     unsigned int val9       : 3;
     unsigned int val10      : 3;
     unsigned int padding    : 2; // make this be 32bits
};

packed_value myval;
myval.val1 = 5;
myval.val2 = 6;
myval.val3 = 7;

)

Foon
  • 6,148
  • 11
  • 40
  • 42
3

Don't use char for this "efficiency" - it's just going to confuse/annoy people (like me :-). Use int for general numeric integers.

For precise control, such as for an array of 8-bit RGB pixels, use uint8_t.

user2864740
  • 60,010
  • 15
  • 145
  • 220
  • @Deduplicator My eyes watered a little. Bit-packing is covered by other answers, so I'll dry my hands and walk away xD – user2864740 Apr 14 '14 at 00:30
2

The type you use to pass the value around almost don't matter, you shoud use a byte-aligned type, char is fine for this (although it will likely be upcast to int after almost any numeric operation, so int might be easier).

When you store it in arrays you should consider packing the data as 2-bits per value.

U2EF1
  • 12,907
  • 3
  • 35
  • 37
1

char is great for holding low range values. It provides efficiency especially when you have a large number of values. There is nothing smaller, as char is a full byte, unless you decide to store multiple values in a single variable by partitioning the bits... Good luck!

user2105505
  • 686
  • 1
  • 9
  • 18
  • "It provides efficiency" - in memory usage, maybe. In execution speed, probably the opposite. – Oliver Charlesworth Apr 14 '14 at 00:19
  • I'm not an architecture expert, but as far as I understand, a char is simply 8 bits. 8 bits is faster than 16 bit shorts, 32 ints, and 64 bit long longs, in memory and execution. – user2105505 Apr 14 '14 at 00:20
  • 1
    On most platforms, the native width of the ALU > 8 bits. Therefore that `char` has to be extracted each time, which may take cycles depending on the architecture. Of course, on the other hand, if your program is limited by memory speed, it may be worth it, because you'll use the cache less. – Oliver Charlesworth Apr 14 '14 at 00:22
1

char is a narrow integer type. It may be either signed or unsigned. If it's unsigned, its range is at least 0 to 255; if it's signed, its range is at least -127 to +127. (Yes, -127, not -128; the standard doesn't require 2's-complement for signed integers).

Type char, as the name implies, is primarily intended to hold characters -- but you can use it to hold small integers if you like, particularly if you know they'll only be in the range 0 to 127. It's probably better to use unsigned char or signed char.

But in any case, don't expect the space savings to be significant relative to using int. Many systems require more and slower code to perform arithmetic on char values than on int values -- and char values are implicitly promoted to int in most cases anyway. If you need to store a large array of small integer values, an array of some character type makes sense. For a single variable, using char rather than int won't save much, if anything.

You should also keep in mind that int is only guaranteed to be at least 16 bits, not 32. (But these days, it's probably going to be 32 bits or wider on most systems you're likely to use, unless you're doing embedded work.) If you need a specific size, there are a number of types (actually typedefs, aliases for existing types) defined in <stdint.h>.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
1

There is no C specification about performance, so any answer is at best a guideline and subject to performance profiling.

In general

1) Using int (or unsigned) is the best speed and smallest code size.

2) Use the smallest integer type char, signed char, or unsigned char) as they use the least memory space. No single type smaller except maybe _Bool which will not work for 0 to 4. Bit fields are potential smaller if you have a number of such small objects to pack together.

It is good practice to use small integer types/bit fields when there are many replications like an large array or when memory is dear. Else simplicity of code should prevail.


As mention by many about char assumptions:

1) It is either signed or unsigned.

2) It has a minimum width of 8 bits and a minimum rage of 0 to 255 or -127 to 127. (not -128)

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    I was going to ask for a source on 2, but found 1 here: http://stackoverflow.com/questions/8010040/why-is-schar-min-defined-as-127-in-c99) – Foon Apr 14 '14 at 11:06
  • @Foon See C11 §5.2.4.2.1 1 & 2: The _minimum_ range of `signed char` is -127 to +127 and `unsigned char` 0 to 255 which `char` must match one of those. `CHAR_BIT` must be at least 8. – chux - Reinstate Monica Apr 15 '14 at 20:37