Forum: Compiler & IDEs Probleme mit Übergabe eines Arrays


von Hans (Gast)


Lesenswert?

Hallo zusammen!

Ich komme hier grad nicht weiter, vielleicht weiß ja jemand einen Rat.

Ich bekomme per UART einen Befehl rein in ASCII-Form. Jetzt versuche ich 
gerade eine Kommandoauswertung zu machen, also habe ich ein Array:
1
#define NR_OF_COMMANDS 3
2
3
char *cmd_list[] = { "TYPE?", "ID?", "MOMVALUE" };
Jetzt will ich herausfinden, welches Kommando angekommen ist, bzw. ob es 
ein gültiges ist, also mache folgendes:
1
int8_t command_interpreter( void )
2
{
3
  uint8_t counter;
4
5
  for( counter = 0; counter < NR_OF_COMMANDS; counter++ )
6
  {
7
    if( !uart_compare_rx_string( cmd_list[counter] ) )
8
    {
9
      return counter;
10
    }
11
  }
12
13
  return -1;
14
}
Diese Funktion sendet also den Textstring aus cmd_list, abhängig vom 
Zählindex counter an die Unterfunktion uart_compare_rx_string, welche so 
aussieht:
1
int8_t uart_compare_rx_string( char *test_string  )
2
{
3
  uint8_t stringlength;
4
  int8_t return_value;
5
6
  stringlength = strlen( test_string );
7
  return_value = strncmp( uart_control.rx_buffer, test_string, stringlength );
8
9
  return return_value;
10
}
Das Problem jetzt: Das ganze funktioniert wunderbar, solange man nur die 
Befehle TYPE? und ID? benutzt - das kann man im Wechsel beliebig oft hin 
und her machen. MOMVALUE? geht genau einmal, danach ist beim 
String-Compare für jeden String die Stringlänge immer 0.

Hat jemand einen Ansatz wo ich da suchen sollte?

: Verschoben durch User
von Marian (phiarc) Benutzerseite


Lesenswert?

Du castest da implizit rum, würde ich behaupten (uint8_t stringlength 
und sowas), also mal alle Warnungen anmachen, dann sollte der Compiler 
schon ein wenig meckern.

Als allgemeine Empfehlung noch ein NUL-terminiertes Array zu benutzen 
statt der fehleranfälligen Konstruktion von Array + Array-Länge.

Ergo (immer alles const machen was const machbar ist!)
const char *cmd_list[] = {"12", "123", 0};

Iteration
int i = 0;
while(cmd_list[i]) {
// was mit cmd_list[i] machen
i++;
}

Geht natürlich auch als for-Schleife

Von der Programmstruktur her empfinde ich eine Funktion wie 
uart_compare_rx_string als groben Pfusch; besser: Standardfunktionen 
benutzen und sowas wie uart_rx_get_string haben, dann ist sofort klar, 
mit welchen Daten gearbeitet wird. Außerdem duplizierst du nicht etliche 
Stringvergleichsfunktionen, nur weil jeweils der eine Operand ein 
anderer ist.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:

> Hat jemand einen Ansatz wo ich da suchen sollte?

So wie das geschrieben ist: auf jeden Fall nicht in dieser Funktion. Das 
Problem ist wo anders.

PS: der streln bzw. strncmp bringt dir an dieser Stelle genau gar 
nichts. Das ist nur eine vermeintliche Sicherheit, die in Wirklichkeit 
nicht existiert. Die Funktion ist genauso sicher bzw. unsicher, wie wenn 
du einfach nur einen strcmp benutzt hättest.

: Bearbeitet durch User
von Marian (phiarc) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> PS: der streln bzw. strncmp bringt dir an dieser Stelle genau gar
> nichts. Das ist nur eine vermeintliche Sicherheit, die in Wirklichkeit
> nicht existiert.

Stimmt, das habe ich ja gar nicht gesehen.

: Bearbeitet durch User
von Hans (Gast)


Lesenswert?

Marian B. schrieb:
> Du castest da implizit rum, würde ich behaupten (uint8_t stringlength
> und sowas)

Sorry, das verstehe ich nicht, das sind doch nur Variablen damit ich 
sehen kann, was als Ergebnis aus strlen herauskommt und was strcmp mir 
zurückgibt. Welchen cast meinst du?

Marian B. schrieb:
> besser: Standardfunktionen
> benutzen

Was ist denn hierfür die Standardfunktion?

von B. S. (bestucki)


Lesenswert?

Marian B. schrieb:
> Du castest da implizit rum, würde ich behaupten (uint8_t stringlength
> und sowas), also mal alle Warnungen anmachen, dann sollte der Compiler
> schon ein wenig meckern.

Da gibts keine Warnungen (GCC mit -Wall, -Wextra und -pedantic). Klar 
ist es schöner, size_t zu verwenden. Aber auf einem uC darf man durchaus 
kleinere Datentypen verwenden.

Marian B. schrieb:
> Ergo (immer alles const machen was const machbar ist!)
> const char *cmd_list[] = {"12", "123", 0};

Na dann auch wirklich alles:
1
const char * const cmd_list[] = {"12", "123", 0}

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:
> Hans schrieb:
>
>> Hat jemand einen Ansatz wo ich da suchen sollte?
>
> So wie das geschrieben ist: auf jeden Fall nicht in dieser Funktion. Das
> Problem ist wo anders.
>
> PS: der streln bzw. strncmp bringt dir an dieser Stelle genau gar
> nichts. Das ist nur eine vermeintliche Sicherheit, die in Wirklichkeit
> nicht existiert. Die Funktion ist genauso sicher bzw. unsicher, wie wenn
> du einfach nur einen strcmp benutzt hättest.

Und nachdem sie dann auf
1
int8_t uart_compare_rx_string( char *test_string  )
2
{
3
  return strcmp( uart_control.rx_buffer, test_string );
4
}
eindampft, kannst du dir den ganzen Funktionsaufruf auch sparen, indem 
du im Commando-Interpreter den strcmp machst
1
int8_t command_interpreter( void )
2
{
3
  uint8_t counter;
4
5
  for( counter = 0; counter < NR_OF_COMMANDS; counter++ )
6
  {
7
    if( !strcmp( uart_control.rx_buffer, cmd_list[counter] ) )
8
    {
9
      return counter;
10
    }
11
  }
12
13
  return -1;
14
}

und wenn du da dann auch noch den uart_control.rx_buffer zum AUfrufer 
verlagerst
1
int8_t command_interpreter( const char * command )
2
{
3
  uint8_t counter;
4
5
  for( counter = 0; counter < NR_OF_COMMANDS; counter++ )
6
  {
7
    if( !strcmp( command, cmd_list[counter] ) )
8
    {
9
      return counter;
10
    }
11
  }
12
13
  return -1;
14
}
dann hast du eine schöne Funktion, der du ein Kommando übergeben kannst, 
und die dann das weitere veranlasst. Egal wo das Kommando herkommt. Sei 
es von der UART oder sei es, dass du im Programm selber diesen 
Interpreter mit einem konstanten String aufrufst.

: Bearbeitet durch User
von Marian (phiarc) Benutzerseite


Lesenswert?

strlen liefert nach POSIX size_t, was z.B. beim AVR-GCC uint16_t 
entspricht und uint16_t =/= uint8_t. Ergo impliziter Cast bei der 
Zuweisung. strcmp liefert nach POSIX int, was z.B. beim AVR-GCC int16_t 
entspricht und int16_t =/= int8_t. Wenn jemand solchen Code schreibt, 
liegt der Verdacht nahe, dass es an allen anderen Stellen ähnlich 
aussieht und eine alte Weisheit sagt, dass implizite Casts implizite 
Fehler implizieren.

be stucki schrieb:
> Na dann auch wirklich alles:const char * const cmd_list[] = {"12",
> "123", 0}

Sehr richtig :-)

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

be stucki schrieb:

> Na dann auch wirklich alles:
>
1
const char * const cmd_list[] = {"12", "123", 0}

Full Ack.

Vor allen Dingen dieses const machen würde ich sehr empfehlen. Und dann 
natürlich auch an allen verwendenden Stellen durchziehen. Aber das sagt 
dir dann schon der Compiler, bei welchen Funktionsargumenten das ein 
oder andere const fehlt. Die Einführung von const zieht zwar oft einen 
Rattenschwanz an (einfachen) Änderungen bzw. Anpassungen nach sich. Die 
sind es aber wert gemacht zu werden.

Ob man da mit einer impliziten Längenangabe arbeitet, oder mit einem 
#define in dem die Längenangabe steht, oder mit einem #define, welches 
die Länge mittels sizeof bestimmt, würde ich momentan erst mal als nicht 
ganz so wichtig abtun. OK, mit dem 0 Pointer im Array spart man sich 
Funktionsargumente.

: Bearbeitet durch User
von Marian (phiarc) Benutzerseite


Lesenswert?

Der Vorteil eines terminierten Arrays ist einfach der, dass man nicht 
vergessen kann die Längenangabe anzupassen, eine Fehlerquelle weniger. 
Wenn man vergisst die Längenangabe um eins zu erhöhen, nachdem man einen 
neuen Befehl hinzugefügt hat, kann man lange Suchen, wieso der Befehl 
nicht funktioniert...

Wo wir gerade bei "alles const machen was geht" sind: Hilfsfunktionen 
und modulweite globale Variablen static machen, cmd_list ist da 
wahrscheinlich auch ein Kandidat.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Marian B. schrieb:

> Wenn man vergisst die Längenangabe um eins zu erhöhen, nachdem man einen
> neuen Befehl hinzugefügt hat, kann man lange Suchen, wieso der Befehl
> nicht funktioniert...


Na ja
Man macht das natürlich ja auch nicht so
1
#define NR_OF_COMMANDS 3
2
3
const char * const cmd_list[] = { "TYPE?", "ID?", "MOMVALUE" };

das das aus der sicht von Fehlervermeidung Schwachsinn ist, darüber 
brauchen wir uns nicht unterhalten.

Wenn schon, dann so
1
#define NR_OF_COMMANDS 3
2
3
const char * const cmd_list[NR_OF_COMMANDS] = { "TYPE?", "ID?", "MOMVALUE" };
dann krieg ich vom Compiler eine auf den Deckel, wenn ich ein Kommando 
hinzufüge und den #define vergesse anzupassen.

Noch besser wäre die Variante
1
const char * const cmd_list[] = { "TYPE?", "ID?", "MOMVALUE" };
2
3
#define NR_OF_COMMANDS (sizeof(cmd_list) / sizeof(cmd_list[0]))
in der ich mich um das #define überhaupt nicht mehr kümmern muss, wenn 
ich neue Strings hinzufüge.

Aber darüber will ich mich jetzt nicht streiten. Man kann es so oder so 
machen. Letzten Endes läuft es darauf hinaus, ob man in allen 
Funktionen, die dieses Array als Argument kriegen, auch die Länge 
mitgeben muss oder ob man die aus dem Array ermitteln kann. Letzters ist 
sicherlich schöner, aber so ein großes Problem ist es dann auch wieder 
nicht.

: Bearbeitet durch User
von Hans (Gast)


Lesenswert?

Man, da kommt ja einiges zurück von euch - danke schonmal!

Das muss ich mir jetzt erstmal durchlesen...

von Karl H. (kbuchegg)


Lesenswert?

Tatsache ist aber, dass das Problem nicht auf den geposteten Code 
zurückgeführt werden kann.
Also verfolge mal, was zb mit dem Rückgabewert der Funktion gemacht 
wird. Irgendwo wirst du dir ins Array cmd_list reinschreiben. Entweder 
indem du unmitoviert da einen Pointer auf einen tatsächlich leeren 
String reinschreibst, oder indem du gleich einen NULL Pointer ins Array 
schreibst.

Gerade deshalb wäre das durchziehen von const nicht ganz unwichtig. Denn 
dann klopft dir der Compiler da auf die Finger, wenn du das bewusst 
versuchst. Kann natürlich auch sein, dass du wanders ein Array 
überläufst und dabei zufällig genau diese cmd_list erwischt hast. const 
alleine ist auch kein Allheilmittel. Aber es hilft.

: Bearbeitet durch User
von Hans (Gast)


Lesenswert?

Da melde ich mich jetzt nochmal zurück.

Also funktionieren tut es jetzt. Aber nun würde ich nochmal gerne den 
besten Weg für die ganze Geschichte finden.

Es sieht folgendermaßen aus:

Ich habe eine Funktion uart_functions.c / .h, in der auch mein 
uart_control.rx_buffer steckt. Dieses Array ist mit volatile deklariert, 
da dieses Array per UART-Interrupt gefüllt wird. An dieser Stelle 
meckert der Compiler z.B. schonmal, dass "volatile char" inkompatibel 
mit dem "const char" des StringCompare ist. Aber wie sollte ich das 
jetzt am besten lösen? Ich kann ja den rx_buffer nicht const machen, da 
er sich ja ändert, oder wo ist hier mein Denkfehler?
1
int8_t uart_compare_rx_string( const char *test_string  )
2
{
3
  return strcmp( uart_control.rx_buffer, test_string );
4
}

Der rx_buffer ist ja nur in der uart_functions bekannt, daher habe ich 
die Vergleichsfunktion auch in den uart_functions.

Karl Heinz schrieb:
> und wenn du da dann auch noch den uart_control.rx_buffer zum AUfrufer
> verlagerst

Wie soll ich das verstehen? Was packe ich denn jetzt am besten wohin? 
Wäre es besser, uart_control global zugänglich zu machen?

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:

> Ich habe eine Funktion uart_functions.c / .h, in der auch mein
> uart_control.rx_buffer steckt. Dieses Array ist mit volatile deklariert,
> da dieses Array per UART-Interrupt gefüllt wird. An dieser Stelle
> meckert der Compiler z.B. schonmal, dass "volatile char" inkompatibel
> mit dem "const char" des StringCompare ist.

An dieser Stelle könnte man das volatile wegcasten, da der Compiler wohl 
sowieso keine Chance hat, da durch Optimierung etwas zu vermasseln.

> Aber wie sollte ich das
> jetzt am besten lösen? Ich kann ja den rx_buffer nicht const machen, da
> er sich ja ändert, oder wo ist hier mein Denkfehler?

Den willst du auch nicht const machen.

Das const an dieser Stelle:
> int8_t uart_compare_rx_string( const char *test_string  )

ist die Zusicherung der Funktion:
Ich werde mich nicht an den Daten vergreifen. Ich behandle deinen 
String, wie wenn er konstant wäre. D.h. ich lese den String aus, benutze 
ihn um irgendwelche Entscheidungen zu treffen, aber ich werde nicht 
versuchen, den String zu verändern.


Das ist die Bedeutung eines const in der Argumentliste einer Funktion. 
Das hat erst mal nichts damit zu tun, ob die entsprechende Variable vom 
Aufrufer const ist oder nicht. Für beides gilt: wenn eine aufgerufene 
Funktion diese Zusicherung gibt, dann bin ich auf der sicheren Seite und 
muss nicht damit rechnen, dass die Funktion den String verändert.

Aus genau dem gleichen Grund, ist ja zb das Argument der Funktion strlen 
(welches die Länge eines Strings bestimmt)
1
size_t strlen( const char* str )

das bedeutet nicht, dass man da nur konstante Strings reinstecken darf, 
wie in
1
  i = strlen( "Hallo World" );

sondern man darf selbstverständlich auch variable Strings reingeben
1
  char str[40];
2
  strcpy( str, "Hallo World" );
3
4
...
5
6
  i = strlen( str );

während es bei letzterem noch egal wäre, wenn die Funktion den String 
selbst verändert (ist ja ein Array im Speicher), wäre das in diesem Fall
1
  i = strlen( "Hallo World" );
fatal. Das String Literal "Hallo World" ist für das Programm tabu. Es 
ist illegal, zu versuchen dieses Literal zu verändern (zb durch eine 
Zuweisung von Einzelzeichen. Daher ist es in diesem Fall wichtig, dass 
strlen
1
size_t strlen( const char* str )
mit seinem const die Zusicherung gibt: Ich versuche das erst gar nicht. 
Mach dir keine Sorgen, ich lasse den String, den du mir gibst, in Ruhe!

Das const bewirkt, dass in so einem Fall
1
void foo( const char* str )
2
{
3
  str[0] = 'a';
4
}
der Compiler aktiv wird: Die Funktion hat die Zusicherung gegeben, den 
Inhalt von str nicht zu verändern und trotzdem versucht sie es mit der 
Zuweisung. Das kann aber nicht sein! Zusicherung ist Zusicherung und 
damit ist dann die Zuweisung ein Fehler, der auch vom Compiler angemerkt 
wird.

Ein const bewirkt also 2 Dinge: zum einen ist es ein Kontrakt mit dem 
Aufrufer, was die Funktion tun wird bzw. nicht tun wird. Und zum anderen 
überwacht der Compiler auch, ob sich die Funktion an diese Zusicherung 
hält.

> Der rx_buffer ist ja nur in der uart_functions bekannt, daher habe ich
> die Vergleichsfunktion auch in den uart_functions.

Du musst dir über Zuständigkeiten im klaren werden!
das rx-Modul ist dafür zuständig, die UART zu behandeln. Das UART-Modul 
ist aber nicht dafür zuständig, einen Commando Interpreter zu bedienen. 
Das gehört nicht in seinen Kompetenzbereich.
Genauso, wie du immer ein wenig abseits deines aktuellen Projektes über 
den Tellerrand schauen solltest. In deinem konkreten Programm willst du 
den Kommando-Interpreter auf den Strings arbeiten lassen, die du per 
UART bekommst. Aber ist das immer so? Oder ist es nicht eigentlich so, 
dass es für einen Kommando-Interpreter völlig egal sein sollte, wo der 
String herkommt, mit dem er arbeitet?
Ich würde mal letzteres sagen. Für einen Kommando Interpreter sollte das 
soch eigentlich keinen Unterschied machen. Der kriegt einen String und 
den wertet er aus. Ob der String von einem File auf einer SD-Karte 
kommt, ob der per Netzwerkverbindung eingetroffen ist, ob der per UART 
gekommen ist oder ob den ein Benutzer über eine Tastatur eingegeben hat, 
das sollte doch für einen Kommando-Interpreter keinen Unterschied 
machen. Er bekommt einen String und den wertet er aus - egal wo der 
String hergekommen ist.
Daher sollte ein Kommando-Interpreter überhaupt nicht wissen, wo der 
String herstammt.

Natürlich wird man auf einem kleinen µC auch schon mal eine Grenze 
ziehen und zulassen, das die reine Lehre auch schon mal etwas verwässert 
wird. Je nachdem welchen Mehraufwand man sich einhandelt. Aber ein
1
int main()
2
{
3
  ...
4
5
  while( 1 ) {
6
    if( uart_string_available() )
7
    {
8
      cmdNumber = command_interpreter( uart_rx_string() );
9
      ....
10
}
(oder wie auch immer du dann die Funktionen nennst)
ist jetzt dann auch wieder nicht sooooo viel Mehrarbeit, dass man davor 
zurückscheuen muss um die Zuständigkeiten zu erahlten: Die UART ist 
dafür zuständig einen String zu empfangen, der command Interpreter ist 
dafür zuständig einen String auszuwerten und das verknüpfende Bindeglied 
sitzt in main(), welches die Daten von dem einen Modul in das andere 
Modul entsprechend den Programmanforderungen und der geforderten Logik 
weiterleitet. Es gibt kaum einen triftigen Grund, dafür Abängigkeiten 
zwischen 2 Modulen in den Module selbst zuzulassen.


>> und wenn du da dann auch noch den uart_control.rx_buffer zum AUfrufer
>> verlagerst
>
> Wie soll ich das verstehen?

Es gibt keinen Grund, warum in der main() nicht das Bindeglied zwischen 
den Welten 'UART' und 'Kommando-Auswertung' sein soll.

> Was packe ich denn jetzt am besten wohin?
> Wäre es besser, uart_control global zugänglich zu machen?

Du kannst auch eine Funktion machen, die dir Zugang zum String gibt. 
Wenn du eine Funktion hast, die dir einen anderen String mit dem UART 
String vergleicht, dann kannst du den UART String auch gleich 
rausrücken. Das schenkt sich nicht. Oder auf einem µC (auch wenn es 
gegen die reine Lehre verstößt), direkten Zugang zum Array gewähren, in 
welchem der String steht.

: Bearbeitet durch User
von Hans (Gast)


Lesenswert?

Hi Karl Heinz!

Danke für deine sehr ausführlichen Antworten. Um es jetzt mal endgültig 
zu verstehen...ich konnte nämlich herausfinden, ab wann das Programm 
nicht mehr rund läuft in meiner alten Variante. Es liegt hier auch am 
"const" - so sah es vorher aus:

uart_functions.c
1
char *uart_err_msg_array[] = { "Receive buffer overflow",
2
                               "Too few characters received",
3
                               "Character timeout",
4
                               "Unknown command", "Unknown" };
uart_functions.h
1
extern char *uart_err_msg_array[];
In der main() konnte ich also so auf dieses Array zugreifen. In der 
main() folgte jetzt bei der Abfrage nach "MOMVALUE" im Verlauf folgende 
Zeile:
1
char uart_response[50];
2
uint8_t tx_bytecount;
3
...
4
case CMD_MOMVALUE:
5
{
6
  float test = 400.35;
7
  tx_bytecount = sprintf( uart_response, "%E", test );
8
  uart_update_tx_buffer( uart_response, 0, tx_bytecount );
9
  uart_start_tx( tx_bytecount );
10
  break;
11
}
12
...
Und jedesmal, wenn die sprintf-Anweisung ausgeführt wurde, war das Array 
in uart_functions.c platt. Wenn ich es jetzt ebenfalls als "const char * 
const uart_err_msg_array" anlege, dann geht es.

Aber wieso passiert das an der Stelle? Kann der Compiler da einfach 
drüberschreiben? Bzw. wieso schreibt er einfach drüber?

von Hans (Gast)


Lesenswert?

Ach und nochwas: Ist es richtig, dass mir strncmp auch Werte ausgibt, 
die nicht -1, 1 oder 0 sind. Ich habe nämlich auch Zahlen wie 18 als 
Rückgabewert. Nur überall im Netz lese ich nur von den drei Zahlen.

von Peter II (Gast)


Lesenswert?

Hans schrieb:
> Nur überall im Netz lese ich nur von den drei Zahlen.

merkwürdig. Im ersten Link im netzt steht:

Returns an integral value indicating the relationship between the 
strings:
 A zero value indicates that the characters compared in both strings 
form the same string.
 A value greater than zero indicates that the first character that does 
not match has a greater value in str1 than in str2; And a value less 
than zero indicates the opposite.

von Hans (Gast)


Lesenswert?

OK, dann hab ich genau den nicht gesehen.
Hier steht es halt anders:
http://www.c-howto.de/tutorial-strings-zeichenketten-stringfunktionen-vergleichen-strcmp.html

Sorry

von Hans (Gast)


Lesenswert?

Karl Heinz schrieb:
> int main()
> {
>   ...
>
>   while( 1 ) {
>     if( uart_string_available() )
>     {
>       cmdNumber = command_interpreter( uart_rx_string() );
>       ....
> }

Das sieht besser aus, aber was macht die Funktion uart_rx_string() jetzt 
genau? Sie muss doch in dem Fall die Adresse des Arrays zurückgeben, 
oder? Wie sieht die Funktion denn dann aus?

char *uart_rx_string( void )

?

von Hans (Gast)


Lesenswert?

Also funktionieren tut das hier folgendermaßen:
1
const char *uart_return_rx_buffer_address( void )
2
{
3
  return (const char *) uart_control.rx_buffer;
4
}
Nur ist es auch richtig so?

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:


> main() folgte jetzt bei der Abfrage nach "MOMVALUE" im Verlauf folgende
> Zeile:
>
1
> char uart_response[50];
2
> uint8_t tx_bytecount;
3
> ...
4
> case CMD_MOMVALUE:
5
> {
6
>   float test = 400.35;
7
>   tx_bytecount = sprintf( uart_response, "%E", test );
8
>   uart_update_tx_buffer( uart_response, 0, tx_bytecount );
9
>   uart_start_tx( tx_bytecount );
10
>   break;
11
> }
12
> ...
13
>
> Und jedesmal, wenn die sprintf-Anweisung ausgeführt wurde, war das Array
> in uart_functions.c platt.

Ist interessant, daa %E eigentlich mit Floating Point werten umgehen 
können müsste. Probier mal ein ganz normales %f, nicht dass da die 
Implementierung für %E fehlerhaft ist.

> Wenn ich es jetzt ebenfalls als "const char *
> const uart_err_msg_array" anlege, dann geht es.

Wird wahrscheinlich einfach nur an einer anderen Stelle im Speicher 
platziert sein. Damit trifft der 'Fehler' jetzt irgendwas anderes im 
Speicher, was er platt macht.


> Aber wieso passiert das an der Stelle? Kann der Compiler da einfach
> drüberschreiben? Bzw. wieso schreibt er einfach drüber?

Der Compiler schreibt da überhaupt nicht drüber. Das Programm tut es. 
Und es gibt viele, viele Möglichkeiten was da alles passiert sein kann. 
Ohne den Code für printf zu sehen, ist das aus der Entfernung recht 
schwer einzuschätzen, was da konkret passiert.

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:
> Also funktionieren tut das hier folgendermaßen:
>
1
> const char *uart_return_rx_buffer_address( void )
2
> {
3
>   return (const char *) uart_control.rx_buffer;
4
> }
5
>
> Nur ist es auch richtig so?

Ist perfekt.
Nur den Cast kannst du weglassen.

EIn derartiges const im Datentyp des Returnwertes bedeutet hier einfach 
nur:
Lieber Aufrufer! Ich gebe dir zwar jetzt einen Pointer auf ein 
Speicherfeld, aber du darfst diesen Pointer nicht dazu benutzen, mir den 
String unter dem Allerwertesten zu verändern. Für dich, geschätzter 
Aufrufer, ist dieser String tabu und darfst ihn nicht ändern.
Du kannst daher diesen Pointer in allen Operationen benutzen, die die 
Zusicherung geben, den String nicht zu verändern
1
  const char* pStr = uart_return_rx_buffer_address();
2
  size_t len = strlen( pStr );   // Ist ok, denn strlen gibt die Zusicherung
3
                                 // den String nicht zu verändern
du kannst ihn aber nicht dazu benutzen, den String zu verändern
1
  const char* pStr = uart_return_rx_buffer_address();
2
  pStr[0] = 'a';             // Fehler. pStr zeigt auf einen konstanten String
3
  strcpy( pStr, "Hallo" );   // Fehler: der ganze String in pStr ist nicht änderbar
4
5
  char tmp[40];
6
  strcpy( tmp, pStr );    // Ist soweit ok. strcpy gibt die Zusicherung, die Quelle nicht zu verändern


Du castest mir da ein wenig zu sorglos rum ohne dir zu überlegen, was 
der Datentyp und insbesondere die Modifier wie 'const' eigentlich 
aussagen. Bei const dreht sich alles darum, schreibende Zugriffe 
einzuschränken. Und selbstverständlich kann eine Funktion
1
static char text[50];
2
const char* getPtr( void )
3
{
4
  return text;
5
}
dem Aufrufer einen Pointer auf ein char-Array als Pointer auf const 
rausgeben, um den Aufrufer zu signalisieren: Hände weg von meinem Array! 
Du darfst gerne lesend zugreifen, aber verändern darfst du den Inhalt 
nicht. In diesem Sinne ist das einfach nur eine Einschränkung, die beim 
Liefern der Adresse dazukommt. Und dazu braucht es keinen Cast. Die 
Rechte des Aufrufers einzuschränken kann niemals problematisch sein. 
Umgekehrt aber schon. Wenn sich der Aufrufer einen Dreck um diese 
Einschränkung kümmert ...
1
  char* pStr = (char*)uart_return_rx_buffer_address();
2
  pStr[0] = 'a';             // Fehler. pStr zeigt auf einen konstanten String
... und das const einfach wegcastet, dann kann (muss aber nicht) das 
alles problematisch werden.

Casts sind Waffen!
Die will man nicht nach der Rundumschlagmethode nach dem 
Gieskannenprinzip verteilen.

: Bearbeitet durch User
von Hans (Gast)


Lesenswert?

Ich heul gleich...

Karl Heinz schrieb:
> Ist interessant, daa %E eigentlich mit Floating Point werten umgehen
> können müsste. Probier mal ein ganz normales %f, nicht dass da die
> Implementierung für %E fehlerhaft ist.

Es geht weiter mit dem sprintf...ich weiß echt nicht mehr weiter!!!
1
output_value = 100.0; // GLOBALE VARIABLE
2
3
4
void main( void )
5
{
6
  ...
7
  char uart_response[30];
8
  float out_val = 400.35;
9
  uint8_t bytecount;
10
11
  bytecount = sprintf( uart_response, "%.2f", out_val );
12
}
Ich habe eine globale Variable "output_value", welche ich eigentlich mit 
dem sprintf ausgeben möchte. Wenn ich dies tue, dann ist output_value 
jedesmal nach dem sprintf wieder auf 0.0 - testweise habe ich nun 
"out_val" im sprintf. Das funktioniert auch, out_val behält ihren 
Inhalt, aber die globale "output_value"-Variable wird weiterhin 
verändert durch das sprintf, obwohl auf diese garnicht zugegriffen wird.

Bitte bitte sagt mir, wo ich suchen kann...ich verzweifel hier wirklich.

von Hans (Gast)


Lesenswert?

Karl Heinz schrieb:
> Nur den Cast kannst du weglassen.
Also so?
1
const char *uart_return_rx_buffer_address( void )
2
{
3
  return uart_control.rx_buffer;
4
}
Ohne den Cast meckert der Compiler, dass die Datentypen nicht passen.

von Hans (Gast)


Lesenswert?

Der Controller startet neu...:-\ Das sprintf resettet den - sehe ich 
jetzt an einer LED welche ich mit einem ersten Kommando ausschalte und 
die beim Start dann wieder angeht.

von Hans (Gast)


Lesenswert?

Ist der Stack wohl zu schmal

von Oliver S. (oliverso)


Lesenswert?

Hans schrieb:
> Es geht weiter mit dem sprintf...ich weiß echt nicht mehr weiter!!!

Natürlich geht das weiter. Du hast ja bisher das Problem nicht gelöst. 
Dein Programm überschreibt den Speicher für globale Variablen. Hat es 
früher getan, und tut es immer noch. Deine String-Konstanten sind durch 
das const an eine andere Stelle gewandert, damit trifft es jetzt andere 
Variable.

Auf und für was programmierst du eigentlich? Stack groß genug?

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Hans schrieb:

> Ohne den Cast meckert der Compiler, dass die Datentypen nicht passen.

Ja.
Aber er meckert nicht wegen dem const.
Sondern er meckert, weil du an dieser Stelle das 'volatile' verlierst.

Abgesehen davon:
Ich schliesse mich an. Es sieht wohl so aus, als ob dein Programm zu 
groß für den Stack ist bzw. zu intensive Stacknutzung betreibt.

: Bearbeitet durch User
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.