typedef struct {
unsigned char a;
unsigned char b;
unsigned char c;
unsigned char d;
} MyStruct;
I also use a union to access different parts of this struct easier.
[code
typedef union {
MyStruct my_struct;
unsigned short first_half;
unsigned long whole_thing;
} MyUnion;
I use a few of these values as the index into other arrays. The other arrays are 256 elements in size, and I never use any element higher than 191 (around there). I am getting weird behavior though. If I use the unsigned char as the index, everything breaks. When I use a regular int, all works fine. I''ve heard in the past that you should use always use the default size of the machine (32-bits here) as an index into an array or when computing pointer offsets. Anyone else heard this, or know the reason for this, or know why my program breaks when I use an 8-bit value instead of a 32-bit? It doesn''t crash, but it doesn''t do what it''s supposed to do.
🔒 Locked
Mixing different variable types
Advertisement
Original Post
I have recently had some problems with mixing different variable types (ex. mixing signed with unsigned, char with short with long, etc.), and I am wondering if there are any "rules" or anything as for when it is ok to mix these different types.
The main problem areas are the different data type widths. An example. I have one struct that uses chars instead of ints to conserve space, because I have a big array of these, and I copy them a lot.
Erm... didn''t quite understand what you meant in the last paragraph... do you mind posting a source code example where it works, and another where it doesn''t?
ToohrVyk
-------------
Extatica - a free 3d game engine
Available soon!
Click here to learn more
ToohrVyk
-------------
Extatica - a free 3d game engine
Available soon!
Click here to learn more
This doesn''t work...
This does work...
It is called from this...
Similarly, if I change ''c'' and ''piece'' to unsigned char in InitAttacks(), it breaks.
This is part of an initialization routine that initializes an attack table in a chess program. Each element of attackable[] is a bit field describing which pieces could conceivably attack a square of a given offset (-128, +128). You add 128 to avoid a negative offset. If you want to read more about what I''m doing, go here and read the section "A Bonus" at the bottom.
void RayTrace (int iterations, char offset, unsigned char bit) {
int i;
unsigned char square;
for (i = square = 0; i < iterations; i++) {
square += offset;
attackable[square + 128] |= bit;
step[square + 128] = offset;
}
}
This does work...
void RayTrace (int iterations, int offset, unsigned flag) {
int i;
int sq;
for (i = sq = 0; i < iterations; i++) {
sq += offset;
attackable[sq + 128] |= flag;
step[sq + 128] = offset;
}
}
It is called from this...
void InitAttacks () {
int c, piece;
for (c = WHITE; c <= BLACK; c++) {
int i;
for (i = 0; i < 2; i++)
RayTrace(1, pawn_dirs[c], piece_bits[c][PAWN]);
}
for (piece = KNIGHT; piece <= KING; piece++) {
int iterations;
char * dir;
iterations = slider[piece] ? 7 : 1;
for (dir = piece_dirs[0][piece]; *dir; dir++)
RayTrace(iterations, *dir, piece_bits[0][piece]);
}
}
Similarly, if I change ''c'' and ''piece'' to unsigned char in InitAttacks(), it breaks.
This is part of an initialization routine that initializes an attack table in a chess program. Each element of attackable[] is a bit field describing which pieces could conceivably attack a square of a given offset (-128, +128). You add 128 to avoid a negative offset. If you want to read more about what I''m doing, go here and read the section "A Bonus" at the bottom.
One problem you may be having is with your union. Little-endian machines are going to throw off your variables. For example, the value of "first_half" has the high order byte set to the value of b. "whole_thing" has them in the reverse order. Run the following code and you''ll see:
I''m not sure if this is what you''re asking about, since I can''t understand that last paragraph either. Are you using the union members as the indexes?
#include <cstdio>
struct MyStruct
{
unsigned char a;
unsigned char b;
unsigned char c;
unsigned char d;
};
union MyUnion
{
MyStruct my_struct;
unsigned short first_half;
unsigned long whole_thing;
};
int main(int argc, char *argv[])
{
MyUnion oUnion;
oUnion.my_struct.a = 0xAA;
oUnion.my_struct.b = 0xBB;
oUnion.my_struct.c = 0xCC;
oUnion.my_struct.d = 0xDD;
printf("first_half: %X\n", oUnion.first_half);
printf("whole_thing: %X\n", oUnion.whole_thing);
return 0;
}
I''m not sure if this is what you''re asking about, since I can''t understand that last paragraph either. Are you using the union members as the indexes?
Side note:
I think you''d be better off with bitboards (I figure the 0x88 method arose out of a dire lack of memory
)
Now back to your problem:
Stupid question: are you overflowing your 8 bit range? |0x88 + 0x7f| = 9 bits.
Only use char when you need to save space, i.e. large arrays. Loading an 8 bit value into a 32 bit register (mov?x) is slower than a normal move, and 8 bit ops will lead to partial register stalls, which are devastating to Intel CPUs.
For 2-3 auto variables, just use int.
BTW, watch for [ i ] in code
500x3
I think you''d be better off with bitboards (I figure the 0x88 method arose out of a dire lack of memory
Now back to your problem:
quote:
When I use a regular int, all works fine. I''ve heard in the past that you should use always use the default size of the machine (32-bits here) as an index into an array or when computing pointer offsets. Anyone else heard this, or know the reason for this, or know why my program breaks when I use an 8-bit value instead of a 32-bit?
Stupid question: are you overflowing your 8 bit range? |0x88 + 0x7f| = 9 bits.
Only use char when you need to save space, i.e. large arrays. Loading an 8 bit value into a 32 bit register (mov?x) is slower than a normal move, and 8 bit ops will lead to partial register stalls, which are devastating to Intel CPUs.
For 2-3 auto variables, just use int.
BTW, watch for [ i ] in code
500x3
quote:
Are you using the union members as the indexes?
No, my actual structure is something like:
struct {
unsigned char color;
unsigned char type;
unsigned char square;
unsigned char dummy;
}; I use the union to grab just the piece info out of that. That also makes transferring the contents of one square to another easier.
quote:
Your second for loop looks weird. Why is dir a pointer? Is piece_dirs definitely an array of pointers? Is the last pointer definitely 0?
BoardOffset white_pawn_dirs[] = { 15, 17, 16, 0 };
BoardOffset black_pawn_dirs[] = { -15, -17, -16, 0 };
BoardOffset knight_dirs[] = { -33, -31, -18, -14, 14, 18, 31, 33, 0 };
BoardOffset bishop_dirs[] = { -17, -15, 15, 17, 0 };
BoardOffset rook_dirs[] = { -16, -1, 1, 16, 0 };
BoardOffset queen_dirs[] = { -17, -16, -15, -1, 1, 15, 16, 17, 0 };
BoardOffset king_dirs[] = { -17, -16, -15, -1, 1, 15, 16, 17, 0 };
BoardOffset * piece_dirs[2][6] = {
{ white_pawn_dirs, knight_dirs, bishop_dirs, rook_dirs, queen_dirs, king_dirs },
{ black_pawn_dirs, knight_dirs, bishop_dirs, rook_dirs, queen_dirs, king_dirs }
}; quote:I have written several chess programs. Currently I am just playing around with different board representations. I''ve done several different kinds of bitboard engines in the past.
I think you''d be better off with bitboards
quote:I''m not actually using the 0x88 board representation (which is 128-elements in size). I''m using a 256 element board, which gives you the benefits of 0x88, with a few small bonuses.
Stupid question: are you overflowing your 8 bit range? |0x88 + 0x7f| = 9 bits.
quote:The reason I started in on using 8-bit variables is to make it easier to copy the whole structure. In a chess program, you will do a lot of piece copying from square to square, move copying everytime you make a move to back it up, and so on. I thought the 32-bit structure with 8-bit fields would be more efficient in the copying aspect. 1 copy vs. 4. So what would you recommend here?
Only use char when you need to save space, i.e. large arrays. Loading an 8 bit value into a 32 bit register (mov?x) is slower than a normal move, and 8 bit ops will lead to partial register stalls, which are devastating to Intel CPUs.
For 2-3 auto variables, just use int.
quote:Not sure what you mean here.
BTW, watch for [ i ] in code
Instead of doing the bit management yourself, make the compiler do it for you.
Debug the code, step through it and see what it does differently. It may have something to do with the sign-extention when the flag is promoted to an int (assuming attackable is an int).
struct mine
{
unsigned int color:8;
unsigned int type:8;
unsigned int square:8;
unsigned int dummy:8;
};
Debug the code, step through it and see what it does differently. It may have something to do with the sign-extention when the flag is promoted to an int (assuming attackable is an int).
>> I think you''d be better off with bitboards
aight.
So your board is an array of this struct? I''d just use ints, encoding the color in the MSB (or whatever). Copying is simple+fast, and it''s no bigger than the struct (smaller if the compiler padded it).
>> BTW, watch for [ i ] in code
> Not sure what you mean here.
Your post suddenly goes italic after a certain array index
quote:
I have written several chess programs. Currently I am just playing around with different board representations. I''ve done several different kinds of bitboard engines in the past.
aight.
quote:
The reason I started in on using 8-bit variables is to make it easier to copy the whole structure. In a chess program, you will do a lot of piece copying from square to square, move copying everytime you make a move to back it up, and so on. I thought the 32-bit structure with 8-bit fields would be more efficient in the copying aspect. 1 copy vs. 4. So what would you recommend here?
So your board is an array of this struct? I''d just use ints, encoding the color in the MSB (or whatever). Copying is simple+fast, and it''s no bigger than the struct (smaller if the compiler padded it).
>> BTW, watch for [ i ] in code
> Not sure what you mean here.
Your post suddenly goes italic after a certain array index
Look at the following:
On g++ 3.2, this yields an x value of 239. If you add 128 to that, you're past 256. The reason your second one works, is because you use an int, which properly stores the negative value.
EDIT: In other words, "square" should be a char, not unsigned.
[edited by - cgoat on March 13, 2003 11:31:20 AM]
int main(int argc, char *argv[])
{
unsigned char x = 0;
char y = -17;
x+=y;
printf("%d\n", x);
return 0;
}
On g++ 3.2, this yields an x value of 239. If you add 128 to that, you're past 256. The reason your second one works, is because you use an int, which properly stores the negative value.
EDIT: In other words, "square" should be a char, not unsigned.
[edited by - cgoat on March 13, 2003 11:31:20 AM]
void RayTrace (int iterations, char offset, unsigned char bit) {
{
int i;
unsigned char square;
for (i = square = 0; i < iterations; i++) {
square += offset;
attackable[square + 128] |= bit;
step[square + 128] = offset;
}
}
Ok I see it now... The error lies in the attackable[square + 128] line. The point is, square + 128 adds the value of 128 to the value of square, and returns a value the same size as square. This is why, if square is an int, it will work, and if square is a char, it won''t (8 bits is too small to actually contain more than 256 values).
Two ways to work around this are :
1° Revert square to an int
2° Use attackable[(int)square + 128]
(this will convert square to a 32bit int value, so the sum will also be done over 32 bits).
ToohrVyk
-------------
Extatica - a free 3d game engine
Available soon!
Click here to learn more
Actually, the problem lies in passing a negative value for offset. He''d have the same problem if "sq" was an unsigned int. If you take 0 and add -17 to it, you get -17. But cast -17 to an unsigned char and you get 239. Cast it to an unsigned int and you get a very large number. Thus, taking that unsigned value and adding 128 to it gives an out of range index.
quote:This is what I had done previously. I ran into a problem though. I used the least significant bit as the ''color'' bit of the piece on the square. This made it nice and easy to use the color as the index into an array. My problem was that if I had a loop such as this:
Original post by Jan Wassenberg
So your board is an array of this struct? I''d just use ints, encoding the color in the MSB (or whatever). Copying is simple+fast, and it''s no bigger than the struct (smaller if the compiler padded it).
#define WHITE 0
#define BLACK 1
#define Color(x) ((x) & 1)
if (Color(board[square]) == WHITE) {
// do stuff
}
The problem is that an empty square (which I had as 0) would also turn up as a WHITE square. If I used some other value for ''empty'' such as -1, then it will think the color of an emty square is black. That meant I had to use more than one bit, so if I''m going to use more bits, I could do as I''m doing now, get rid of an AND to find out the color, and still keep the struct at 32-bits for fast copying. I *could* have done an extra test to make sure the square wasn''t empty before I tested its color, but why do that when I can do it this way, avoid the test, and get rid of an AND?
Anyway, I started writing an amazons program last night. It''s way easier than chess. In a little over an hour''s time of coding, I have a working program.
Thanks for everyone''s help. I think I just need to play around with mixing variables and get a better grasp of it, and be more careful when I mix them.
Sorry for the late reply - missed your post.
I think checking which piece is on the square is more frequent; storing color in the LSB makes this a bit harder.
> This made it nice and easy to use the color as the index into an array.
Yes. Usually, though, I figure you''d have a color variable that''s extracted once from the square (e.g. with shr), making this moot.
Alternate way to get square color, without a memory hit: extract row and column (you probably have these already, if not, it''s a mov, and, shr), add them, check if it''s even.
I don''t see the problem. Do you need a test for non-empty, white squares? In that case, empty := piece # 0; check if color == white and piece != 0.
This is easier with a bitboard: you can easily enumerate all non-empty squares, and you just check whether it''s white.
An ''empty'' bit is redundant; you can encode this in the piece field as above. Also, cmp [color_var], 1 is no faster / smaller than test [square], COLOR_BIT.
I wonder why you need this information; also, you''re not getting rid of an and, you''re replacing it with a cmp.
Simple rule of thumb: unless you''re squeezed for space, use int in place of char.
quote:
This is what I had done previously. I ran into a problem though. I used the least significant bit as the ''color'' bit of the piece on the square.
I think checking which piece is on the square is more frequent; storing color in the LSB makes this a bit harder.
> This made it nice and easy to use the color as the index into an array.
Yes. Usually, though, I figure you''d have a color variable that''s extracted once from the square (e.g. with shr), making this moot.
quote:
My problem was that if I had a loop such as this:
#define Color(x) ((x) & 1)
if (Color(board[square]) == WHITE) {}
Alternate way to get square color, without a memory hit: extract row and column (you probably have these already, if not, it''s a mov, and, shr), add them, check if it''s even.
quote:
The problem is that an empty square (which I had as 0) would also turn up as a WHITE square.
I don''t see the problem. Do you need a test for non-empty, white squares? In that case, empty := piece # 0; check if color == white and piece != 0.
This is easier with a bitboard: you can easily enumerate all non-empty squares, and you just check whether it''s white.
quote:
If I used some other value for ''empty'' such as -1, then it will think the color of an emty square is black. That meant I had to use more than one bit, so if I''m going to use more bits, I could do as I''m doing now, get rid of an AND to find out the color, and still keep the struct at 32-bits for fast copying.
An ''empty'' bit is redundant; you can encode this in the piece field as above. Also, cmp [color_var], 1 is no faster / smaller than test [square], COLOR_BIT.
quote:
I *could* have done an extra test to make sure the square wasn''t empty before I tested its color, but why do that when I can do it this way, avoid the test, and get rid of an AND?
I wonder why you need this information; also, you''re not getting rid of an and, you''re replacing it with a cmp.
quote:
Thanks for everyone''s help. I think I just need to play around with mixing variables and get a better grasp of it, and be more careful when I mix them.
Simple rule of thumb: unless you''re squeezed for space, use int in place of char.
Topic Locked
This topic has been locked by a moderator. New replies are not allowed.
Sign in to reply to this topic.
Advertisement