Meduza3
Meduza3

Reputation: 21

Getting a "multiple definition of 'X' first defined here" error in C, although I only defined X once

There is only one definition of the name 'codes' in all of my files, although I still get an error. I tried using pragma once and ifndef, but to no avail.

Here is the error I'm getting:

marcin@marcin-VirtualBox:~/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2$ make
gcc -g checkCode.o getDigitBase10.o main.o populateCodeList.o getNextGuess.o -o solve -lm
/usr/bin/ld: getDigitBase10.o:/home/marcin/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2/funkcje.h:10: multiple definition of `codes'; checkCode.o:/home/marcin/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2/funkcje.h:10: first defined here
/usr/bin/ld: main.o:/home/marcin/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2/funkcje.h:8: multiple definition of `codes'; checkCode.o:/home/marcin/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2/funkcje.h:10: first defined here
/usr/bin/ld: populateCodeList.o:/home/marcin/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2/funkcje.h:8: multiple definition of `codes'; checkCode.o:/home/marcin/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2/funkcje.h:10: first defined here
/usr/bin/ld: getNextGuess.o:/home/marcin/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2/funkcje.h:8: multiple definition of `codes'; checkCode.o:/home/marcin/Pulpit/INA/Wstęp/Laby/Lista 4/zadanie 2/funkcje.h:10: first defined here
collect2: error: ld returned 1 exit status
make: *** [Makefile:10: all] Błąd 1

And here are all of my files:

//funkcje.h
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#pragma once
#define SIZE 1296
 
#ifndef codes
int codes[SIZE];
#endif
void populateCodeList(int codes[]);
int getDigitBase10(int n, int digit);
struct results checkCode(int trueCode,int guessCode);
int getNextGuess(int codes[]);
struct results{
    int white;
    int red;
};
 
//checkCode.c
 
#include "funkcje.h"
 
struct results checkCode(int trueCode,int guessCode){
    struct results feedback;
    feedback.red = 0;
    feedback.white = 0;
    if(getDigitBase10(trueCode, 1) == getDigitBase10(guessCode, 1)){
        feedback.red++;
    } else if(getDigitBase10(trueCode, 1) == getDigitBase10(guessCode, 2)){
        feedback.white++;
    } else if(getDigitBase10(trueCode, 1) == getDigitBase10(guessCode, 3)){
        feedback.white++;
    } else if(getDigitBase10(trueCode, 1) == getDigitBase10(guessCode, 4)){
        feedback.white++;
    } 
 
    if(getDigitBase10(trueCode, 2) == getDigitBase10(guessCode, 2)){
        feedback.red++;
    } else if(getDigitBase10(trueCode, 2) == getDigitBase10(guessCode, 1)){
        feedback.white++;
    } else if(getDigitBase10(trueCode, 2) == getDigitBase10(guessCode, 3)){
        feedback.white++;
    } else if(getDigitBase10(trueCode, 2) == getDigitBase10(guessCode, 4)){
        feedback.white++;
    } 
 
    if(getDigitBase10(trueCode, 3) == getDigitBase10(guessCode, 3)){
        feedback.red++;
    } else if(getDigitBase10(trueCode, 3) == getDigitBase10(guessCode, 1)){
        feedback.white++;
    } else if(getDigitBase10(trueCode, 3) == getDigitBase10(guessCode, 2)){
        feedback.white++;
    } else if(getDigitBase10(trueCode, 3) == getDigitBase10(guessCode, 4)){
        feedback.white++;
    } 
 
    if(getDigitBase10(trueCode, 4) == getDigitBase10(guessCode, 4)){
        feedback.red++;
    } else if(getDigitBase10(trueCode, 4) == getDigitBase10(guessCode, 1)){
        feedback.white++;
    } else if(getDigitBase10(trueCode, 4) == getDigitBase10(guessCode, 2)){
        feedback.white++;
    } else if(getDigitBase10(trueCode, 4) == getDigitBase10(guessCode, 3)){
        feedback.white++;
    }
    return feedback;
}
//getDigitBase10.c
#include "funkcje.h"
 
int getDigitBase10(int n, int digit){
 
    for(int i=1; i< digit; i++){
        n = n/10;
    }
 
    return n%10;
}
//getNextGuess.c
#include "funkcje.h"
 
int getNextGuess(int codes[]){
    while(1){
        int i = 0;
        if(codes[i] != 9999){
            return codes[i];
        }
    }
}
//populateCodeList.c
#include "funkcje.h"
 
void populateCodeList(int codes[])
{
    int i = 0;
    for(int a=1; a<=6; a++){
        for(int b=1; b<=6; b++){
            for(int c=1; c<=6; c++){
                for(int d=1; d<=6; d++){
                    codes[i++] = a*1000 + b*100 + c*10 + d;
                }
            }
        }
    }
}
//main.c
#include "funkcje.h"
 
int main(void){
    int turn = 1;
    bool won = false;
 
    int code = 1234; //Zmienić tę linijkę aby zmienić kod
    int currentGuess = 1122;
 
    populateCodeList(codes);
 
    while(!won){
 
    struct results currentResults = checkCode(code,currentGuess); // Trzymaj wyniki tury w currentResults
    printf("Ze strzałem %d, wyniki są następujące:\nRed: %d\nWhite: %d\n",currentGuess, currentResults.red, currentResults.white); // Powiedz jakie są wyniki tej tury
 
    if(currentResults.red == 4){    // Jeśli są 4 czerwone, to znaczy że komputer wygrał.
        printf("Wygrałem! Twój kod to %d",currentGuess);
        break;
    }
 
    for(int i=0;i<SIZE;i++){ // Usuń wszystkie kody, których wyniki nie równają się wynikom z tej tury
        if(checkCode(currentGuess,codes[i]).red != checkCode(code,currentGuess).red && 
            checkCode(currentGuess,codes[i]).white != checkCode(code,currentGuess).white){
            codes[i] = 9999;
        }
    } 
 
    currentGuess = getNextGuess(codes); //Weź najmniejszy nie-usunięty kod jako następny strzał
 
        turn++;
        if(turn>8){
            printf("Czas mi się skończył i nie odgadłem twojego kodu.");
            break;
        }
    }
    return 0;
}

I tried to use pragma once to avoid the linker error and tried to surround the codes definition in the ifndef order but it still produces the same error as before. While copying and pasting everything to the same file, no errors are thrown.

Upvotes: 2

Views: 243

Answers (1)

Eric Postpischil
Eric Postpischil

Reputation: 222526

At file scope, int codes[SIZE] is a special kind of declaration called a tentative definition. In spite of its name, it is not a definition (similarly to how a prospective employee on a job interview is not an employee). However, it can cause a definition to be created.

A clean way to declare and define an object that is used in multiple translation units is to declare it with extern in a header that is included in each unit that uses the object by name:

extern int codes[SIZE];

and to define the object in the source file of one translation unit:

int codes[SIZE] = { 0 };

The tentative definition form, without extern, has been in somewhat common use, particularly with GCC which treated it as a “common” symbol, described below. However, GCC’s default behavior changed in version 10, with the result that this common practice now causes multiple-definition errors. You can get the old behavior with the -fcommon switch, but generally programmers should switch to the explicit declaration form, extern codes[SIZE];.

This reason a file-scope declaration of int codes[SIZE]; is a tentative definition lies in the history of C development. C was not completely planned and designed in advance. It developed through experiments and different people in different places implementing things differently. When the C committee standardized C, they had to deal with different practices and implementations. One common practice was that of declarations such as int i; in multiple units that were intended to create a single i. (This behavior was inherited from FORTRAN, which had common objects as a similar feature.)

To accommodate this, the committee described int i; at file scope as a special kind of declaration, a tentative definition. If there is a regular definition in the same translation unit that defines the same identifier, the tentative definition acts as a plain declaration, not a definition. If there is no regular definition, the compiler (or other part of C implementation) creates a definition for the identifier as if it had been initialized with zero.

The C standard leaves reconciling of multiple tentative definitions to each C implementation; it does not define the behavior when int i; is used in multiple translation units. Prior to version 10, the default behavior of GCC was to use the “common symbol” behavior; multiple tentative definitions would be reconciled to a single definition when linking. (To support this, the compiler marks tentative definitions differently from regular definitions when creating object modules, so the linker knows which is which.) In version 10, the default changed, and GCC now treats the definitions resulting from tentative definitions as regular symbols instead of common symbols. That is why you get the “multiple definition” error from having int codes[SIZE]; in the header file.

Upvotes: 4

Related Questions