C: Speichern eines Arrays in einen Array durch Funktionsaufruf

Hey,
nächstes "Problem". Einfaches Beispiel zuerst:

Ich speichere A in B mittels Pointer und Adressen:
Code:
void save(int A, int *B)
{
    *B=A;
}
void main()
{
    int A,B;
    A=4; B=;
    trade(A,&B);
}
Das klappt soweit auch. Nun eine Nummer höher: Arrays. (Ist das hier überhaupt korrekt?)

Code:
typedef int val[2];

void save(val A, val *B)
{
    *B[0] = A[0];
    *B[1] = A[1];
}
void main()
{
    val A = {1,2};
    val B;
    trade(A,&B);
}

Dies liefert dann B=[0,0], also klappt das nicht. Das muss an der speziellen Struktur von B liegen. Hat jemand einen Tip, wie ich das korrekt umsetze?

Danke :)

Edit: Ein Problem ist mir bereits aufgefallen: Ich fordere val *B und übergebe &B, eine Adresse, also ein Int/String (wie auch immer..) das kann nicht funktionieren.

Dafür kann ich doch aber ein int *b fordern, dem ich die Adresse von B gebe. Dann speichere ich A in *b...ich teste mal ;)

Kleine Anpassung. Nun klappt's.

Code:
typedef int val[2];

void save(val A, int *b)
{
    *(b+0) = A[0];
    *(b+1) = A[1];
}
void main()
{
    val A = {1,2};
    val B;
    trade(A,&B[0]);
}



Neues Problem
Angenommen, ich habe nun einen sehr langen Array nach obigen Vorbild. Ich würde dann ja gerne mittels
Code:
int k=0;
while(A[k])
    *(b+k) = A[k++];
diesen Speichervorgang durchführen. Mein letzter Versuch endete hier aber in einer Endlosschleife. Anregungen? :)

Wie sieht ein array "int A[3]" aus? Ist hier mit A=[1,2,3]
0 -> 1; 1 -> 2; 2 -> 3; 3 -> "/0"
wie bei char?
 
Zuletzt bearbeitet:
In C ist ein Array im Wesentlichen nichts anderes, als ein Pointer. Deswegen kann eine externe Funktion auch die Größe des Array nicht kennen.
Es sei denn, das Array ist im selben Code Abschnitt definiert worden - dann kann die Größe in Bytes mittels des sizeof-Operators ermittelt werden.
Wenn das nicht geht, wird sie in C üblicherweise übergeben. So z.B.:
Code:
#include <stdio.h>

void safe (const int *A, int *B, const int length)
{
  /* Wir kriegen hier nur Zeiger - Die Größe der Arrays muss uns also extra gesagt werden. */
  int ii = 0;
  for (ii = 0; ii < length; ++ii)  {
    B[ii] = A[ii];
  }
}

int main (int argc, char *argv[])
{
  int my_array[3] = {1, 2, 3};
  /* Das Array wurde hier definiert, der Compiler kennt also seine Größe. */
  const int array_size = sizeof (my_array) / sizeof (int);
  int my_second_array[array_size];
  safe (my_array, my_second_array, array_size);
  
  int ii;
  printf ("A = (");
  for (ii = 0; ii < array_size; ++ii)  {
    printf ("%d, ", my_array[ii]);
  }
  printf (")\nB = (");
  for (ii = 0; ii < array_size; ++ii)  {
    printf ("%d, ", my_second_array[ii]);
  }
  printf (")\n");

  return 0;
}

Allerdings muss man das Rad nicht neu erfinden. Es gibt bereits eine schöne C-Funktion in der Standardbibliothek namens memcpy:
Code:
#include <string.h>

int main (int argc, char *argv[])
{
  int my_array[3] = {1, 2, 3};
  int my_second_array[3];
  memcpy ((void*) my_second_array, (void*) my_array, sizeof (my_array));
}

Viele Grüße,
Pik-9

Edit: Zum besseren Verständnis:

Wenn man in C ein Array definiert und sich anschließend auf den Namen, ohne irgendwelche Zeichen bezieht, ist das ein Zeiger.
Code:
/* Definiere ein Array. */
int array[3] = {1, 2, 3};

/* Das ist quasi ein Zeiger auf int auf das erste (nullte) Element des Arrays. */
int *pointer = array;

/* Genauso kann ein Zeiger wie ein Array behandelt werden. */
pointer[2] = 43;

/* Nimm dich bloß vor Segmentation Faults in Acht! Oh, oh! */
pointer[100] = 666;

In C muss man seinen Speicher ganz alleine verwalten. Das ermöglicht sehr präzises Programmieren, öffnet aber auch schwer zu findenden Programmierfehlern Tür und Tor.
 
Zuletzt bearbeitet:
Das klappt soweit auch. Nun eine Nummer höher: Arrays. (Ist das hier überhaupt korrekt?)

Code:
typedef int val[2];

void save(val A, val *B)
{
    *B[0] = A[0];
    *B[1] = A[1];
}
void main()
{
    val A = {1,2};
    val B;
    trade(A,&B);
}

Dies liefert dann B=[0,0], also klappt das nicht. Das muss an der speziellen Struktur von B liegen. Hat jemand einen Tip, wie ich das korrekt umsetze?
Code:
void save(val A, val B)                                                                
{                                                                                      
        B[0] = A[0];                                                                   
        B[1] = A[1];                                                                   
}                                                                                      
                                                                                       
int main(int argc , char* argv[argc +1]) {                                             
    val A = {3,4};                                                                     
    val B;                                                                             
    save(A,B);
"Arrays are not first class citizens".
Sprich, als Funktionsparameter ist es "automatisch" ein Pointer (auf das erste Element),.
Afaik, ist das ganze historisch bedingt - sobald man ein Array in ein struct einbindet, kann es "by value" übergeben werden.

Angenommen, ich habe nun einen sehr langen Array nach obigen Vorbild. Ich würde dann ja gerne mittels
Code:
int k=0;
while(A[k])
    *(b+k) = A[k++];
diesen Speichervorgang durchführen. Mein letzter Versuch endete hier aber in einer Endlosschleife. Anregungen? :)

Wie sieht ein array "int A[3]" aus? Ist hier mit A=[1,2,3]
0 -> 1; 1 -> 2; 2 -> 3; 3 -> "/0"
wie bei char?
Es schaut wie ein kontinuierlicher Speicherblock aus, ohne irgendwelche Meta/Laufzeitinformationen (Länge, Typ usw).
Natürlich kann man für sich eine Konvention ausarbeiten und eigene Arrays mit '\0' terminieren - sofern man den Wert 0 nicht braucht ;)
 
Oh, das wusste ich über Arrays noch nicht, oder hab es bereits wieder vergessen. Je mehr ich drüber nachdenke, umso bekannter kommt es mir aber vor.

Die Sprache gefällt mir immer besser. ;)

Jeder Size_Of Code benötigt doch selber ebenso Rechenzeit, da er ja den Array entlang durchgeht. Ich hatte gedacht, dass Arrays ähnlich Zeichenketten sind. Eine Zeichenkette wird ja auch mittels char text[] = {"A", "B", "C", "\0"} beendet. Gibt es am Ende eines Arrays kein Trigger, der dem Leser sagt: Hier ist mein Ende?

Natürlich ist mir vorher bekannt, wie groß die Arrays sind, die ich erwarte. Aber es hätte mich ja schon interessiert :D

Mal eine andere Frage, angenommen ich habe einen Array derart:

Code:
static const short mapping[16] = {6,5,4,3,2,1,0,9,8,7, 10, 11, 13, 12, 14, 15};

typedef int val[16];
typedef val key[16];

static const key factor= {ar0, ..., ar15};

void make(key A, key B)
{
    short i;
    for(i=0; i<16; i++){
        mult_val( A[mapping[i]] , factor[i] , B[i] );
    }
}

void mult_val(val A, val B, val C)
{
    short i;
    for(i=0; i<16; i++){
        C[i] = A[i]*B[i];
    }
}

Würde so das Speichern klappen? Btw. ich nutze hier "short i" anstelle von "int i", da ich nur 4-Bit für i brauche. Mir ist kein kleinerer Datentyp bekannt, der weniger Speicher verbraucht als short. Als Orientierung gucke ich hin und wieder auf [1. Spielt das überhaupt eine Rolle, welchen Datentyp ich für i nutze? Nutzt der Compiler nicht den Speicher entsprechend des Inhalts und nicht des zu erwartenden Inhalts?


[1] C-Programmierung: Datentypen – Wikibooks, Sammlung freier Lehr-, Sach- und Fachbucher
 
Zuletzt bearbeitet:
Jeder Size_Of Code benötigt doch selber ebenso Rechenzeit, da er ja den Array entlang durchgeht. Ich hatte gedacht, dass Arrays ähnlich Zeichenketten sind. Eine Zeichenkette wird ja auch mittels char text[] = {"A", "B", "C", "\0"} beendet. Gibt es am Ende eines Arrays kein Trigger, der dem Leser sagt: Hier ist mein Ende?

Natürlich ist mir vorher bekannt, wie groß die Arrays sind, die ich erwarte. Aber es hätte mich ja schon interessiert :D

Mal eine andere Frage, angenommen ich habe einen Array derart:
Der sizeof Operator geht nicht durch das Array durch und sucht nach einer 0! Er wird überhaupt nicht zur Laufzeit ausgeführt - er wird vom Compiler durch einen konstanten Wert ersetzt. Deshalb muss der Compiler die Array-Definition auch an der Stelle kennen, an der er benutzt wird. Andernfalls würde es ja auch mit Zeigern innerhalb einer Funktion funktionieren.
Es gibt in der Standardbibliothek eine Funktion namens strlen(const char*) welche genau das tut: Sie geht ein char Array durch, bis sie auf ein \0 stößt. Kleines Beispiel:
Code:
#include <string.h>
#include <stdio.h>

int main (int argc, char *argv[])
{
  char msg[] = {'H', 'a', 'l', 'l', 'o', 0x0, 'W', 'e', 'l', 't', 0x0};
  printf ("Echte Groesse: %d\n", sizeof (msg));
  printf ("Zur Laufzeit ermittelte Groesse: %d\n", strlen (msg));
  printf ("String: %s\n", msg);
  return 0;
}

Ausgabe:
Code:
$ ./a.out
Echte Groesse: 11
Zur Laufzeit ermittelte Groesse: 5
String: Hallo

Viele Grüße,
Pik-9
 
Mal eine andere Frage, angenommen ich habe einen Array derart:
Code:
static const short mapping[16] = {6,5,4,3,2,1,0,9,8,7, 10, 11, 13, 12, 14, 15};

typedef int val[16];
typedef val key[16];

static const key factor= {ar0, ..., ar15};

void make(key A, key B)
{
    short i;
    for(i=0; i<16; i++){
        mult_val( A[mapping[i]] , factor[i] , B[i] );
    }
}

void mult_val(val A, val B, val C)
{
    short i;
    for(i=0; i<16; i++){
        C[i] = A[i]*B[i];
    }
}

Würde so das Speichern klappen? Btw. ich nutze hier "short i" anstelle von "int i", da ich nur 4-Bit für i brauche. Mir ist kein kleinerer Datentyp bekannt, der weniger Speicher verbraucht als short. Als Orientierung gucke ich hin und wieder auf [1. Spielt das überhaupt eine Rolle, welchen Datentyp ich für i nutze? Nutzt der Compiler nicht den Speicher entsprechend des Inhalts und nicht des zu erwartenden Inhalts?


[1] C-Programmierung: Datentypen – Wikibooks, Sammlung freier Lehr-, Sach- und Fachbucher
In deinem Code gibt es ein paar kleine Probleme:
  1. static gibt innerhalb von Funktionen an, dass dieser Wert über mehrere Funktionsaufrufe hinweg erhalten bleiben soll. Bei einer globalen Definition macht er keinen Sinn.
  2. Da du in der Methode make die Funktion mult_val aufrufst, muss diese auch zuerst deklariert werden.

So könnte es funktionieren:

Code:
#include <stdio.h>

const short mapping[3] = {2, 0, 1};

typedef int val[3];
typedef val key[3];

const key factor= {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

void mult_val(const val A, const val B, val C)
{
    short i;
    for(i=0; i<3; i++){
        C[i] = A[i]*B[i];
    }
}

void make(const key A, key B)
{
    short i;
    for(i=0; i<3; i++){
        mult_val( A[mapping[i]] , factor[i] , B[i] );
    }
}

void print_key(const key X)
{
    printf ("Key = [");
    int ia, ib;
    for (ia = 0; ia < 3; ++ia)  {
        printf ("[");
        for (ib = 0; ib < 3; ++ib)  {
            printf ("%d, ", X[ia][ib]);
        }
        printf ("], ");
    }
    printf ("]\n");
}

int main (int argc, char *argv[])
{
    const key my_key = {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}};
    print_key (my_key);
    key after_make;
    /* Some unpredictable numbers from REM */
    print_key ((const int (*)[]) after_make);
    make (my_key, after_make);
    print_key ((const int (*)[]) after_make);
    return 0;
}

Zur Größe der Datentypen:
Auf den meisten 64-Bit Systemen wird es folgendermaßen gehandhabt:
Datentyp
Größe
long
64 Bit (8 Byte)
int
32 Bit (4 Byte)
short
16 Bit (2 Byte)
char
8 Bit (1 Byte)
Ja, char ist tatsächlich ein sehr kleiner Integer. ;)

Aber wenn es bei deinem Programm wirklich auf die Größe von Variablen ankommt (wie z.B. bei Verschlüsselungsalgorithmen), würde ich stdint.h einbinden.
Diese bietet Typen wie int64_t, uint32_t, int8_t, usw. per typedef an. Das ist dann auch unabhängig von den Compiler-Einstellungen.

Viele Grüße,
Pik-9
 
Jeder Size_Of Code benötigt doch selber ebenso Rechenzeit, da er ja den Array entlang durchgeht.
Nope. Der Standard besagt:
The sizeof operator yields the size (in bytes) of its operand, which may be an
expression or the parenthesized name of a type. The size is determined from the type of
the operand.
The result is an integer. If the type of the operand is a variable length
type, the operand is evaluated; otherwise, the operand is not evaluated and the result
integer constant.
D.h. größtenteils Compiletimeauswertung - die einzige Ausnahme sind VLAs (variable length arrays), deren Implementierung aber wiederum in C11 optional ist.
Ich hatte gedacht, dass Arrays ähnlich Zeichenketten sind.
Andersherum - "Zeichenketten" sind in C einfach nur Arrays mit etwas Syntaxzucker drauf und einer Konvention bezüglich \0 Terminierung.
Gibt es am Ende eines Arrays kein Trigger
Wie stellst Du es Dir vor? Zum einen wird beim Arbeiten mit Zeichenketten sowieso oft die Länge zwischengespeichert, da die Laufzeit von O(n) bei jeder Längenermittlung nicht so der Traum ist,
zum anderen darf man entweder einen bestimmten Wert nicht nutzen dürfen (dies sollte man auch nicht vergessen und strickt einhalten) oder muss einen "breiteren" Typ für die Werte benutzen, um erlaubte Werte und die Terminierung zu unterscheiden.
Dann wäre das simple Mitspeichern der Länge viel günstiger.

Mal eine andere Frage, angenommen ich habe einen Array derart:

Code:
static const short mapping[MY_CONST_LEN] = {6,5,4,3,2,1,0,9,8,7, 10, 11, 13, 12, 14, 15};

typedef int val[MY_CONST_LEN];
typedef val key[HARDCODED_VALUES_ARE_BAD];
...
Würde so das Speichern klappen? Btw. ich nutze hier "short i" anstelle von "int i", da ich nur 4-Bit für i brauche.
Prinzipiell sehe ich keine Probleme. Ich würde nur nicht so sehr auf typedefs setzen - die sind nämlich in C nur simple Aliase (also Umbenennung und nicht etwa Bildung eines "distinct" Typs) und erschweren in solchen Fällen (Arraytyp-aliasing) imho die Lesbarkeit.

Mir ist kein kleinerer Datentyp bekannt, der weniger Speicher verbraucht als short. Als Orientierung gucke ich hin und wieder auf [1. Spielt das überhaupt eine Rolle, welchen Datentyp ich für i nutze? Nutzt der Compiler nicht den Speicher entsprechend des Inhalts und nicht des zu erwartenden Inhalts?
Z.B char. Ist auch in der Tabelle[1] gelistet. Und jap, Zeichen in C gibt's auch nicht, nur ein bisschen Syntaxzucker mit den Literalen und char ist ein numerischer Wert mit dem sich ganz normal rechen lässt ;)
Schöner ist es allerdings, bei solchem Bedarf (Datentypen bestimmter Breite)
int8_t/uint8_t/int16_t/uint16_t/usw. aus der <stdint.h> zu benutzen ;)

Edit: werde wohl alt und langsam :(
 
Zuletzt bearbeitet:
Oh, ich hatte gestern sogar noch was gelesen, dass man mittels static const globale Konstanten deklariert. Es gibt in dem Projekt ein paar Koeffizienten, die einfach nur Rechenpower fressen würden, wenn ich sie innerhalb des Lebenszyklus der Funktion berechnen würde. Diese würde ich vorberechnen, mittels Sage, und das Ergebnis kompatibel speichern.

Es gibt in meinem Projekt ein paar Konstanten, die ich Global definieren möchte. Ob das sinnvoll ist, sei erstmal dahin gestellt :) Darüber muss ich mir dann noch separat Gedanken machen.

Den vorgeschlagenen Code teste ich direkt mal. Danke, an euch beide :)



Nochmal zum Thema static.
Angenommen ich rufe eine Funktion zum ersten Mal auf und berechne darin Variablen, lassen sich diese beim nächsten Aufruf dann als vordefiniert festlegen? Also angenommen ich habe in Runde (#)

#1
Berechne: A,B,C,D
speichere: A*B*C*D

#2
Berechne: A,B,C,D
speichere: A*B*C*D

...

könnte ich den Berechnungsschritt dann optional überspringen? Wo landen die Variablen dann? Sind diese so lange im RAM wie möglich? Ich glaube, dass mein RAM sehr klein ist. Ich habe 2MB Flash und irgendwas im kb Bereich an RAM. Mein Programm wird vermutlich weit unter den 2MB sein. Aktuell ist meine *.bin 24KB groß. Es scheint mir also, als hätte ich noch massig Platz.

@HARDCODED_VALUES_ARE_BAD
nested-loops are more bad :p
 
Zuletzt bearbeitet:
Nochmal zum Thema static.
Angenommen ich rufe eine Funktion zum ersten Mal auf und berechne darin Variablen, lassen sich diese beim nächsten Aufruf dann als vordefiniert festlegen? Also angenommen ich habe in Runde (#)

#1
Berechne: A,B,C,D
speichere: A*B*C*D

#2
Berechne: A,B,C,D
speichere: A*B*C*D
Das Problem ist, dass die Initzialisierungswerte für static-variablen in C zur Kompilierungszeit bekannt sein müssen.
D.h., dass elegante "static foo bar = myfunc();" wird eher nicht klappen.
Man kann aber natürlich drum'rum arbeiten, in dem man z.B "static foo bar = x;" macht und dann, vor der Berechnung abfragt, ob der Wert != x.
Oder eine zusätzliche Variable dafür einführt. Oder eine Art Zähler an die Funktion übergibt.
Letztendlich unterscheidet sich hier die Handhabung der function-lokalen "static" Variable nicht wirklich von der "global" deklarierten Variante (außer, dass diese Variable nur einen beschränkten Sichtbarkeitsbereich hat).

Wo landen die Variablen dann? Sind diese so lange im RAM wie möglich?
Also, wo die Variablen genau landen, sollte implementierungsspezifisch sein (afaik – finde nichts auf die Schnelle im Standard, es sollte aber prinzipiell nicht von Globalvariablen unterscheiden und DATA/BSS Section in der Executable nutzen).
"static" bezieht sich auf die Lebenszeit der Variable (es gibt noch automatic, allocated) – das heißt, dass die Variable über die gesamte "Lebenszeit" des Programms verfügbar ist.
 
Nochmal zum Thema static.
Angenommen ich rufe eine Funktion zum ersten Mal auf und berechne darin Variablen, lassen sich diese beim nächsten Aufruf dann als vordefiniert festlegen? Also angenommen ich habe in Runde (#)

#1
Berechne: A,B,C,D
speichere: A*B*C*D

#2
Berechne: A,B,C,D
speichere: A*B*C*D

...

könnte ich den Berechnungsschritt dann optional überspringen? Wo landen die Variablen dann? Sind diese so lange im RAM wie möglich? Ich glaube, dass mein RAM sehr klein ist. Ich habe 2MB Flash und irgendwas im kb Bereich an RAM. Mein Programm wird vermutlich weit unter den 2MB sein. Aktuell ist meine *.bin 24KB groß. Es scheint mir also, als hätte ich noch massig Platz.

@HARDCODED_VALUES_ARE_BAD
nested-loops are more bad :p

Variablen, die innerhalb einer Funktion als static deklariert wurden, befinden sich im Data/BSS Segment.
Siehe: Programmieren lernen von Anfang an: c:memorylayout - proggen.org
Dieser Link ist sehr gut für das Verständnis der Speichersegmente in einem Prozess.
Ein kleines Beispiel für eine statische Variable:
Code:
#include <stdio.h>

int calc_sum (int x)
{
  static int sum = 0;
  sum += x;
  return sum;
}

int main (int argc, char *argv[])
{
  printf ("Anfangswert: %d\n", calc_sum (0));
  printf ("1. Schritt: %d\n", calc_sum (3));
  printf ("2. Schritt: %d\n", calc_sum (2));

  return 0;
}

Natürlich kannst auf diesem Wege auch statische Konstanten definieren, welche dann natürlich nur innerhalb der Funktion, in der sie definiert wurden, zugänglich sind.
Wenn du eine Variable oder Konstante außerhalb jedes Rumpfes definierst, ist sie automatisch global, dazu bedarf es keines static mehr.

Viele Grüße,
Pik-9

Edit: Diesmal war CDW schneller! :wink:
 
Zuletzt bearbeitet:
Ah ok, dann verstehe ich das jetzt langsam.

Ich wollte am Ende eine Lib erstellen lassen, die die definierten Variablen, Datentypen und Funktionen enthält. Das sollte dann mit Variablen, die ich in einer Header definiert habe, kein Problem sein.

Jetzt kann ich einen Schritt weiter gehen und mal gucken, ob ich irgendwo modulo p-Arithmetik portieren/abgucken kann :D In Sage ist das Leben diesbezüglich wirklich leicht :( Man definiert sich erst einen endlichen Körper und legt dann eine Variable als Element des Körpers fest (nicht symbolisch..). Anschließend werden alle Operationen "^, +, *, -" als Körperoperationen interpretiert. Auch wenn man einen Erweiterungskörper definiert und diesen als Polynom über diesem Körper auffasst wird die Arbeit dadurch erheblich vereinfacht. :) Aber C kennt keine Klassen und es geht auch ohne ganz gut.
 
Zurück
Oben