Forum: Mikrocontroller und Digitale Elektronik Variale Anzahl Werte auf Stack schreiben


von Emax (Gast)


Lesenswert?

Hi,

wie kann ich unter C eine variable Anzahl an Werten auf den Stack legen?

Ich habe ein Programms und binaris (Quellcode nur teilweise), dass 
bisher Hex-Werte als Steuerzeichen erhält. Dieser Quellcode soll orginal 
bleiben.
Ich muss eine Art Kommandointerpreter hinzufügen, der Befehle von einem 
Terminal erhält, diese auf den Stack schiebt und dann die Funktionen so 
aufruft, wie die Hex-Routine tut. Die Hex-routine selbst muss dabei 
erhalten bleiben. Je nach Schnittstelle (CAN oder RS232) wird entweder 
die Hex-Funktion verwendet, oder der Interpreter.
Ich habe mir verschiede Parser und Interpreter aus der Codesammlung 
angeschaut. Da sind die Funktionen so vorbereitet, dass die ihre Werte 
aus einen array aus unions lesen. Das kann ich nicht machen. Ich muss 
zur Laufzeit ermitteln, was ich wie auf den Stack schiebe und dann eine 
Funktion aufrufen.

Dazu muss ich je nach Befehl immer andere Daten auf den Stack schreiben, 
nur wie mach ich das in C? Ich kenne stdarg um eine variale Anzahl an 
Parametern vom Stack zu holen, wie heißen die Umkehrfunktionen dafür?

Emax

von Klaus W. (mfgkw)


Lesenswert?

Die gibt es nicht.
Wenn es überhaupt geht, dann nur mit Assembler und/oder Schweinereien.

von (prx) A. K. (prx)


Lesenswert?

Separaten programmgesteuerten Stack dafür verwenden.

von holger (Gast)


Lesenswert?

>Wenn es überhaupt geht, dann nur mit Assembler und/oder Schweinereien.

Z.B. malloc(). Igitt;)

von Peter (Gast)


Lesenswert?

sollte das ganze nicht mit einer Variabel Parater liste wie bei printf 
gehen?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das kommt darauf an, welcher ABI die jeweilige Compilerimplementierung 
folgt.

Der einzig sinnvolle Weg, eine nicht-ABI-konforme (Assembler)-Funktion 
mit den Parametern zu versorgen, die sie braucht, ist einen 
Assembler-Wrapper zu schreiben, die die Funktion so einpackt, daß sie 
ABI-konfirm ist und von Compiler-Ebene aus verwendbar wird.

von Klaus W. (mfgkw)


Lesenswert?

Was ich mir hier als Schweinerei vorstellen könnte:

Man könnte sich die zu übergebenden Bereiche in einem
anderen Puffer zusammenbauen und den als eine Folge von z.B int
interpretieren.
Dann ruft man eine passende aus einem Satz von Funktionen auf,
die dann die nötige Anzahl Werte aus dem Puffer nimmt und als
Parameter an deine Endfunktion liefert.

Also etwa so:
1
#include <stdlib.h>
2
#include <stddef.h>
3
#include <stdio.h>
4
//#include <string.h>
5
#include <stdint.h>
6
#include <stdarg.h>
7
8
void malmehrmalweniger( int anzahl, ... )
9
{
10
  int    i;
11
12
  va_list    val;
13
  va_start( val, anzahl );
14
15
16
  for( i=0; i<anzahl; ++i )
17
  {
18
    int   aktueller_wert = va_arg( val, int );
19
    printf( "%d 0x%08x\n", i, aktueller_wert );
20
  }
21
  va_end( val );
22
}
23
24
void ruf_malmehrmalweniger_auf_1( int wert1 )
25
{
26
  malmehrmalweniger( 1, wert1 );
27
}
28
29
void ruf_malmehrmalweniger_auf_2( int wert1, int wert2 )
30
{
31
  malmehrmalweniger( 2, wert1, wert2 );
32
}
33
34
void ruf_malmehrmalweniger_auf_3( int wert1, int wert2, int wert3 )
35
{
36
  malmehrmalweniger( 3, wert1, wert2, wert3 );
37
}
38
39
void ruf_malmehrmalweniger_auf_4( int wert1, int wert2, int wert3, int wert4 )
40
{
41
  malmehrmalweniger( 4, wert1, wert2, wert3, wert4 );
42
}
43
44
void ruf_malmehrmalweniger_auf( void *p, size_t n_bytes )
45
{
46
  switch( n_bytes )
47
  {
48
    case 1:
49
    case 2:
50
    case 3:
51
    case 4:
52
      ruf_malmehrmalweniger_auf_1( ((int*)p)[0] );
53
      break;
54
55
    case 5:
56
    case 6:
57
    case 7:
58
    case 8:
59
      ruf_malmehrmalweniger_auf_2( ((int*)p)[0], ((int*)p)[1] );
60
      break;
61
62
    case 9:
63
    case 10:
64
    case 11:
65
    case 12:
66
      ruf_malmehrmalweniger_auf_3( ((int*)p)[0], ((int*)p)[1], ((int*)p)[2] );
67
      break;
68
69
    case 13:
70
    case 14:
71
    case 15:
72
    case 16:
73
      ruf_malmehrmalweniger_auf_4( ((int*)p)[0], ((int*)p)[1], ((int*)p)[2], ((int*)p)[3] );
74
      break;
75
76
    default :
77
78
      break;
79
  }
80
}
81
82
int main( int nargs, char **args )
83
{
84
  // ausreichender Puffer für maximalen Stack:
85
  union
86
  {
87
    uint8_t     byte[0];
88
    int         i[4];
89
  } puffer;
90
91
92
  // Beispiel 1:
93
  // 3 Bytes zusammenbasteln
94
  printf( "Beispiel 1: 3 Bytes\n" );
95
  puffer.byte[0] = 0x12;
96
  puffer.byte[1] = 0x34;
97
  puffer.byte[2] = 0x56;
98
  ruf_malmehrmalweniger_auf( &puffer, 3 );
99
100
  // Beispiel 2:
101
  // 3 Bytes zusammenbasteln
102
  printf( "Beispiel 2: 8 Bytes\n" );
103
  puffer.byte[0] = 0x12;
104
  puffer.byte[1] = 0x34;
105
  puffer.byte[2] = 0x56;
106
  puffer.byte[3] = 0x78;
107
  puffer.byte[4] = 0x9a;
108
  puffer.byte[5] = 0xbc;
109
  puffer.byte[6] = 0xde;
110
  puffer.byte[7] = 0xf0;
111
  ruf_malmehrmalweniger_auf( &puffer, 8 );
112
113
  return 0;
114
}
malmehrmalweniger() soll deine Funktion sein, die du letztlich
aufrufen willst.

Ausgabe unter Linux:
1
klaus@vdr1:~ > gcc -Wall  -std=gnu99 t.c &&  ./a.out
2
Beispiel 1: 3 Bytes
3
0 0x08563412
4
Beispiel 2: 8 Bytes
5
0 0x78563412
6
1 0xf0debc9a

Voraussetzungen in diesem Beispiel:
- int werden auf dem aktuellen System direkt hintereinander übergeben
- die aufgerufene Funktion bekommt als erstes eine int, in der
  drin steht, wie viel Werte kommen (irgendwie muß sie ja
  erkennen, was da kommt)
- maximal 4 int in dieser Form
- man muß zusehen, daß die Felder reichen

von Klaus W. (mfgkw)


Lesenswert?

PS:
Die Ebene mit ruf_malmehrmalweniger_auf_1...4() kann man natürlich
auch weglassen und die Funktion direkt aus dem switch aufrufen.

von Karl H. (kbuchegg)


Lesenswert?

Was auch eventuell geht:
Dazu muss aber der Argumentpassing Mechanismus vom C Compiler und das 
was die Assemblerfunktion auf dem Stack haben möchte, absolut kompatibel 
sein. Ist das nicht der Fall, kannst du das gleich alles wieder 
vergessen.

Schweinereien mit einem Funktionspointer.
Du definierst dir einen Haufen typedefs, die alle möglichen 
Aufrufargumentlisten abdecken.
Dann holst du dir die Adresse der Assemblerfunktion, castest den Pointer 
mit dem richtigen typedef zurecht und rufst die Funktion auf.

Im Prinzip ähnlich zu dem, was Klaus weiter oben gezeigt hat.

Aber ich fürchte das Problem wird einfach sein, dass die Assembler 
Funktion einen völlig anderen Argument Passing Mechanismus benutzt, als 
dein C Compiler. Ich schätze ohne eine Assembler Zwischenschicht wird 
das nichts werden.

von Klaus (Gast)


Lesenswert?

holger schrieb:
>>Wenn es überhaupt geht, dann nur mit Assembler und/oder Schweinereien.
>
> Z.B. malloc(). Igitt;)

6, setzen! Wir reden hier über den Stack!

von Emax (Gast)


Lesenswert?

Hallo, ich danke euch sehr für die Antworten, Anregungen und Beispiele!
Ich werde es nach dem Vorschlag von  Klaus Wachtler realisieren, 
reserviere also eine Anzahl an char auf dem Stack und caste die dann 
irgendwie da drauf.

Gruß Emax

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.