CPP Dateien inkludieren

Das Forum fĂĽr Programmierer und Systemadmins. Von Shell-, Perl- und PHP-Scripts bis zur objektorientierten Programmierung mit C++.

CPP Dateien inkludieren

Beitragvon Greg » Mo 30 Aug, 2004 18:41

Sry, wenn ich nerve aber ich habe noch eine 2 fragen:

Ich habe einigen Funktionen in externe cpp Dateien ausgelagert. Nun will ich in die main.cpp die anderen cpp Dateien mit den Funktionen inkludieren.

Ich habe #include "sichern.cpp" verwendet.

FrĂĽher ging das wunderbar, seit neuem bekomme ich folgenden Fehler:
sichern.obj : error LNK2005: "void __cdecl sichern(char)" (?sichern@@YAXD@Z) bereits in main.obj definiert

Weiters wollte ich wissen wie ich dem exe Program ein individuelles *.ico Icon zuweise?

Ich danke fĂĽr eure Hilfe
Greg
Junior Board-Mitglied
Junior Board-Mitglied
 
Beiträge: 77
Registriert: So 20 Jul, 2003 21:21

Re: CPP Dateien inkludieren

Beitragvon dfx » Mo 30 Aug, 2004 21:04

Greg hat geschrieben:Ich habe #include "sichern.cpp" verwendet.


nein. nein. tu es nicht. nein, nein. nein.

man compilet jede .c(pp) datei extra. dazu hat man dann header files (.h), in denen die prototypen von funktionen stehen. diese header files includet man dann.

zb: du hast sichern.cpp mit der funktion sichern(). dazu hast du main.cpp mit main(), welches sichern() aufruft.

in sichern.cpp steht dann zb:

Code: Alles auswählen
int sichern(int blah) {
    return blah * 2;
}


dazu hast du eine sichern.h:

Code: Alles auswählen
int sichern(int);


und dann die main.cpp:

Code: Alles auswählen
#include "sichern.h"
int main(int argc, char **argv) {
    return sichern(123);
}


(stupides beispiel, sollte aber ausreichen)

die beiden .cpp files werden dann zu object files compilet (endung .o oder .obj, also zb sichern.obj und main.obj), und diese object files werden dann anschliessend zur executable verlinkt (zb irgendwas.exe).

um sicherzustellen, daĂź die prototypen in der .h auch mit den definitionen in der .c(pp) ĂĽbereinstimmen, includet man dann normalerweise auch die .h in der dazupassenden .c(pp). in diesem beispiel wĂĽrde am anfang von sichern.cpp noch ein #include "sichern.h" stehen. (ist bei c++ sogar pflicht, bei c nicht.)

niemals ein source file direkt in einem andern includen. nur header files.
dfx
Board-User Level 3
Board-User Level 3
 
Beiträge: 1368
Registriert: Do 15 Jan, 2004 19:22
Wohnort: graz

Beitragvon Greg » Mo 30 Aug, 2004 22:10

Endlich mal eine gute Erklärung zum Thema "h-Dateien". Ich habe stundenlang gegoogelt und nix kapiert :oops:

Warum sollte man dies h Dateien einbinden?
Wie kann ich dem exe Program ein individuelles *.ico Icon zuweisen?
Greg
Junior Board-Mitglied
Junior Board-Mitglied
 
Beiträge: 77
Registriert: So 20 Jul, 2003 21:21

Beitragvon dfx » Di 31 Aug, 2004 07:18

Greg hat geschrieben:Endlich mal eine gute Erklärung zum Thema "h-Dateien". Ich habe stundenlang gegoogelt und nix kapiert :oops:

Warum sollte man dies h Dateien einbinden?


siehe diverse dokumentation zum unterschied zwischen "deklaration" und "definition".

ein prototyp in einem .h file ist eine "deklaration". sie gibt an, daĂź eine funktion existiert und wie diese aufzurufen ist. funktionscode ist dabei keiner enthalten.

"definiert" werden funktionen in .c(pp) files, dh hier steht der eigentliche programmcode. jede funktion muß pro programm genau einmal definiert sein, wobei "einmal" im sinne von aufrufen des compilers zu sehen ist. dh eine bestimmte funktion darf nur genau einmal bei genau einem compiler-lauf definiert sein. für deklarationen gibt es diese einschränkung nicht.

wenn du jetzt zwei .cpp files hast, hast du zwei compiler-läufe, einen pro file. sichern() ist in sichern.cpp definiert. wenn sichern.cpp nun auch in main.cpp includiert wird, wird sichern() ein zweites mal in main.cpp (via dem incluide) definiert. beim verlinken der object files hast du dann eine kollision, daher der fehler.

da kannst natürlich auch dein altes include lassen und sichern.cpp selbst nie extra compilen (sondern nur main.cpp). keine entwicklungsumgebung wird das aber so machen, und so soll man's auch nicht machen(tm). immerhin gehen dadurch sämtliche vorteile von mehreren source files verloren. stell dir vor, du hast 50 source files, die alle in main.cpp includiert werden, und compilet wird immer nur die main.cpp. erstens fehlt dabei jeglicher überblick (irgendwann wirst du wegen fehlenden prototypen ins stolpern geraten), zweitens mußt du jedes mal bei einer änderung in nur einem file immer alle source files neu compilen (da ja immer alles auf einmal compilet wird). wenn du dagegen jedes source file extra compilest, mußt du nur das eine file neu compilen, das sich geändert hat, und anschliessend die object files neu verlinken. und das ist vor allem bei vielen source files um ein vielfaches schneller.

Wie kann ich dem exe Program ein individuelles *.ico Icon zuweisen?


afaicr geht das mittels resource editor, aber ich bin kein windows-entwickler, daher :dontknow:
dfx
Board-User Level 3
Board-User Level 3
 
Beiträge: 1368
Registriert: Do 15 Jan, 2004 19:22
Wohnort: graz

Beitragvon Greg » Di 31 Aug, 2004 10:27

dfx hat geschrieben:wenn du jetzt zwei .cpp files hast, hast du zwei compiler-läufe, einen pro file. sichern() ist in sichern.cpp definiert. wenn sichern.cpp nun auch in main.cpp includiert wird, wird sichern() ein zweites mal in main.cpp (via dem incluide) definiert. beim verlinken der object files hast du dann eine kollision, daher der fehler.


Jetzt wird mir einiges klar :idea:. Die Fehlermeldung beim linken lautete auch so ähnlich "... sichern() is already defined in main.obj"

Jetzt habe ich noch eine Frage (ich stoße während dem coden immer wieder auf neue Probs: :banghead2: ): Wie kann ich bei einer Funktion einen ganzen String zurückgeben? Ich habe eine eigene Funktion "fileread" angelegt:

Code: Alles auswählen
char fileread(char filename[100]="")
{
   char a[100]="";
   FILE *OFile1;
   OFile1 = fopen(filename,"w");
   if(OFile1!=NULL)
   {
      fscanf(OFile1,"%s",&a);
      return a;
   }

   else
      printf("Fehler");
}


Leider kann ich den String "a" nicht zurĂĽckgeben, da ich die Fehlermeldung erhalte, dass dieser String nich in char konvertiert werden kann
Greg
Junior Board-Mitglied
Junior Board-Mitglied
 
Beiträge: 77
Registriert: So 20 Jul, 2003 21:21

Beitragvon dfx » Di 31 Aug, 2004 10:48

:rtfm:

char a[100] ist ein array aus chars und ist syntaktisch (fast) äquivalent zu einem pointer auf einen char (char *). wenn du also "a" zurückgeben willst, kannst du nur einen pointer auf "a" zurückgeben, und somit muß "fileread" definiert werden als char *fileread(.....).

nun der knackpunkt: "a" liegt als lokale variable auf dem stack von fileread(). nachdem fileread() aber an den aufrufer zurĂĽckgekehrt ist (nach return), ist der stack von fileread() nicht mehr gĂĽltig und somit ein pointer auf "a" auch nicht mehr. das heiĂźt: du kannst "a" so nicht zurĂĽckgeben. entweder du legst "a" woanders hin als auf den stack von fileread() (also zb auf den heap, zb per static char a[100]), oder die aufrufende funktion muĂź einen passenden buffer ĂĽbergeben (also zb void fileread(char *filename, char *a)).

buffer overflows mĂĽssen auch berĂĽcksichtigt werden!

als alternative kannst du c++ objekte verwenden, zb string objekte, die auch aus funktionen zurückgegeben werden können. kommt drauf an, ob du reines c machen willst, oder c++.
dfx
Board-User Level 3
Board-User Level 3
 
Beiträge: 1368
Registriert: Do 15 Jan, 2004 19:22
Wohnort: graz

Beitragvon Greg » Di 31 Aug, 2004 11:34

Also ich habe die Variable "a" jetzt als Globale als in keiner Funktion definiert. Stimmt das? (Die exe wird ohne Fehler und Warnungen erstellt)
Greg
Junior Board-Mitglied
Junior Board-Mitglied
 
Beiträge: 77
Registriert: So 20 Jul, 2003 21:21

Beitragvon dfx » Di 31 Aug, 2004 11:36

ja, das ist eine möglichkeit. du mußt dann nur beachten, daß der inhalt von "a" bei jedem aufruf von fileread() überschrieben wird. somit ist fileread() u.a. weder "reentrant" noch thread-safe.
dfx
Board-User Level 3
Board-User Level 3
 
Beiträge: 1368
Registriert: Do 15 Jan, 2004 19:22
Wohnort: graz

Beitragvon Greg » Di 31 Aug, 2004 11:56

Okay, wie ist das bei den Strings mit den Leerzeichen? Wenn ich den Sicherungspfad per scanf einlese und z.B. "C:\Ein Test" eingebe wird nach "C:\Ein" gesichert?
Greg
Junior Board-Mitglied
Junior Board-Mitglied
 
Beiträge: 77
Registriert: So 20 Jul, 2003 21:21

Beitragvon dfx » Di 31 Aug, 2004 12:04

scanf ist fĂĽr spezielle zwecke gedacht und erfordert spezielle syntax. wenn du nur eine zeile string einlesen willst: fgets()
dfx
Board-User Level 3
Board-User Level 3
 
Beiträge: 1368
Registriert: Do 15 Jan, 2004 19:22
Wohnort: graz

Beitragvon Greg » Di 31 Aug, 2004 14:36

Welche Parameter brauche ich fĂĽr fgets()?

Ich habe mir das so vorgestellt:
Code: Alles auswählen
printf("\n\nBitte geben Sie einen kompletten Sicherungspfad an: \n");
      scanf("%s",ziel);

      //Befehlsdefinitionen
      sprintf(befehl1, "%s:", &quelle1);  //Ins Zielverzeichnis springen
      sprintf(befehl2, "xcopy %s %s /E",quelle1,ziel); //xcopy
      
      //Befehlsaufrufe
      system(befehl2);
Greg
Junior Board-Mitglied
Junior Board-Mitglied
 
Beiträge: 77
Registriert: So 20 Jul, 2003 21:21

Beitragvon TheProdigy » Di 31 Aug, 2004 14:40

fgets() sollte in der Hilfe genau beschrieben sein. Sonst sollte Google weiterhelfen (Stichworte: Syntax fgets):
http://www.phim.unibe.ch/comp_doc/c_manual/C/FUNCTIONS/fgets.html

Mit welcher Entwicklungsumgebung arbeitest du?

EDIT:

In deinem Codebeispiel gibt es einen Denkfehler beim Wechseln in das Zielverzeichnis (-laufwerk). Wenn der Anwender z.B. "C:\Daten" eingibt, dann wid "C:\Daten:" ausgefĂĽhrt, was keinen Sinn ergibt. Ausserdem musst du den Verzeichniswechsel auch ausfĂĽhren (was in deinem Beispiel fehlt).

Der xcopy-Befehl funktioniert auch nicht, falls dein Verzeichnis-Name Leerzeichen beinhaltet (z.B. "C:\Eigene Dateien"). In diesem Fall muss der Verzeichnisname unter doppelte AnfĂĽhrungszeichen gesetzt werden:

xcopy "C:\Eigene Dateien" "S:\Eigenes Ziel" /E

Vielleicht ist es besser, wenn du gleich eine CMD-Datei mit den gewĂĽnschten Befehlen erzeugst und diese dann ausfĂĽhrst (bzw. BAT-Datei unter Win9x):

Code: Alles auswählen
  FILE *fp;
  fp = fopen("mybackup.cmd", "w"); // wird im akt. Verzeichnis erzeugt
  fprintf( fp, "@echo off\n");
  fprintf( fp, "chdrive %s\n", sourcedir);
  fprintf( fp, "chdir %s\n", sourcedir);
  fprintf( fp, "xcopy *.* %s /E\n", targetdir); // siehe Anmerkung
  fclose( fp);
  system( "mybackup.cmd"); // ich hoffe, dass es so funzt ;)


Anmerkung:
Wenn du bereits ins Quell-Verzeichnis wechselst, dann macht diese Angabe beim xcopy keinen Sinn mehr.

HTH,
Gerald
TheProdigy
Board-Mitglied
Board-Mitglied
 
Beiträge: 101
Registriert: Mi 05 Mai, 2004 10:41

Beitragvon Greg » Mi 01 Sep, 2004 10:21

TheProdigy hat geschrieben:Mit welcher Entwicklungsumgebung arbeitest du?


MS Visual Studio 6.0 Prof.

TheProdigy hat geschrieben:fgets() sollte in der Hilfe genau beschrieben sein.


fgets() verwende ich doch um z.B. in eine Datei zu schreiben, oder? Ich verstehe das mit den Pointern nicht, ich will ja nur den Wert eines Strings einlesen, aber nix in irgentwelche Dateien schreiben.

TheProdigy hat geschrieben:In deinem Codebeispiel gibt es einen Denkfehler beim Wechseln in das Zielverzeichnis (-laufwerk). Wenn der Anwender z.B. "C:\Daten" eingibt, dann wid "C:\Daten:" ausgefĂĽhrt, was keinen Sinn ergibt. Ausserdem musst du den Verzeichniswechsel auch ausfĂĽhren (was in deinem Beispiel fehlt).


Danke fĂĽr den Tipp, aber dieser Befehl wird ja nicht ausgefĂĽhrt, es wird nur der xcopy Befehl ausgefĂĽhrt.
Greg
Junior Board-Mitglied
Junior Board-Mitglied
 
Beiträge: 77
Registriert: So 20 Jul, 2003 21:21

Beitragvon dfx » Mi 01 Sep, 2004 10:35

fgets schreibt auch nix. (fgets = File GET String, oder so ähnlich.)

Code: Alles auswählen
SYNOPSIS
       #include <stdio.h>

       char *fgets(char *s, int size, FILE *stream);
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading
stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A '\0' is stored after the last character
in the buffer.


wobei der "stream" gleich "stdin" ist.

und btw: wenn du in c oder c++ erfolgreich sein willst, solltest du daran arbeiten, dein verständnis von pointern zu verbessern. ;)
dfx
Board-User Level 3
Board-User Level 3
 
Beiträge: 1368
Registriert: Do 15 Jan, 2004 19:22
Wohnort: graz

Beitragvon TheProdigy » Mi 01 Sep, 2004 10:38

jetzt war dfx schneller ;)

Falls du etwas per Tastatur einlesen willst, kannst du auch gets() verwenden. Funktionsweise ist analog, nur die Parameter "FILE *stream" und "int size" fallen weg.
TheProdigy
Board-Mitglied
Board-Mitglied
 
Beiträge: 101
Registriert: Mi 05 Mai, 2004 10:41

Nächste

ZurĂĽck zu PROGRAMMIER FORUM

Wer ist online?

Mitglieder in diesem Forum: Majestic-12 [Bot] und 4 Gäste