Skip to main content
GameDev.net gamedev.net
🔒 Locked

unsigned char, unsigned short - Why?

Started by riyunoa Mar 29, 2008 at 8:47 AM 15 replies 2.4k views
Original Post
riyunoa
riyunoa
Hey guys. I've just encountered an interesting programming thing. I see HP which I would have stored as an integer, stored as unsigned char/unsigned short in C++. Why (and how) is this done? [Edited by - riyunoa on March 29, 2008 11:14:08 AM]
Specialist84
Specialist84
It´s done because you usually don´t need a wider range than 0 - 100. You can safe memory. int needs 4 bytes, char only one byte.
www.bug-soft.net
ToohrVyk
ToohrVyk
If you have to save a large number of health values, then using an unsigned short or an unsigned char will use only half (respectively one-fourth) the memory of an integer on an average 32-bit system.
Sc4Freak
Sc4Freak
Insanity.

Seriously, though, on modern architectures there aren't all that many reasons to be using unsigned char or unsigned short when an integer would do just fine. It may have been an attempt at optimisation (although you'll find that the compiler sometimes likes to pad these types out to 4 bytes anyway). Or perhaps the author wanted to guarantee that the values would stay within a certain range (0-255 for example) and taking advantage of integer overflow to handle it for him.
Promethium
Promethium
Often unsigned char/short/int/long etc. are used as a sort of conceptual reminder that this value should never be negative. HP's (health points?) for instance: The character have a positive amount of health, and when it reaches 0 he is dead. He can never have a negative amount of health, so you use an unsigned value to indicate this. This way you know that if you ever try to assign a negative value to it, your logic is flawed. Note that it only serves as a reminder to the programmer, C++ will happily accept a negative value in an unsigned variable, and just wrap around.

The reason for using short or char instead of int is most likely just to save a few bytes of RAM, something which is nearly redundant with modern machines, but was very important some years ago. It can still be usefull when doing network trafic, where every saved byte over a lossy internet connection is a bonus.
Antheus
Antheus
Quote:
Original post by ToohrVyk
If you have to save a large number of health values, then using an unsigned short or an unsigned char will use only half (respectively one-fourth) the memory of an integer on an average 32-bit system.


It should be noted that without action on developer's part, they'll frequently use exactly the same amount of memory as an int due to padding.
intransigent-seal
intransigent-seal
Keeping your data small is not redundant on modern machines. Cache misses give a massive speed hit, so minimizing working-set size and being careful with the details of how you lay out your data is still a necessary optimization for the highest performance code.

Also, if you're writing for a games console you're likely to be much more constrained in the amount of RAM you have available, and the cache sizes of the processor(s) that you're running on.

Having said that, I don't imagine health points are likely to be involved in any code that really needs heavy optimization.

John B
The best thing about the internet is the way people with no experience or qualifications can pretend to be completely superior to other people who have no experience or qualifications.
riyunoa
riyunoa
Oh, that makes plenty of sense, I had the idea it had something to do with that. But how DO people store integers as unsigned chars? I read somewhere that it's like a byte...

But I never thought that chars would be used to store integers, always thought that chars were 'z' and that kinda thing.
Porthos
Porthos
ASCII characters like 'z' are just numbers, integers. See the ASCII table for a full list. Each character corresponds to a number which is really stored in the 'char'. However, when e.g. printing text on the screen, the printing routine interprets these values, lookups them in the ASCII table and prints the right character. You could store in a char whatever you want!

However, using the native integer size for a platform is normally the most efficient way. This is in C++ the 'int' primitive type.

Best regards,
Porthos
Bonedry
Bonedry
Quote:
Original post by riyunoa
Oh, that makes plenty of sense, I had the idea it had something to do with that. But how DO people store integers as unsigned chars? I read somewhere that it's like a byte...

But I never thought that chars would be used to store integers, always thought that chars were 'z' and that kinda thing.


chars are just integers. Even when storing a character (like 'z'), it is stored as an integer (as defined by ASCII).

So using them for arithmetics is done the same way as you would do with other integer types, like int.

char some_number = 5;some_number++;some_number *= 5;std::cout << static_cast<int>(some_number) << std::endl; //Prints 30
dmatter
dmatter
Quote:
Original post by riyunoa
Oh, that makes plenty of sense, I had the idea it had something to do with that. But how DO people store integers as unsigned chars? I read somewhere that it's like a byte...

But I never thought that chars would be used to store integers, always thought that chars were 'z' and that kinda thing.


chars are primarily used for storing ASCII characters, yes.
In actual fact characters, like 'z', are represented by their integer ASCII code, all the char really stores is this integer code.

The ramification of this is that the following code is legal:

char c1 = 'z';
char c2 = 122;
std::cout << c1 << " " << c2;
cignox1
cignox1
When reading colors from an integer buffer (i.e. an image loaded from disk) I usually use unsigned char to cast the void* (or int*) pointer to a color:

unsigned char r = ((unsigned char*)buffer)[0];
unsigned char g = ((unsigned char*)buffer)[1];
unsigned char b = ((unsigned char*)buffer)[2];

Of course, I can even (and often I do) use an unsigned char pointer from start...
dashurc
dashurc
I use unsigned variables for two reasons:

-Saving space. Although save file space is more of a concern than RAM for me these days (even on handhelds). An example of where I'd use an unsigned char is if I need to store player attributes in a save file (i.e. 500 players each with 100 attributes which are all in the range of 0 to 100. Using an int would require about 195KB for the save file. Using an unsigned char would only require 48KB for the same information. Some games have much larger player databases and would see an even bigger savings).

-If I want to indicate that a variable should always be a positive value. It's more to identify what the variable is to be used for, for other users of my classes than anything though.
zedz
zedz
int are potentially safer with arithmatic
eg

unsigned char health = 100;
health -= 120; // player should be dead
but instead
health is now 236 // lazarus
Bonedry
Bonedry
Quote:
Original post by dashurc
I use unsigned variables for two reasons:

-Saving space. Although save file space is more of a concern than RAM for me these days (even on handhelds). An example of where I'd use an unsigned char is if I need to store player attributes in a save file (i.e. 500 players each with 100 attributes which are all in the range of 0 to 100. ...


Although it is something to think about, unsigned (as opposed to signed) has no effect here, as signed chars can contain values up to 127.


Quote:
Original post by zedz
int are potentially safer with arithmatic
eg

unsigned char health = 100;
health -= 120; // player should be dead
but instead
health is now 236 // lazarus



This particular example has nothing to do with int, though. It is rather a matter of whether to choose signed or unsigned.
alvaro
alvaro
You may want to use unsigned integer types if you intend to apply bitwise operations to them and you want it to behave in predictable ways. Their signed versions will typically behave as if implemented using two's complement, but the language does not guarantee that this is the case.

It might not be very important in practice, but I thought it should be mentioned.

bzroom
bzroom
I often get mixed when using signed and unsigned ints because I typically only use unsigned and size_t (what ever that is..). The nice thing about unsigned is that you only have to check one bound to see if an index is valid (assuming the max index is less than the max of storage).

for EX:
void DoSomething( const unsigned int index ){  if (index < myArray.Size())  {    //actually do something  }}


This may not necessarily be a good idea, but if you're using unsigned there is no need to worry about the negative case, as -1 will really be MyStorageType::MaxValue.

This all is fine and dandy until one day you want to pass a -1 to that function to signify some other kind of case (like wishing to pass a zero to a reference). Then all you need to do is delete the word "unsigned" :). But as mentioned before. It's often used as a mental note, and its also used to acheive a bit of head room in your variable size. For instance you can only store +127 ish in a char where you could store +255 in a char.

Anywho, I'm done rambling.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.