0

I'm playing around, making a small chessgame in C, cause I haven't touched C for years.

My main is a pretty simple test:

#include "ChessGame.h"

int main()
{
    initializeNewGame();
    onLift('a', '2');
    onSet('a', '3');
}

InitializeNewGame() is going to clear some buffers, and initialize the board. The board is a simple struct, defined in:

chessBoardStructure.h:

struct chessboard
{
    char board[8][8];
};

struct chessboard* board;

When initialize is called, it utilizes a header called Chessboard.h. This header has the responsibility of checking that the user is following the rules. It also knows how to initalize the board.

chessBoard.c

#include "chessBoardStructure.h"

extern struct chessboard * board;

void initializeBoard()
{
    board = malloc(sizeof(struct chessboard));
    /* Sets the array to the right chars */
 }

Now, from my understanding, I should now have defined the global variable, board. And to verify I print out the board after InitializeBoard() has been called from InitializeNewGame(). All seems fine.

Now the InitializeGame() returns to main, and onLift() is called. This should verify some rules. The problem is, when onLift() is called in ChessGame.c as:

ChessGame.c extern struct chessboard* board;

void onLift(char col, char row)
{

    short err = getValidMoves(liftedCol, liftedRow, &validEndPoints, &validRoads, board);
    if (err == -1)
        handleBadPiece();

    /* Do stuff */

}

The board is just full of -51. When I set a watch on the board in the header, I see it being initialized, and then when InitializeGame() exits scope, it becomes the good ol' -51.

What am I doing wrong here?

I miss C++. :-)

Thank you!

Edit small example I tried changing the extern around as proposed, but the same thing happened. See the below example.

main.c

#include "a.h"

int main()
{
    masterInitialize();
    printData();

    return 0;
}

(Headers not shown, due to just declarations)

a.c

#include "a.h"
#include "b.h"
#include "struct.h"
#include <stdio.h>

struct mystruct* datastruct;

void masterInitialize()
{
    initializeDataStruct();
}

void printData()
{
    for (int i = 0; i < 10; i++)
        printf("Data: %c\n", datastruct->data[i]);
}

b.c

#include "b.h"
#include "struct.h"
#include <stdlib.h>

struct mystruct* datastruct;

void initializeDataStruct()
{
    datastruct = malloc(sizeof(struct mystruct));
    for (int i = 0; i < 10; i++)
        datastruct->data[i] = 1;
}

struct.h

struct mystruct
{
    char data[10];
};

extern struct mystruct* datastruct;
Benjamin Larsen
  • 560
  • 1
  • 7
  • 21

2 Answers2

3

You've got things the wrong way around.

This should be in "chessBoard.c".

struct chessboard* board;

and this should be in your header

extern struct chessboard * board;

As that'll tell all the files where you include it that there exists the variable board declared somewhere and at link time, it'll use the one you've declared in "chessBoard.c"

With the way you have it currently, each C file that includes the header will have it's own unique version of board

Chris Turner
  • 8,082
  • 1
  • 14
  • 18
  • Hm, are you sure? As far as I can see, since the variable board isn't initialized, there should be only a single instance of it. Otherwise, since the visibility of the symbol is public, there should be a linker error, but there should never be two separate instances of `board` – Ctx Jun 26 '17 at 12:35
  • @Ctx there may be multiple extern declarations but there must be one declaration in C which is not extern. – unalignedmemoryaccess Jun 26 '17 at 12:39
  • @tilz0R Yes, but there can be multiple non-extern declarations, which cumulate to a single symbol on linking, provided at most one of these is initialized. As far as I can see, it cannot happen, that two public visible symbols of the same name result in two separate instances of that symbol, as this answer claims. – Ctx Jun 26 '17 at 12:42
  • Yup, I'm sure. The scope of a variable declared outside of a function is local to the compilation unit. The `extern` keyword is used to say that the variable is declared in another compilation unit. – Chris Turner Jun 26 '17 at 12:42
  • @ChrisTurner No, you are wrong here, I just tried to sound a bit diplomatic ;) See my comments above – Ctx Jun 26 '17 at 12:43
  • @ChrisTurner I tried as you proposed. Having the same problem. I've created a small example that does as you propose, but still fails - see the edit :) – Benjamin Larsen Jun 26 '17 at 12:43
  • @BenjaminLarsen well...you haven't done as I suggested - you declared the variable in two C files. Should only need it in only one of them – Chris Turner Jun 26 '17 at 12:47
  • @ctx that sounds like undefined behaviour - how does it know which of the many variables is the correct one to use? – Chris Turner Jun 26 '17 at 12:49
  • @ChrisTurner That's why at most one instance may have an initializer – Ctx Jun 26 '17 at 12:50
  • @ChrisTurner - that did it! – Benjamin Larsen Jun 26 '17 at 12:50
  • @ChrisTurner "The compiler treats function declarations without a storage class specifier as if they included the specifier extern. Similarly, any object identifiers that you declare outside all functions and without a storage class specifier have external linkage" – Ctx Jun 26 '17 at 12:51
  • @Ctx no you're wrong. Having multiple definitions is undefined behaviour as per C standard, but allowed in POSIX. – Antti Haapala -- Слава Україні Jun 26 '17 at 12:53
  • @AntiHaapala Then you should vote down this: https://stackoverflow.com/questions/7189982/two-variables-with-same-name-and-type-in-two-different-c-files-compile-with-g or this: https://stackoverflow.com/questions/1490693/tentative-definitions-in-c99-and-linking – Ctx Jun 26 '17 at 12:54
  • @Ctx see this linked from there: https://stackoverflow.com/questions/1490693/tentative-definitions-in-c99-and-linking – Antti Haapala -- Слава Україні Jun 26 '17 at 13:03
0

extern struct chessboard * board; doesn't define anything; it just declares the identifier board and that it is of type pointer to struct chessboard somewhere.

struct chessboard* board; is a (tentative) definition. It defines a single variable with external linkage and static storage duration. However, you've put it into the included source file, which is included in several places. Thus in every place that #includes this file will have a conflicting definition of board.

GCC on Linux will merge multiple definitions in separate translation units, but the C standard doesn't state that. Instead it says (C11 6.9:

5 [...] If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.

In your code there were multiple definitions for the same identifier, which will cause your program to have undefined behaviour, because C11 4p2:

If a ''shall'' or ''shall not'' requirement that appears outside of a constraint or runtime- constraint is violated, the behavior is undefined.[...]


However (I guess it is) POSIX prescribes the common extension J.5.11:

J.5.11 Multiple external definitions

There may be more than one external definition for the identifier of an object, with or without the explicit use of the keyword extern; if the definitions disagree, or more than one is initialized, the behavior is undefined (6.9.2).

Community
  • 1
  • 1