Forum: Mikrocontroller und Digitale Elektronik Variable Anzahl an Parameter ohne Mengenparameter


von nowayback (Gast)


Lesenswert?

Guten Tag,

in meinem C-Programm (atxmega128a1) verwende ich folgende Funktion zum 
Versenden von Nachrichten an die Außenwelt:
1
void send(char* msg, uint8_t len);
Da diese Nachrichten auch öfter Variablenwerte beinhalten, die 
normalerweise erst umständlich mittels strcat in ein Zwischenpuffer 
geschrieben werden müssten bevor man die Nachricht mittels send 
versenden kann, will ich eine Funktion schreiben, die beliebig viele 
Parameter miteinander verknüpft und anschließend absendet. Dafür habe 
ich bereits folgende Funktion geschrieben:
1
void sendC(int anzahl,...)
2
{
3
va_list vl;
4
int i;
5
char tmp[256]="";
6
7
va_start(vl,anzahl);
8
for (i=0;i<anzahl;i++)
9
{
10
strcat(tmp,va_arg(vl,char*));
11
strcat(tmp,"|"); // "|" ist ein Trennzeichen
12
}
13
va_end(vl);
14
send(tmp);
15
}

Mit dieser Funktion lassen sich nun sehr einfach Nachrichten mit 
Variablenwerten versenden, zum Beispiel:
1
sendC(3,"DEBUG",itoa(var1),itoa(var2));
Allerdings muss nun ständig an erster Stelle übergeben werden, wie viele 
char*-Parameter der Funktion übergeben werden.
Im Internet habe ich auch die Variante gefunden, dass der letzte 
Parameter immer NULL ist und anhand von diesem das Ende erkannt werden 
kann, also z.B.
1
sendC("DEBUG",itoa(var1),itoa(var2),NULL);
Natürlich benötigt auch diese Variante immer einen weiteren Parameter.
Gibt es auch eine Methode, ohne weitere Parameter auszukommen, wie es 
zum Beispiel sprintf macht? Oder erkennt sprintf selbst die Anzahl der 
Parameter anhand der angeforderten Parameter im Format-String und eine 
Reduktion der Funktion auf die wesentlichen char*-Parameter ist nicht 
möglich?
Wie macht Ihr das in euren Programmen?

von swarner (Gast)


Lesenswert?

Hi,

Ich bin mir ziemlich sicher, dass Du den letzten Parameter einfach 
weglassen kannst. Der wird automatisch auf NULL gesetzt (ist ja nicht 
vorhanden).
Damit kannst Du das Ende Deiner Parameterliste leicht erkennen.

Gruß

Sascha Warner

von Karl H. (kbuchegg)


Lesenswert?

nowayback schrieb:

> Natürlich benötigt auch diese Variante immer einen weiteren Parameter.
> Gibt es auch eine Methode, ohne weitere Parameter auszukommen

Nope

> wie es
> zum Beispiel sprintf macht? Oder erkennt sprintf selbst die Anzahl der
> Parameter anhand der angeforderten Parameter im Format-String

exakt
Genau das ist auch der Grund, warum man beim Format String peinlich 
genau sein muss und man dem gcc eine Überprüfung des Format-Strings mit 
den Argumenten beim Aufruf spendiert hat. Geht da was schief, sind die 
Auswirkungen oft verheerend.

> Wie macht Ihr das in euren Programmen?

variable Argument-Listen nach Möglichkeit vermeiden.

von Peter (Gast)


Lesenswert?

swarner schrieb:
> Ich bin mir ziemlich sicher, dass Du den letzten Parameter einfach
> weglassen kannst. Der wird automatisch auf NULL gesetzt (ist ja nicht
> vorhanden).

nein, das sind werte die auf de Stack liege, wer sollte denn dort ein 
NULL hinlegen? man bekommt beim lesen einfach die DAten die dort zu 
letzt standen. Entweder schafft man es geschickt über ein Makro das am 
ende immer eine NULL steht oder man muss die länge mitgeben.

Printf macht es anhand des Format Strings, schreibt doch mal

printf("%s%d%s%d");

dann sieht du was er macht.

von eject (Gast)


Lesenswert?

jede woche kommt wieder einer der ned suchen kann und stellt die gleiche 
frage... anstatt mal sinvolle ideen zu haben will man immer nur alles 
mögliche haben.
zuerst losfrickeln und sich dann wundern wenn nix passend 
funktioniert...
schade sowas

echt jetzt..

von swarner (Gast)


Lesenswert?

Zum Glueck war ich mir nicht "ganz sicher" ;)
Anyway, thx, wieder was gelernt.

von Peter (Gast)


Lesenswert?

wie geht das überhaupt mit dem itoa mit nur einem parameter?

> sendC("DEBUG",itoa(var1),itoa(var2),NULL);

von nowayback (Gast)


Lesenswert?

Vielen Dank für Eure Antworten.
@swarner: Habe jetzt zwar testweise auf NULL am Ende überprüft..aber da 
kommt tatsächlich keines ;)
@Peter: Das mit dem Makro hört sich vielversprechend an, da werde ich 
mal ansetzen. Den itoa-Befehl habe ich übrigens nur exemplarisch 
hingeschrieben, der echte itoa-Befehl benötigt natürlich drei Parameter.

von Karl H. (kbuchegg)


Lesenswert?

Karl heinz Buchegger schrieb:

> variable Argument-Listen nach Möglichkeit vermeiden.

Aber wenn es nicht anders geht, dann nach Möglichkeit einen bereits 
bekannten Mechanismus benutzen und keinen eigenen erfinden.

Dazu sind manchmal die Funktionen aus der printf-Familie hilfreich, die 
es auch in einer Form gibt, dass man sie in eigenen variadischen 
Funktion benutzen kann.

zb vsprintf als variadische Form vom sprintf
1
void SendC(char * format, ...)
2
{
3
  char buffer[256];
4
  va_list args;
5
6
  va_start( args, format );
7
  vsprintf( buffer, format, args );
8
  send( buffer );
9
  va_end( args );
10
}

Der Aufruf sieht dann völlig gleich aus, wie bei printf oder sprintf
1
  SendC( "DEBUG|%d|%d", var1, var2 );
mit allen Vor und Nachteilen (vor allen Dingen Nachteilen). Wenn man 
jetzt den gcc noch dazu bringen könnte, den Formatstring im Aufruf 
ähnlich wie bei printf zu checken, wäre das (fast) perfekt.

von nowayback (Gast)


Lesenswert?

Jep, so etwas wäre natürlich die perfekte Lösung! Dann könnte ich auch 
gleich im Formatstring bei Integern meine gewünschte Anzahl an Ziffern 
angeben. Hast du Ansätze, wie man GCC ohne eigene Kompilierung ;) 
beibringen könnte, auf den Formatstring zu achten?

von Karl H. (kbuchegg)


Lesenswert?

nowayback schrieb:

> angeben. Hast du Ansätze, wie man GCC ohne eigene Kompilierung ;)
> beibringen könnte, auf den Formatstring zu achten?


Nein. Leider.
Ich weiß nicht wie das funktioniert.

Ich würde mir aber mal die stdio.h genauer ansehen und dort die Funktion 
printf suchen. Mit ein wenig Glück steht dort eine 
Funktionsattributierung, die dieses Verhalten für den gcc einschaltet.
Steht dort beim Protoyp nichts dabei, dann ist dieser Formatstring-Check 
direkt hadcoded in den gcc eingebaut worden.

von sebastians (Gast)


Lesenswert?

> wie man GCC ohne eigene Kompilierung ;) beibringen könnte, auf den Formatstring 
zu achten

http://en.lmgtfy.com/?q=gcc+check+formatstring

2. Treffer:
http://gcc.gnu.org/onlinedocs/gcc-4.3.2//gcc/Function-Attributes.html

Sogar mit Beispiel:
1
extern int
2
my_printf (void *my_object, const char *my_format, ...)
3
    __attribute__ ((format (printf, 2, 3)));

von nowayback (Gast)


Lesenswert?

sebastians schrieb:
> http://en.lmgtfy.com/?q=gcc+check+formatstring

Natürlich habe ich gegoogelt, aber meine Keywords hatten leider keinen 
Erfolg.
Ansonsten vielen Dank für den Link!
Diese gerade gefunden Seite erklärt das auch recht gut: 
http://brewforums.qualcomm.com/archive/index.php/t-10032.html

Die Funktion habe ich jetzt implementiert und mit verschiedenen Werten 
getestet. Sie funktioniert super und der Compiler bemerkt korrekt 
falsche oder fehlende Parameter.

Hier noch der vollständige Code:
1
extern void sendC(const char* format, ...) __attribute__ (format (printf,1,2)));
2
extern void sendC(const char* format, ...)
3
{
4
 char buffer[256];
5
 va_list args;
6
7
 va_start(args,format);
8
 vsprintf(buffer,format,args);
9
 send(buffer,strlen(buffer)); // Der len-Parameter fehlt im Beispiel oben
10
 va_end(args);
11
}

Aufruf wie printf.
Nochmals vielen Dank für Eure Antworten!

von nowayback (Gast)


Lesenswert?

Hoppala, falscher Link in der Zwischenablage.
Hier der korrekte Link: 
http://unixwiz.net/techtips/gnu-c-attributes.html#format

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.