Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: c++ multiple definition of

Sivun loppuun

jonez [19.12.2005 22:26:57]

#

Eli minulla on kaksi .cpp tiedostoa jossa toisessa sijaitsee main funktio ynm ja toisessa ovat sitten funktiot ja includet. Nyt koitan kääntää sitä ja se herjaa:

multiple definition of (kaikki funktiot vuorollaan)
first defined here

Siis first defined missä? Siirsin ne funktiot tuohon main.cpp ja toimi kunnolla? Mikä pirhana siinä on vikana? Idenä Dev-c++.

FooBat [19.12.2005 22:56:51]

#

Todennäköisesti sinulla on tälläinen tilanne:

*** foo.c
int foo() {return 1;}
int foo2() {return 2;}


*** main.c

#include "foo.c"

int main(void) {
  return 1;
}

Ongelma syntyy siinä, että kääntäjä kääntää projektissa mukana olevat tiedostot erikseen ja sitten linkittää nämä tiedostot yhteen. Nyt käännettäessä foo.c tiedostoa määritellään funktiot foo ja foo2 ja käännettäessä main.c:tä ne määritellään toistamiseen, koska include suoraan kopioi kohdetiedoston sisällä haluttuun kohtaan, jolloin main.c näyttää kääntäjälle tältä:

*** main.c esiprosessorin jälkeen
int foo() {return 1;}
int foo2() {return 2;}

int main(void) {
  return 1;
}

Tämän jälkeen sinulla on ohjelmassasi molemmat funktiot kahteen kertaan määriteltynä ja kääntäjä valittaa.

Perusratkaisu tähän tilanteeseen on tehtä header-tiedosto, jossa vain esittelet funktiot, mutta et määrittele niitä.

Eli tyyliin:

***foo.h
#ifndef FOO_H
#define FOO_H

int foo();
int foo2();

#endif /* FOO_H */
*** foo.c
#include "foo.h"

int foo() {return 1;}
int foo2() {return 2;}

*** main.c

#include "foo.h"

int main(void) {
  return 1;
}

Nyt funktiot määritellään ja käännetään vain kerran, ja kääntäjä tietää main.c tiedostossa, että foo ja foo2 funktiot on määritelty jossain muussa modulissa.

Toivottavasti arvasin ongelmasi oikein.

jonez [21.12.2005 18:45:32]

#

Kyllä kyllä, kiitos. Kuitenkin valittaa vielä noista stringeistä siellä .h tiedostossa. Mitenköhän tämän saisi korjattua?

Tzaeru [21.12.2005 18:48:07]

#

#include <string> kenties?

jonez [21.12.2005 19:18:05]

#

Tottakai siellä on mutta ei se auta. :(

FooBat [21.12.2005 19:46:10]

#

Onko sinulla muuttujia/vakiota h-tiedossa? Jos on, niin nillä tulee vastaan samat ongelmat kuin funktiolla eli ne määritellään kahteen kertaan. Jos oikeasti tarvitset kutsua jonkun toisen modulin muuttujaa, voit esitellä sen h-tiedostossa seuraavasti:

extern string foobar;

jonez [21.12.2005 22:40:09]

#

Ei, mulla on siellä esimerkiksi:
string fooBar();

Joka siis palauttaa stringin. Anteeksi epätarkkuuteni.

Metabolix [21.12.2005 23:01:08]

#

Kaikkeen tuollaiseen laitetaan otsikossa extern eteen, jos otsikkoa on tarkoitus käyttää useammassa cpp-tiedostossa tai jos cpp-tiedostosta löytyy myös funktion määrittely. Extern (siis external, käsittääkseni) kertoo kääntäjälle, että sen ei tarvitse huolehtia funktion olemassaolosta. Tällöin on mahdollista kääntää tietyt funktiot erillään. Linkkeri hoitaa sitten niiden liittämisen yhdeksi paketiksi. Jos extern-määrittelyllä käytettyä funktiota ei tässä vaiheessa löydy, ilmoittaa linkkeri virheestä: "Unresolved external symbol".

FooBat [21.12.2005 23:45:04]

#

Onko sulla se

#include <string>

varmasti myös siellä h-tiedostossa?

jonez [22.12.2005 01:19:51]

#

On #include <string> siellä h-tiedostossa, on extern siinä stringin edessä ja silti sanoo et:

`string' does not name a type

:(

Metabolix [22.12.2005 01:24:35]

#

string => std::string tai alkuun (includen jälkeen) using std::string; (tai hätätapauksessa using namespace std;)

jonez [22.12.2005 01:28:52]

#

Kiitos kiitos, nyt se toimii. :)

Yritin kyllä aikaisemminkin tota using namescape std; mut herjas vaan. Tuo using std::string; kuitenkin toimi.

EDIT: Vielä että miten kun haluasin käyttää taulukkoa vaikka tossa stringis niin mitenköhän se onnistuisi?

Metabolix [22.12.2005 01:57:54]

#

Ihan samalla tavalla toimii kuin muukin taulukko.

int main(void)
{
    string str_taulu[3] = {
        "Matti",
        "Pekka",
        "Lasse"
    };

    cout << str_taulu[0][0] // 1. rivin 1. merkki = M
         << str_taulu[1][1] // 2. rivin 2. merkki = e
         << str_taulu[2][2] // 3. rivin 3. merkki = s
         << str_taulu[0][3] // 1. rivin 4. merkki = t
         << str_taulu[1][4] // 2. rivin 5. merkki = a
         << endl;           // Rivinvaihto

    return 0;
}

jonez [22.12.2005 02:24:01]

#

Joo mut esimerkiksi sillai et joku int funktio palauttais taulukon. Vaikka:

int yhteen(int luku, int luku1){
  int Array[2];
Array[0] = luku;
Array[1] = luku1;
Array[2] = luku+luku1;

return Array[];
}

sit jossain:
int toinenArray[2] = yhteen(1, 3);

ps. oon väsynyt joten (jos mahdollista) sekavampaa kuin yleensä tää selitys

Metabolix [22.12.2005 02:31:47]

#

Ei onnistu palauttamalla, mutta parametrinä kylläkin:

void Laita_Taulukkoon_Lukuja(int * Taulukko, int Maara, int EkaLuku)
{
  int i;
  // Kirjoitetaan Taulukko-tauluun Maara lukua alkaen arvosta EkaLuku
  for (i = 0; i < Maara; i++)
    Taulukko[i] = EkaLuku + i;
}

int main()
{
  int Luvut[10];

  // 10 lukua alkaen 9:stä, eli 9, 10, ... , 18.
  Laita_Taulukkoon_Lukuja(Luvut, 10, 9);

  return 0;
}

Gaxx [22.12.2005 11:26:08]

#

jonez kirjoitti:

Yritin kyllä aikaisemminkin tota using namescape std; mut herjas vaan.

Niin... Se on namespace, ei namescape.

Itsekin muistelin joskus alkuaikoina tuon juurin noin — väärin :)

jonez [22.12.2005 13:18:44]

#

Kiitos


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta