C Programm Fehlersuche

#1

Hi ich habe folgendes C Programm und soll es auf Fehler untersuchen

#include <stdio.h>
#include <string.h>

//input: single text argument
//output: text argument printed backwards
int main(int argc, char **argv) {
  char* buff = malloc(strlen(argv[0]));
  int i;
  for(i=sizeof(argv[0]); i>=0; i--) {
    *buff++ = argv[0]**;
  }
  printf("%s
", buff);
  free(buff);
  return 0;
}```


Meine Ideen bisher:


```#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//input: single text argument
//output: text argument printed backwards
int main(int argc, char **argv) {
  char * buff = (char *) malloc(sizeof(argv[1]+1);
  char * dest = buff;
  int i;
  for(i=strlen(argv[1])-1; i>=0; i--) {
    *dest++ = argv[1]**;
  }
  printf("%s
", buff);
  free(buff);
  free(dest);
  return 0;
}```

kriege da aber eine längliche fehlermeldung...


mfg

*** Edit ***

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//input: single text argument
//output: text argument printed backwards

int main(int argc, char **argv) {
  char* buff =  malloc(sizeof(argv[1]));
  char* dest = buff;
  int i;
  for(i=strlen(argv[1])-1; i>=0; i--){
    *dest++ = argv[1]**;
  }
  printf("%s
", buff);
  free(buff);
  
  return 0;
 }

die Version scheint jetzt bei kurzen Wörtern zu funktionieren bei längeren aber nicht...
#2

Besagte “Längliche Fehlermeldung” ist zumindest in VS erstmal nur
“missing ‘)’ before ‘;’”
was eigentlich klar sein sollte.

Ein paar Fehler sind klar, allgemein:

  • Zumindest (mein altes) VC braucht den cast nach char* bei malloc - obwohl der eigentlich NICHT nötig sein sollte. Hinschreiben “kann aber nicht schaden”
  • Ja, argv[0] ist der Name des Programms. Die eigentlichen Argumente fangen bei argv[1] an.
  • Das argv ist ein Pointer auf Pointer. Damit ist sizeof(argv[1]) die Größe eines Pointers. Und diese Größe ist 4 (auf 32 bit Systemen) bzw. 8 (auf 64 bit Systemen). Was auch erklärt, warum das Programm bei “kurzen” Worten zu funktionieren scheint. (Lass’ mich raten: “Kurz” heißt “Weniger als 8 Zeichen”? :D). Dort sollte wohl strlen(argv[1])+1 verwendet werden.
  • Die +1 und -1 die du da stochastisch verteilt hast, hängen wohl mit dem zusammen: In C gibt es keine Strings. Das, was einem String am nächsten kommt, ist ein “zero-terminated byte array”. Das Entscheidende ist, dass so ein byte- bzw. char-array NUR dann als “String” angesehen werden kann, wenn sein letztes byte 0 ist (nicht "0" oder '0', sondern 0). (Konkret: Wenn man einen “String” ohne 0 an printf übergibt, weiß das nicht, wo es mit dem printen aufhören soll, und versucht dann, Speicher zu lesen, auf den es gar keinen Zugriff hat). Dieses 0 muss man auch am Ende händisch in den Array schreiben.

Das Ziel-Array mit diesm dest-Pointer zu füllen ist zwar theoretisch geschickt und effizient, aber in diesem Fall etwas hakelig. Das kann man so machen, aber man kann es sich auch einfach machen, und ausnutzen, dass Arrays und Pointer in C … syntaktisch stark verwandt sind.


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//input: single text argument
//output: text argument printed backwards
int main(int argc, char **argv) 
{
    // Nicht auf ungültige argv[1] zugreifen!
    if (argc < 2)
    {
        printf("Eine hilfreiche Fehlermeldung
");
        return 1;
    }
    // String-Länge +1 (für die 0 am Ende)
    char* buff = (char*)malloc(strlen(argv[1])+1);
    char* dest = buff;
    int i;
    for(i=strlen(argv[1])-1; i>=0; i--){
        *dest++ = argv[1]**;
    }

    // Hier die 0 ans Ende setzen
    *dest=0;
    printf("%s
", buff);
    free(buff);
    return 0;
}

(BTW: Die C-Tags sind im Moment kaputt, da gab es Probleme mit dem Syntax-Highlighter ( @EagleEye , @L-ectron-X hatten da schon ein Auge drauf). Für “normalen”, “kurzen” C-Code einfach CODE-Tags verwenden, da bleibt’s zumindest eingerückt)

EDIT: Ach ja, das reine ANSI-C erwartet die Variablendeklarationen ganz am Anfang. D.h. irgendein strikt eingestellter Compiler würde ggf. sowas erwarten


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

//input: single text argument
//output: text argument printed backwards
int main(int argc, char **argv) 
{
    int wordLength;
    char* buff;
    char* dest;
    int i;

    // Nicht auf ungültige argv[1] zugreifen!
    if (argc < 2)
    {
        printf("Eine hilfreiche Fehlermeldung
");
        return 1;
    }

    // String-Länge +1 (für die 0 am Ende)
    wordLength = strlen(argv[1]);
    buff = (char*)malloc(wordLength+1);
    dest = buff;
    for(i=wordLength-1; i>=0; i--){
        *dest++ = argv[1]**;
    }

    // Hier die 0 ans Ende setzen
    *dest=0;
    printf("%s
", buff);
    free(buff);
    return 0;
}

#3

wow, danke für diesen ausführliche post::manklatsch::manklatsch

ich werde ihn mir mal in Ruhe durchlesen…

das mit es gibt keine Strings in C verwirrt mich erlich gesagt sehr. Einerseits sagt man es gibt keine String aber dann gibt es z.B. so eine h File <string.h> ??!?!

#4

In diesem Header stehen die Funktionen, die man braucht, um auf besagten “zero-terminated byte-arrays” die Operationen durchzuführen, die Sinn machen, wenn man sie als “Strings” interpretiert. Z.B.: “strlen” liefert (praktisch) die Position des besagten 0-bytes, was dann eben die “String-Länge” ist. (Dann noch String-Vergleiche, Kopien usw. … eben das, was in einer modernen IDE bei einer objektorientierten Sprache in einem Popup erscheinen würde, wenn man myString. eintippt :smiley: )

#5

ok, danke ich glaube ich habe es verstanden. ich habe noch eine weitere Teilaufgabe:

#include <stdio.h>


int[] times(int [] vector, int times) {
  int i;
  for(i=0; i<sizeof(vector); i++) {
    vector** *= times;
  }
  return vector;
}


int main(int argc, char **argv) {
  int[4] v = {1,1,1,1};
  vector = times(vector,2);
  printf("(%d,%d,%d,%d)", v[1],v[2],v[3],v[4]);
  return 0;
}

Zunächst zur Funktion soll das Programm einen Vektor verdoppelt ausgeben?

dann ein Fehler der mir aufgefallen ist sizeof(vector) muss man noch teilen durch die Länge eines Elements

*** Edit ***

ka, ob es dem Sinn des Programmstückes entspricht, aber ich gehe davon aus,
dass man einen Vektor mit einem Skalar multiplizieren soll…

#include <stdio.h>


struct array {
	int wert[4];
};

struct array times(struct array vector, int times) {
  int i;
  for(i=0; i<sizeof(struct array) / sizeof(int); i++) {
    vector.wert** *= times;
  }
  return vector;
}


int main(int argc, char **argv) {
  struct array v;
  v.wert[0] = 1;
  v.wert[1] = 1;
  v.wert[2] = 1;
  v.wert[3] = 1;
  v = times(v, 15);
  printf("(%d,%d,%d,%d)", v.wert[0],v.wert[1],v.wert[2],v.wert[3]);
  return 0;
}
#6

Hm. Da sind

  • einige offensichtliche Dinge falsch
  • einige Details falsch die (wie ich zugeben muss) mir auch nicht (mehr) durch Draufschauen auffallen
  • einige Sachen falsch, die mit diffizilen Spezifika von C zu tun haben

Offensichtlich ist z.B. dass die Variable vector in der main nicht deklariert ist, oder dass die indizes bei 0 anfangen (und nicht bei 1).

Details sind z.B., dass
int[4] v = {1,1,1,1}; eben
int v[4] = {1,1,1,1};
heißen muss. (Ich programmiere üblicherweise nicht mehr in C (wenn überhaupt dann in C++), und Dinge, die man z.B. von Java gewohnt ist, sind in C schlicht falsch (und das Bewußtsein dafür fehlt (mir)) - deswegen hätte ich das jetzt auch nicht “erkannt”, sondern habe es mehr oder weniger durch “ausprobieren” rausgefunden).

Bei den “diffizilen Spezifika von C” kommen jetzt die Sachen, wo ich auch durch mangelnde Praxis nicht mehr ganz so firm bin. (Vor ca. 10 Jahren kannte ich mich damit mal “etwas besser” aus, aber in der Zwischenzeit hat sich viel getan, deswegen sollte man alles, was ich dazu sage, mit etwas Skepsis aufnehmen, und hinnehmen, dass es vielleicht “etwas unpräzise” ist).

Ein wichtiger Punkt ist das Verhältnis zwischen Pointern und Arrays in C. Die sind weitgehend gleich. Man kann bei beiden auf die Elemente mit pointerOrArray[index] zugreifen. Ein Array ist praktisch kaum zu unterscheiden von einem Pointer - nämlich einem Pointer auf das erste Array-Element.

[ot]
Tatsächlich geht diese “Gleichwertigkeit” sehr (SEHR!) tief in die Sprache runter. Es klingt verrückt, aber es gilt tatsächlich:
array** ist das gleiche wie
*(array + i) ist das gleiche wie
*(i + array) ist das gleiche wie
i[array]

Das hier läßt sich also wirklich compilieren:


  int array[4] = { 12, 34, 56, 78 };
  int index = 2;
  int element = index[array]; // <---------- WTF !?
  printf("%d
", element);

und gibt “56” aus…
[/ot]

Richtig fummelig wird das aber, wenn arrays an Funktionen übergeben werden. DORT sind die Rollen dann etwas anders. GANZ grob gesagt: Der Typ eines Funktionsparameters ist dann nicht “Irgendein Array”, sondern ein Array mit einer bestimmten Größe. Man kann also (bitte nicht festnageln, aber IIRC) eine Funktion wie
void myFunction(int array[4]) { ... }
nicht mit einem Array der Größe 5 aufrufen. Wenn man die Arraygröße aber NICHT angibt, ist das IIRC dann doch wieder gleichbedeutend mit
void myFunction(int *array) { ... }

(Vielleicht kann @SlaterB da auch noch klärend eingreifen - IIRC war der auf Spotlight auch gelegentlich im C-Forum aktiv (vielleicht verwechsle ich da auch gerade was)).

Langer Rede gar kein Sinn: Wenn man arrays an eine C-Funktion übergeben will, macht man das üblicherweise so, dass man einen Pointer übergibt (nämlich auf das erste Array-Element), und zusätzlich die Länge des übergebenen Arrays. (Die kriegt man aus dem Pointer selbst nämlich nicht mehr raus - sizeof(pointer) liefert immer 4 oder 8).

Auch einen Array aus einer Funktion zurückzugeben ist heikel - speziell wenn der Array nur lokal definiert ist. Und sowas wie
int[] giveMeSomeArray() { ... }
geht gar nicht.


Die Funktionsdeklaration würde also eher so aussehen:
void times(int vector[], int length, int times)
Oder, analog dazu
void times(int *vector, int length, int times)

Der Aufruf wäre dann mit
times(v,sizeof(v)/sizeof(int),2);
möglich - wie du schon sagtest: Da muss durch die Elementgröße geteilt werden. Warum das “sizeof” DORT dann DOCH funktioniert? Weil der Array lokal als Array mit einer bestimmten Größe bekannt ist (und nicht nur als Pointer).

(Sorry, ich bräuchte da auch mal wieder eine kleine Auffrischung :o )

#7

in dem Buch c von a bis z (was für ein buchtitel :D) steht das man das Array in ein struct eintüten soll^^

#8

Naja (schnelle Websuche zur Referenz: Rheinwerk Computing :: C von A bis Z – 11.5 Übergabe von Arrays an Funktionen )

Wie aber ist vorzugehen, wenn ein Array unbedingt als Kopie übergeben werden soll? Sie können das Array in eine Struktur verpacken und dann an die Funktion übergeben.

Das kann man machen, WENN der Array als Kopie übergeben werden SOLL. (Und entsprechend, wenn eine Kopie zurückgegeben werden SOLL, was dann ja später beschrieben ist). Man bedient sich dabei der Tatsache, dass structs eben anders behandelt werden, als Arrays - nämlich dass sie “by value” übergeben werden, auch wenn sie einen Array enthalten (der dann eben kopiert wird). Ich kann mich nicht erinnern, das schonmal in dieser Form gesehen zu haben, aber kann jetzt keine technisch-fundierte Bewertung dazu abgeben.

(Nur eine unfundierte: Ich finde das nicht besonders hübsch -_- . Auf jeden Fall sollte man sich über die Implikationen im Klaren sein: Es werden wirklich Kopien erstellt. Zumindest ein Kommentar wie // Wrap array into struct to be able to pass it by value könnte nicht schaden. Websuchen liefert sowas wie Is it a best practice to wrap arrays and their length variable in a struct in C? - Stack Overflow , wo auch gesagt wird, dass das ~~“schon OK sein kann”, aber man sollte wohl nicht pauschal JEDEN Array in eine struct wickeln, sondern sich klar machen, wie das Verwendungsmuster aussehen soll…)