Array als Returnwert

Schnelle objektorientierte, kompilierende Programmiersprache.
chris_1981_
Beiträge: 72
Registriert: Sa Jun 15, 2013 8:41 pm

Array als Returnwert

Beitrag von chris_1981_ » So Feb 02, 2014 2:23 pm

Hallo zusammen,

entschuldigt solche eine Frage, ich weiß das ihr euch mit "wichtigerem" hier beschäftigt, aber ich begreife es im Moment einfach nicht, ja vielleicht sollte ich mir eine andere Berufung suchen, aber so ist das, wenn man mit etwas anfängt und vor lauter Bücher den Papierwald nicht sieht ;) .
Ich bin wirklich dabei C zu lernen, ab und an habe ich halt Rückfragen.

Wer jetzt noch Interesse hat, um folgenden Code geht es:

Code: Alles auswählen

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

char reverse_string( char string[80])
{
         return (string[80]);
}

int main (int argc, char **argv)
{
    int test;
    int i;
    int a = 0;
    char info[80] = "";
    char rev_info[80] = "";
    char testchar[80] = "diesisteinarraytest";
                 
    test = strlen (testchar);

    printf("Laenge: %i \n", test);
                        
    for (i=0;i<=test;i++)
    {
        if ( i>=2 && i<= 4 )
        {
            info[a] = testchar[i];
            a++;
        }
    }

    rev_info[80] = reverse_string(info);     
                               
    printf("testinfo: %s \n", rev_info);             
    return 0;
}
Jetzt steht in einigen Büchern, dass man in Funktionen kein Return Array nutzen kann. (?)
Wenn ich den Code so compiliere und danach ausführe, bekomme ich nichts zurückgegeben.

Hier fehlt mir noch das Verständnis.

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Array als Returnwert

Beitrag von mfro » So Feb 02, 2014 3:59 pm

chris_1981_ hat geschrieben:...Jetzt steht in einigen Büchern, dass man in Funktionen kein Return Array nutzen kann. (?)...
Was in deinen Büchern steht, ist völlig richtig. Die einzige Möglichkeit in C, aus einer Funktion einen Datentyp als Returnwert zurückzugeben, der in der Größe die eines Maschinenwortes übersteigt, ist eine Struktur.

Das ist hier aber überhaupt nicht das Problem. Du machst etwas völlig anderes als Du denkst.

Code: Alles auswählen

    char info[80] = "";
Das ist - völlig korrekt - die Deklaration, Definition und Initialisierung (alles in einem) eines Zeichenfeldes. Im Langtext: "info ist ein Feld aus 80 Zeichen, dessen erstes Element mit 0 initialisiert ist, der Rest ist undefiniert".


Code: Alles auswählen

char reverse_string(char string[80]) { ...
Das ist die Deklaration und Definition einer Funktion. In natürlicher Sprache: "reverse_string ist eine Funktion, die ein 80 Zeichen langes Zeichenfeld (in der Funktion unter dem Namen "string" bekannt) als Argument annimmt und ein einzelnes Zeichen zurückliefert".

Code: Alles auswählen

         return (string[80]);
Das hier ist - obwohl es sehr ähnlich aussieht - etwas völlig anderes.

Keine Definition und keine Deklaration (die steht nämlich schon oben), sondern an dieser Stelle ein "Ausdruck" ("expression"). Was hier steht, heißt: "gebe das 80zigste Zeichen des Zeichenfeldes "string" als Funktionsergebnis zurück".

Abgesehen davon, daß die erlaubten Indizes in dieses Feld nur von 0 bis 79 gehen, ist das völlig korrekter Code.

Aber es ist eben ganz und gar nicht die Rückgabe eines Feldes, sondern nur die eines einzelnen Feldelementes (dessen Inhalt hier undefiniert ist).

chris_1981_ hat geschrieben: Wenn ich den Code so compiliere und danach ausführe, bekomme ich nichts zurückgegeben.
Hier fehlt mir noch das Verständnis.
Ich hoffe, Du verstehst jetzt, warum. Aber auch hier denke ich - wie gestern schon - hat dein Compiler bestimmt gemeckert.

Tip: Lies' die Warnungen und Fehlermeldungen, die dein Compiler ausspuckt (das macht der nämlich nicht nur, um dich zu ärgern). Die Ausgaben können dir helfen, deine Fehler zu verstehen, der Compiler kann nämlich - zumindest im Augenblick noch - besser C als Du ;)
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

chris_1981_
Beiträge: 72
Registriert: Sa Jun 15, 2013 8:41 pm

Re: Array als Returnwert

Beitrag von chris_1981_ » So Feb 02, 2014 4:56 pm

Hallo mfro,

danke für deine Mühe und Geduld!
Ja, ich habe ein Problem mit dem Verständnis, da ich immer wieder andere Scriptsprachen hier vermische.
Ich habe versucht, wie in Perl, ein String Array zurückzugeben, genau das muss ich lassen.
Keine Definition und keine Deklaration (die steht nämlich schon oben), sondern an dieser Stelle ein "Ausdruck" ("expression"). Was hier steht, heißt: "gebe das 80zigste Zeichen des Zeichenfeldes "string" als Funktionsergebnis zurück".

Abgesehen davon, daß die erlaubten Indizes in dieses Feld nur von 0 bis 79 gehen, ist das völlig korrekter Code.
OK, verstanden und ja - ich habe es anders aufgefasst.
Mir hätte es auffallen müssen, als ich einzelne Stringzeichen übergeben habe, das hat wunderbar funktioniert.
Tip: Lies' die Warnungen und Fehlermeldungen, die dein Compiler ausspuckt (das macht der nämlich nicht nur, um dich zu ärgern). Die Ausgaben können dir helfen, deine Fehler zu verstehen, der Compiler kann nämlich - zumindest im Augenblick noch - besser C als Du
Es gibt wirklich keine Fehlermeldungen bei dieser Compilierung.
Ich will ja wohl die Fehlermeldungen richtig interpretieren aber manchmal verstehe ich leider nicht genau worum es geht, deswegen hier die Fragen.

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Array als Returnwert

Beitrag von mfro » So Feb 02, 2014 5:20 pm

chris_1981_ hat geschrieben: Es gibt wirklich keine Fehlermeldungen bei dieser Compilierung.
Welchen Compiler benutzt Du? Meiner sagt:

Code: Alles auswählen

t.c:32:2: warning: array index 80 is past the end of the array (which contains 80 elements) [-Warray-bounds]
        rev_info[80] = reverse_string(info);     
        ^        ~~
t.c:16:2: note: array 'rev_info' declared here
        char rev_info[80] = "";
        ^
1 warning generated.
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3125
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Array als Returnwert

Beitrag von cloidnerux » So Feb 02, 2014 7:53 pm

Welchen Compiler benutzt Du? Meiner sagt:
Compilierst du mit "-Wall"?
Ich hab so eine Fehlermeldung noch nicht gesehen.
Es gibt wirklich keine Fehlermeldungen bei dieser Compilierung.
Ich will ja wohl die Fehlermeldungen richtig interpretieren aber manchmal verstehe ich leider nicht genau worum es geht, deswegen hier die Fragen.
Man muss sich weder für Unwissenheit schämen noch rechtfertigen. Jeder fängt mal an und da kann es passieren, dass man neue Konzepte und Paradigmen nicht auf Anhieb versteht. Das einzige was schlimm ist, ist nicht lernen.
Also, wenn du etwas nicht verstehst, frag ruhig. Du lernst etwas und jemand anderes in deiner Situation kann davon auch profitieren.
Ja, ich habe ein Problem mit dem Verständnis, da ich immer wieder andere Scriptsprachen hier vermische.
Ich habe versucht, wie in Perl, ein String Array zurückzugeben, genau das muss ich lassen.
Man kann in C Arrays zurückgeben, aber nicht explizit. In Perl ist ein String-Array ein Objekt das von der Laufzeitumgebung gemanaged wird. In C ist ein Array ein stück Speicher von dem du die Speicheradresse des ersten Elementes kennst und wie viele Elemente gespeichert sind über die Initialisierung.
Deswegen kannst du nicht einfach ein beliebiges Array zurückgeben, da du ja zwei Informationen zurückgeben musst, Position und Länge.
Du könntest aber dich darauf einigen, immer nur eine bestimmte Länge zu nutzen und musst nur noch die Position im Speicher zurückgeben, daher einen Pointer:

Code: Alles auswählen

char * returnArray()
{
    char array[10];
    return array;
}
Aber, hier beginnt die Arbeit mit Pointern(Zeigern). Damit darf man nicht leichtfertig hantieren, da du sonst Daten und Code im Speicher überschreiben kannst und dein Programm abstürzen wird.
Redundanz macht wiederholen unnötig.
quod erat expectandum

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Array als Returnwert

Beitrag von mfro » So Feb 02, 2014 8:34 pm

cloidnerux hat geschrieben: Du könntest aber dich darauf einigen, immer nur eine bestimmte Länge zu nutzen und musst nur noch die Position im Speicher zurückgeben, daher einen Pointer:

Code: Alles auswählen

char * returnArray()
{
    char array[10];
    return array;
}
Aber, hier beginnt die Arbeit mit Pointern(Zeigern). Damit darf man nicht leichtfertig hantieren...
Ups. Stimmt, Pointer sind gefährlich, wenn man nicht wirklich weiß, was man da tut. Dein Beispielcode ist allerdings ein denkbar schlechtes Beispiel (oder ein gutes, wie man's nimmt).

Bitte so nicht nachmachen. Diese Funktion hat einen grundsätzlichen, fatalen Fehler.
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

chris_1981_
Beiträge: 72
Registriert: Sa Jun 15, 2013 8:41 pm

Re: Array als Returnwert

Beitrag von chris_1981_ » So Feb 02, 2014 10:53 pm

Hallo zuammen,

ja mit -Wall wird es deutlich sinniger, damit kann man auch die Meldungen besser verstehen.
Ich arbeite mit "vim" (vi -improved) und leider steht in den meisten Bücher nur noch etwas von Eclipse, ja das geht auch, aber ich finde der Lerneffekt ist höher, wenn man alles "ohne" Hilfsmittel durchführen kann.

Ich versuche jetzt mit einem Struct das Array zurück zu geben, bevor man sich an Pointer setzt.

Danke!

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Array als Returnwert

Beitrag von mfro » Mo Feb 03, 2014 7:57 am

chris_1981_ hat geschrieben: Ich versuche jetzt mit einem Struct das Array zurück zu geben, bevor man sich an Pointer setzt.
Aber bitte nicht so wie in dem Beispiel von cloidnerux oben (da hat er nicht aufgepaßt).

Das gibt die Adresse einer lokalen, automatischen Variable zurück, die außerhalb der Funktion keine Gültigkeit besitzt. So produziert man sehr schwer zu findende, gemeine Fehler - weil's manchmal tut und meistens nicht, abhängig vom Kontext. Der zurückgegebene Zeiger zeigt nach dem Verlassen der Funktion ins Nirwana.

Grundsätzlich halte ich es für keine besonders gute Idee, zu versuchen, in C Perl-Verhalten nachzubilden. Zumindest nicht mit den Sprachelementen, die Du bislang kennst. Du wirst damit in genau die Probleme reinrennen.

C ist eine "Stack-Maschine", d.h. lokale Variablen, Funktionsparameter und -Rückgabewerte werden (vorwiegend) über den Prozessorstack abgewickelt. Das ist effektiv und schnell für "schmale" Datentypen und bequem für den Compilerbauer (lokale Variablen "verschwinden" von alleine - "automatisch", wenn sie nicht mehr gebraucht werden, deswegen heißen sie auch so: "auto").

Für den Programmierer heißt das allerdings, daß es - (wie schon erwähnt) abgesehen von Strukturen - keine Möglichkeit gibt, aus Funktionen komplexe Datentypen "by value" zurückzugeben. Diese Ausnahme (Struktur) ist so "esoterisch", daß viele Programmierer gar nicht (oder nicht mehr - wenn sie's mal gelernt haben) wissen, daß sie existiert. Ganz nebenbei ist sie nicht besonders effektiv (der Prozessorstack ist für solche Dinge nicht gedacht).

Auch wenn's didaktisch suboptimal ist, trotzdem das Beispiel dazu:

Code: Alles auswählen

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

struct stringtrick
{
    char langer_string[1024];
};

struct stringtrick byValue(void)
{
    struct stringtrick s;

    strcpy(s.langer_string, "Das ist ein langer String als Funktionsrückgabewert\n");
    return s;
}

int main(int argc, char *argv[])
{
    printf("%s\n", byValue().langer_string);
}
Ich behaupte: die meisten C-Programmierer wissen gar nicht, daß das geht (und würden auch hier Zeiger, malloc() und free() benutzen). Ich würde empfehlen - auch wenn's geht - es ganz schnell wieder zu vergessen (es ist also nicht schlimm, wenn Du nicht gleich verstehst, was daran so besonders ist).

Lebe mit diesen Restriktionen.

Für dich bedeutet das: solange Du noch nicht weißt, wie man in Funktionen mit dynamischem Speicher umgeht (malloc, free, ...), dürfen sie nur Speicherbereiche zurückgeben, die Du vorher von außen hineingegeben hast. Was wie eine Einschränkung aussieht (und auch eine ist), erzwingt effiziente Programme: die C "Runtime" muß sich nicht mit Gültigkeitsbereichen und automatischen Kopien rumschlagen. Wenn der Programmierer die unbedingt haben will, muß er sie explizit selber hinschreiben.
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Array als Returnwert

Beitrag von Xin » Mo Feb 03, 2014 9:42 am

chris_1981_ hat geschrieben:entschuldigt solche eine Frage, ich weiß das ihr euch mit "wichtigerem" hier beschäftigt
Die Forenkultur da draußen muss echt am ... sein. ^^
chris_1981_ hat geschrieben:, aber ich begreife es im Moment einfach nicht, ja vielleicht sollte ich mir eine andere Berufung suchen, aber so ist das, wenn man mit etwas anfängt und vor lauter Bücher den Papierwald nicht sieht ;) .
Ich bin wirklich dabei C zu lernen, ab und an habe ich halt Rückfragen.
Punkt 1: Ich empfehle mal dreist die Anfänge des C-Tutorials hier zu lesen. Ich habe früher Leuten C beigebracht und reite da sehr schön auf den Begriffen Wert, Variable und Datentyp rum, weil ich genau weiß, dass man das als Anfänger erstmal kapieren muss...
chris_1981_ hat geschrieben: Wer jetzt noch Interesse hat, um folgenden Code geht es:

Code: Alles auswählen

char reverse_string( char string[80])
{
         return (string[80]);
}
... wie man hier sieht. Du gibst den 81. Buchstaben eines 80 Zeichen langen Arrays zurück. Den Funktionsnamen nach willst du den String umdrehen?
chris_1981_ hat geschrieben: Jetzt steht in einigen Büchern, dass man in Funktionen kein Return Array nutzen kann. (?)
Wenn ich den Code so compiliere und danach ausführe, bekomme ich nichts zurückgegeben.
Doch, die Kopie des 81. Buchstaben eines 80 Zeichen langen Strings. Du verwendest Index 80, obwohl 80 Zeichen von 0 bis 79 angesprochen werden. Was Du zurückbekommst, weiß kein Mensch ^^

Du möchtest ein Array zurückgeben. Das geht so nicht, Du musst den Zeiger auf das erste Element des Arrays zurückgeben: (char *).

Code: Alles auswählen

char * reverse_string( char string[80] )
{
         return "Hallo proggen.org";
}
So bekommst Du schonmal überhaupt etwas zurück. Nochmals der Tipp, Dir unser Tutorial von vorne bis hinten durchzulesen. Du musst Dir irgendwo Speicher zurechtlegen, auf den dessen erstes Element verweisen kannst. Das geht mit malloc und free, oder Du drehst den String, den Du bekommst - der hat ja schon ein Stück Speicher, dass zu ihm gehört. Hier gibt es verschiedene Möglichkeiten.
cloidnerux hat geschrieben: Deswegen kannst du nicht einfach ein beliebiges Array zurückgeben, da du ja zwei Informationen zurückgeben musst, Position und Länge.
Du könntest aber dich darauf einigen, immer nur eine bestimmte Länge zu nutzen und musst nur noch die Position im Speicher zurückgeben, daher einen Pointer:

Code: Alles auswählen

char * returnArray()
{
    char array[10];
    return array;
}
Aber, hier beginnt die Arbeit mit Pointern(Zeigern). Damit darf man nicht leichtfertig hantieren, da du sonst Daten und Code im Speicher überschreiben kannst und dein Programm abstürzen wird.
Wer hat Dich eigentlich zum Moderator gemacht?* :-D

Das Stück Code hier bitte sofort vergessen oder als absolutes No-Go merken!
Es wird ein Array als lokale Variable angelegt und der Zeiger auf das erste Element zurückgegeben. Bei der Rückgabe endet die Funktion und damit auch die Existenz der lokalen Variablen. Der Zeiger, der bei der aufrufenden Funktion ankommt ist damit ungültig. Darum: Böse Falle.


(*) Bin froh, Dich als Moderator zu haben. :-)
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

chris_1981_
Beiträge: 72
Registriert: Sa Jun 15, 2013 8:41 pm

Re: Array als Returnwert

Beitrag von chris_1981_ » Mo Feb 03, 2014 6:03 pm

Hallo zusammen,

danke für die vielen Antworten und die Erklärungen.
Vielleicht sollte ich mal erläutern, was dieses mit dem Text auf sich hat?

Also folgender Fall:
Ich bekomme ein x Zeichen langes Array über die Serielle Schnittstelle.
Das Array ist wie folgt aufgebaut:
0791947101670000040C91942143568700003121911273324004D4F29C0E
Na? Erkennt es einer? Ja es handelt sich um einen PDU String vom Handy.
Dieser String ist halt auch so aufgebaut das "ein Werte" paar verdreht ist.
Diesen gilt es nun lesbar zu machen und wiederum die Möglichkeit zu schaffen per Serieller Datenübertragung eine SMS zu versenden. Ich weiß, dass es diese "Umwandlung" auch schon fertig im Netz gibt, aber wie soll ich denn lernen, wenn ich nicht mal versuche dieses Problem selber zu lösen.

Um das ganze auf die Spitze zu treiben, soll ein Atmega 32 demnächst diese Aufgabe erledigen.
Klar, was mache ich dann hier, da wäre doch mikrocontroller.net besser geeignet.
Wenn man aber so wie ich, noch keine Idee hat wie man weiter vorgehen kann, muss ich das ganze erst in C probieren und nicht direkt auf einem Microcontroller.

Jetzt noch eine Frage:
Würdet ihr das "empfangenen" String array weiterverarbeiten?
Oder wie würdet ihr so etwas anfangen?

Vielen Dank!

Antworten