C question: pointer arithmetic

Discussion of chess software programming and technical issues.

Moderators: hgm, Dann Corbit, Harvey Williamson

Forum rules
This textbox is used to restore diagrams posted with the [d] tag before the upgrade.
Karlo Bala
Posts: 337
Joined: Wed Mar 22, 2006 9:17 am
Location: Novi Sad, Serbia

Re: C question: pointer arithmetic

Post by Karlo Bala » Wed Jun 25, 2008 8:32 pm

Zach Wegner wrote:No, that still wouldn't work. You would only get the address of the parameter, which is on the stack. Unfortunately I think the only way to fix it is to change the parameter.
There is lot of ways to do wrong thing with pointers of different types. That's why I prefer raw pointers and explicit casting :wink:
Best Regards,
Karlo Balla Jr.

Posts: 838
Joined: Thu Jul 05, 2007 3:03 pm
Location: British Columbia, Canada

Re: C question: pointer arithmetic

Post by wgarvin » Thu Jun 26, 2008 6:53 am

hgm wrote:The way I figured it ChessSquare would indeed be a single byte (less than 256 piece types). Thus Board would be an 8x8 array of bytes, i.e. 64 bytes.
Board is an array type, i.e. "array of 8 of (array of 8 of (ChessSquare))"
hgm wrote:I expect board and boards[0] to be of the same type, namely type Board. Objects of type Board should be pointers, as they can be indexed (twice). So I am taking the difference of two pointers of the same type.
The parameter board has the same array type as Board, which means you have to pass something which is a Board to it. But when the name of a variable with array type is used without indexing, it can decay into a non-array type, in this case "pointer to (array of 8 of (ChessSquare))".

I think what's happening is that both array expressions (board and boards[0]) are decaying into pointers, and you then compute the difference between those pointers. The pointers both have the same type, "pointer to (array of 8 of (ChessSquare))". Since sizeof(array of 8 of ChessSquare) is 8*sizeof(ChessSquare), and sizeof(Board) is 64*sizeof(ChessSquare), it makes sense that you'd get (64/8) == 8 as the result.

I can't think of any sensible explanation for getting 16, though!
hgm wrote:But I was led to believe that when subtracting two pointers, the difference (in bytes) would be divided by the size of the object pointed to, i.e. boards[1] - boards[0] should always be 1, irrespective of sizeof(boards[0]).
That doesn't sound right to me. However, (&boards[1] - &boards[0]) should always be 1, since it is morally equivalent to ((boards+1) - (boards+0)).
hgm wrote:The fact that the address of boards[1] was passed as an argument called board should not really matter. Yet I get 16, and François 8 (presumably because the size of 64 bytes is divided by sizeof(int), and I have a 32-bit machine). This seems incorrect to me. Especially that sizeof(int) should have an effect is very fishy, as ints did not enter the equation anywhere...

Am I stupid or what? :shock:
It does in fact pass the address of boards[1], but that parameter doesn't have the type of pointer-to-type-of(boards[1]). Its passing a pointer to ChessSquare[8], not a pointer to ChessSquare[8][8].

I wonder if the ChessSquare[8] type somehow decays into a ChessSquare* ? That would seem like a compiler bug, but if it did, you would have a ChessSquare** and taking 64 bytes divided by sizeof(ChessSquare*)==4 would give you your 16. Hmm, I'm confused..

User avatar
Posts: 26128
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller

Re: C question: pointer arithmetic

Post by hgm » Thu Jun 26, 2008 9:20 am

Well, forget about the 16, that might have been my mistake. But I am not in a position anymore to make sure, and now that I understand what was wrong, the details of how wrong it was are not very important.

I now convert everything to (int) before doing any arithmetic,

p = ((int) board - (int) boards[0]) / ((int)boards[1] - (int) boards[0]);

and this seems save and portable enough, albeit a bit ugly and cumbersome.

User avatar
Zach Wegner
Posts: 1922
Joined: Wed Mar 08, 2006 11:51 pm
Location: Earth

Re: C question: pointer arithmetic

Post by Zach Wegner » Thu Jun 26, 2008 3:25 pm

Karlo Bala wrote:There is lot of ways to do wrong thing with pointers of different types. That's why I prefer raw pointers and explicit casting :wink:
I think that is just an argument against what you are doing. My method only compares pointers of the same type, while yours looks very hacky to me, fudging pointers into something they are not. It just happens to work because Board is already a pointer.

Posts: 3969
Joined: Thu May 15, 2008 7:57 pm
Location: Berlin, Germany
Full name: Sven Schüle

Re: C question: pointer arithmetic

Post by Sven » Thu Jun 26, 2008 4:11 pm

Hi Harm-Geert,

since passing arrays - especially multi-dimensional ones - as function arguments is a common source of errors I would always strongly recommend to avoid it. An improvement would be to turn the array into a struct containing the array. The following code takes few characters more but I think it is functionally equivalent to the original and much safer:

Code: Select all

typedef enum { Pawn, ... King } ChessSquare;

typedef struct { ChessSquare sq[8][8]; } Board;

Board boards[500];

void Apply(Board * board)
    int p = board - &(boards[0]);
    printf("%d\n", p);
It requires to access a square by "board->sq[x][y]" instead of the shorter "board[x][y]" but this has no runtime impact. You would have to change all your code that uses "Board" resp. "Apply()", though. But the effort is worth it.

It turns "Board" into a typedef to a data type that can be handled much easier than a typedef for a pointer to "something that is not easy to tell without thinking".

It avoids too much fiddling with C/C++ pointer arithmetic which is not easy when it comes to multi-dimensional arrays.

It avoids some horrible casts.

It takes less time to understand the code, and to see that it is bug-free.

Just my 2 cents ...


User avatar
Posts: 26128
Joined: Fri Mar 10, 2006 9:06 am
Location: Amsterdam
Full name: H G Muller

Re: C question: pointer arithmetic

Post by hgm » Thu Jun 26, 2008 4:26 pm

Sven Schüle wrote:Just my 2 cents ...
Yes, I agree. If I would have set it up from scratch I would have put the 8x8 board in a struct, and I would have put the e.p. status and castling rights as other fields in that same struct.

But alas, we are talking about an existing source, which is huge by my standards (distributed over many files, typical length of a single file 10,000 lines). A source with very little comments, which I largely have to reverse engineer to know what it is doing in the first place. Even specification of its interface (on the ICS side) seem to be non-existent. So one guide-line I adopted is "never change anything unless it is absolutely necessary, and keep the possible scope of any change as small as possible".

So if it is possible to solve this without changing the type of the parameter, this is the preferred way.

Post Reply