C question: pointer arithmetic

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

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

C question: pointer arithmetic

Post by hgm »

Suppose I have

Code: Select all

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

typedef ChessSquare Board[8][8];

Board boards[500];

void Apply(Board board)
{
    int p = board - boards[0];
    printf("%d\n", p);
}
When I now call Apply(boards[1]), is there any particular reason why this would print 16, rather then 1?
Carey
Posts: 313
Joined: Wed Mar 08, 2006 8:18 pm

Re: C question: pointer arithmetic

Post by Carey »

hgm wrote:Suppose I have

Code: Select all

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

typedef ChessSquare Board[8][8];

Board boards[500];

void Apply(Board board)
{
    int p = board - boards[0];
    printf("%d\n", p);
}
When I now call Apply(boards[1]), is there any particular reason why this would print 16, rather then 1?
That would depend on the contents of board[1] and board[0]

You are finding the difference between their values. So it should depend on whatever you initialize them with.

Perhaps the snippet you are showing isn't what you were meaning?

What are you actually wanting to do?
FrancoisK
Posts: 80
Joined: Tue Jul 18, 2006 10:46 pm

Re: C question: pointer arithmetic

Post by FrancoisK »

I got 8 on my PC.
The compiler treats the passed pointer as if it was declared as ChessSquare[8]* board (which at first surprised me as much as you :wink:). As the pointed type weighs 8 chars (the enum is compiled as one char here), we have 8 of them between the 2 pointers.
hgm wrote:Suppose I have

Code: Select all

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

typedef ChessSquare Board[8][8];

Board boards[500];

void Apply(Board board)
{
    int p = board - boards[0];
    printf("%d\n", p);
}
When I now call Apply(boards[1]), is there any particular reason why this would print 16, rather then 1?
Karlo Bala
Posts: 373
Joined: Wed Mar 22, 2006 10:17 am
Location: Novi Sad, Serbia
Full name: Karlo Balla

Re: C question: pointer arithmetic

Post by Karlo Bala »

hgm wrote:Suppose I have

Code: Select all

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

typedef ChessSquare Board[8][8];

Board boards[500];

void Apply(Board board)
{
    int p = board - boards[0];
    printf("%d\n", p);
}
When I now call Apply(boards[1]), is there any particular reason why this would print 16, rather then 1?
I think you are mixing pointers with different sizes.

board is pointer to ChessSquare (8*8 elements)
boards[0] is pointer to Board (500*8*8 elements)

You should explicitly write what kind of pointer distance you want.

For example:

Code: Select all

int p = (ChessSquare*)board - (ChessSquare*)boards[0] -> 64
int p = (Board*)board - (Board*)boards[0] -> 1
Best Regards,
Karlo Balla Jr.
User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: C question: pointer arithmetic

Post by hgm »

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.

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.

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]).

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:
User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: C question: pointer arithmetic

Post by hgm »

Karlo Bala wrote: For example:

Code: Select all

int p = (ChessSquare*)board - (ChessSquare*)boards[0] -> 64
int p = (Board*)board - (Board*)boards[0] -> 1
So you are saying that I am mixing up pointers after all (although not within the expression): Although board and boards[0] are pointers, they are not (Board *), but they point to an 8-byte row of ChessSquare. That would explain the 8 (but not the 16).

I was pretty sure I got all multiples of 16 (but in the mean time I fixed it by writing

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

(This seemed the safest, given that I didn't understand what was going on).

This was actually a part of the WinBoard source code, where the game history is stored in the array boards[] (so that you can scroll through the game easily). I want to derive the index of the current position in the routine ApplyMove(), while it only gets passed the address of the current board. This because I need to use that same index in an array castlingRights[] and epStatus[], which did not exist in the original WinBoard. ApplyMove() can then update the castling and e.p. rights together with the rest of the position.
User avatar
Zach Wegner
Posts: 1922
Joined: Thu Mar 09, 2006 12:51 am
Location: Earth

Re: C question: pointer arithmetic

Post by Zach Wegner »

Your pointer logic is wrong. When you pass in a Board "by value" like that, it only almost works because it's an array that can be treated like a pointer. But it is pointing to something else, a *ChessSquare[8]. You need pointers to the boards themselves, not their first elements.

Code: Select all

typedef enum { Pawn, King } ChessSquare;

typedef ChessSquare Board[8][8];

Board boards[500];

void Apply(Board *board)
{
            int p = board - boards;
                printf("%d\n", p);
}

int main()
{
        Apply(&boards[1]);
        return 0;
}
...prints 1. No casts required.
Karlo Bala
Posts: 373
Joined: Wed Mar 22, 2006 10:17 am
Location: Novi Sad, Serbia
Full name: Karlo Balla

Re: C question: pointer arithmetic

Post by Karlo Bala »

hgm wrote:
Karlo Bala wrote: For example:

Code: Select all

int p = (ChessSquare*)board - (ChessSquare*)boards[0] -> 64
int p = (Board*)board - (Board*)boards[0] -> 1
So you are saying that I am mixing up pointers after all (although not within the expression): Although board and boards[0] are pointers, they are not (Board *), but they point to an 8-byte row of ChessSquare. That would explain the 8 (but not the 16).

I was pretty sure I got all multiples of 16 (but in the mean time I fixed it by writing

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

(This seemed the safest, given that I didn't understand what was going on).
I think that problem arise after passing array as function parameter.
boards[0], board[1],... are pointers to Board and working with 8x8 element memory blocks. Board is 8x8 array. Whenever you pass array through param it became pointer to n-1 right most dimension sub-array. So, after passing board[0] as board(param), board(param) became pointer to 8 element memory block. It seems that most of us got 8 as result of Apply(Board board) function, but in case of mixing pointers result should be undefined and could depend on compiler.

Here is compiler output (VS2005):

Code: Select all

004113BE  mov         eax,dword ptr [board] 
004113C1  sub         eax,offset boards (417178h) 
004113C6  sar         eax,5 
004113C9  mov         dword ptr [p],eax 
Divide by 32 looks very strange to me and I haven't good explanation.

One way to get 8 as result:

Code: Select all

typedef ChessSquare Row[8];
int p = (Row*)board - (Row*)boards[0];
Maybe compiler made some similar conversion and we got 8, but 16? It is very strange...
Best Regards,
Karlo Balla Jr.
User avatar
hgm
Posts: 27796
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: C question: pointer arithmetic

Post by hgm »

Zach is right, what I forgot is to apply the unary & to the pointers, and I did so because they already where pointers / addresses (but not to what I was thinking). I intended to write

p = &board - &boards[0];

which is the same as

p = &board - boards;

I am starting to doubt about the 16 now; the faulty code is already gone, and so is its output, so I can't easily check. Perhaps I was unlucky that the routine was only called for even values of the index.

I don't want to fiddle with the type of the argument of Apply, as it is called from many other places, but applying the & operator after passing as an argument works just as well (because what is passed is still a pointer; it would not work if Board was a non-pointer type).
User avatar
Zach Wegner
Posts: 1922
Joined: Thu Mar 09, 2006 12:51 am
Location: Earth

Re: C question: pointer arithmetic

Post by Zach Wegner »

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.