GCC ARM: Problem mit "\n" am Stringende

OP #2876128
Ich habe festgestellt, dass der aktuelle GCC-ARM ein "\n" am Stringende 
wegfrisst! (Ich benutze die CooCox CoIDE, für STM32F4)

Zum Beispiel bei printf("Hallo\n");

Mit einem Hex-Editor kann man im generierten *.bin-File sehen, dass das 
\n (=0x10) tatsächlich fehlt.

- Wenn des \n am Stringanfang oder in der Mitte des String steht,
  ist es vorhanden.

- Wenn man zwei \n ans Stringende hängt, ist eines vorhanden.

- Ein \r wird nicht weggefressen.

- Optimierungslevel hat keinen Einfluss.

Ich vermute es liegt am Preprozessor: Link => Kapitel 1.1
http://sunsite.ualberta.ca/Documentation/Gnu/gcc-2.95.2/html_chapter/cpp_1.html

Kann es sein, dass der Preprocessor das \n" als Trigraph interpretiert 
und aus dem String wegfrisst? Falls "Ja": wie verbiete ich dem 
Preprozessor diese Unsitte?
Moderator Persönliche Seite
Peter S. schrieb:
> Ich habe festgestellt, dass der aktuelle GCC-ARM ein "\n" am Stringende
> wegfrisst!

Ja, nicht nur der für den ARM.

Wenn du statt der Glaskugel einfach den generierten Assemblercode
ansehen würdest, würdest du sofort feststellen, dass er nämlich
das printf() durch ein puts() ersetzt, und dann muss er das
abschließende Newline auch entfernen, denn puts() fügt selbst eins
an.

[edit: Stefan war einen Tick schneller]
Gast
Hast du in der CooCox CoIDE die stdio.c ins Projekt eingebunden?
Ich vermute, wie Stefan schon gesagt hat, dass dein \n schon in der 
stdio wegoptimiert wird. Bzw. probier es mit \r\n.
OP
>> \n (=0x10)
>Soso...
Korrigiere: \n (=(dez)10 = 0x0A)

>Hast du in der CooCox CoIDE die stdio.c ins Projekt eingebunden?
Ja

>Schau nach, welche Funktion überhaupt aufgerufen wird. Es ist nämlich
>gut möglich, dass das 'printf("Hallo\n");' zu einem 'puts("Hallo");'
>optimiert wurde.
Okey ich gucke mir mal des Assemblercode an.
OP
When ich mit dem Debugger durch den Code gehe (-O0) sehe ich die 
folgende Verschachtelung der Aufrufe

main.c: printf("\nHallo\n") => puts(const char *pStr)
=> pStr @ 0x8002bb4 => "\nHallo\0" (ohne \n)

printf.c: puts() => fputs(pStr, stdout)
printf.c: fputs(const char *pStr, FILE *pStream) => fputc(*pStr, 
pStream)
printf.c: fputc(signed int c, FILE *pStream) => PrintChar(c)
printf.c: PrintChar(char c) => USART_SendData(Open_USART, (uint8_t) c);

stm32f4xx_usart.c:
1
//Diese Funktion habe ich angepasst
2
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
3
{
4
  /* Check the parameters */
5
  assert_param(IS_USART_ALL_PERIPH(USARTx));
6
  assert_param(IS_USART_DATA(Data));
7
  if ((Data=='\r')||(Data=='\n'))
8
  {
9
    USARTx->DR = ('\r');
10
    while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET); 
11
    Data='\n';
12
  }
13
   /* Transmit Data */
14
  USARTx->DR = (Data & (uint16_t)0x01FF);
15
  while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
16
}

Es wird nirgens ein \n ausgenommen. Ich füge aber eines ein, falls ein 
\r vorkommt.
Ich würde übrigens dringend davon abraten, in dieser Form die PeriphLib 
zu hacken, weil du das in jeder Version neu machen musst und letztlich 
über die eigenen Füsse stolperst. Der korrekte Ansatz wäre, PrintChar 
auf Basis der unveränderten Lib-Funktionen zu definieren.

Apropos: Wie sieht PrintChar aus?
Hast du denn überhaupt mal kontrolliert, ob das \n in der UART-Ausgabe 
tatsächlich fehlt? Denn wie bereits gesagt wurde, dass es am Ende des 
Strings im Speicher fehlt hat gar nichts zu sagen. Das \n sollte von 
fputs nach dem String automatisch hinzugefügt werden.
OP
>Der korrekte Ansatz wäre, PrintChar
>auf Basis der unveränderten Lib-Funktionen zu definieren.
Wie zum Henker geht den das? Ich übe nun seit ca 16 Stunden ein ganz 
banales printf für die UART2 auf die Beine zu stellen!!!!
OP
>Hast du denn überhaupt mal kontrolliert, ob das \n in der UART-Ausgabe
>tatsächlich fehlt?
Ja, es fehlt definitiv!

>Das \n sollte von fputs nach dem String automatisch hinzugefügt werden.
Ich will aber, dass fputs dann und nur dann ein \n am Stringende 
ausgibt, wenn ich auch ein \n am Stringende von printf verwende!
Peter S. schrieb:

>>Das \n sollte von fputs nach dem String automatisch hinzugefügt werden.
> Ich will aber, dass fputs dann und nur dann ein \n am Stringende
> ausgibt, wenn ich auch ein \n am Stringende von printf verwende!

Denkst du, die Compilerbauer sind doof, oder was?

Der Compiler ersetzt
printf( String );
natürlich nur dann durch einen Aufruf von puts, wenn der String auch mit 
einem \n aufhört.


puts      hängt an den String automatisch ein \n an
fputs     tut genau das nicht
Moderator Persönliche Seite
fputs() darf kein \n am Stringende hinzufügen, nur puts() macht das!

Das ist eine schräge Asymmetrie in der C-Bibliothek, die man nur mit
der Erklärung "historisch gewachsen" belegen kann.

Mit anderen Worten, ein puts() kann man nicht implementieren, indem
man schreibt
1
#define puts(s) fputs(s, stdout)

Wenn das puts() in deiner Bibliothek kein \n von sich aus anhängt,
dann ist es kaputt. => /dev/mülltonne
OP
>Apropos: Wie sieht PrintChar aus?
1
void PrintChar(char c)
2
{
3
  USART_SendData(Open_USART, (uint8_t) c);
4
}

>Denkst du, die Compilerbauer sind doof, oder was?
Irgendwas muss falsch laufen, wenn ein banales printf so kompliziert 
ist, bzw. mir hier offenbar auch keiner ein simples Beispiel geben kann, 
wie ich das unter " CooCox CoIDE, für STM32F4" bewerkstelligen kann!
Jörg Wunsch schrieb:

> Mit anderen Worten, ein puts() kann man nicht implementieren, indem
> man schreibt
>
>
1
#define puts(s) fputs(s, stdout)
>
> Wenn das puts() in deiner Bibliothek kein \n von sich aus anhängt,
> dann ist es kaputt. => /dev/mülltonne


Eben.

1
#define puts(s) do { fputs(s, stdout); fputc('\n', stdout); } while( 0 )
würde aber gehen.
Peter S. schrieb:
1
> void PrintChar(char c)
2
> {
3
>   USART_SendData(Open_USART, (uint8_t) c);
4
> }
5
>

Dachte ich mir. Dort muss die CRLF-Nummer und der TXE-Test rein.

> Irgendwas muss falsch laufen, wenn ein banales printf so kompliziert
> ist, bzw. mir hier offenbar auch keiner ein simples Beispiel geben kann,

Nicht jeder verwendet diese Kuh-Hähne und programmieren musst du dein 
Programm schon selber. Was du gekriegst hast ist Hilfestellung.
OP
>Wenn das puts() in deiner Bibliothek kein \n von sich aus anhängt,
>dann ist es kaputt. => /dev/mülltonne
Ok, die Bibliothek ist nicht von mir , die hat jemand in die IDE 
eingefügt! Ich schmeisse sie gerne weg, wenn mir doch bitte jemand 
erklären könnte, wie ich eine brauchbare vom aktuellen GCC ARM benutzen 
kann!
Wie macht das der Rest von dieser Welt?
Peter S. schrieb:

>>Denkst du, die Compilerbauer sind doof, oder was?
> Irgendwas muss falsch laufen, wenn ein banales printf so kompliziert
> ist,

Ein banales printf ist nicht kompliziert.
Du musst nur der I/O Library dann auch die Treiber-Schicht so zur 
Verfügung stellen, wie sie das erwartet. Nicht mehr und nicht weniger. 
Mit 'kompliziert' hat das erst mal nicht viel zu tun. Das setzen von 
Pixel auf einer VGA-Karte ist auch nicht kompliziert im eigentlichen 
Sinne. Aber das ganze mit all den Hilfsfunktionen so in einen Treiber zu 
verpacken, dass er Windows als Display-Treiber untergejubelt werden 
kann, ist nun mal Aufwand.
Peter S. schrieb:

> Wie macht das der Rest von dieser Welt?


Ich würde mal schätzen: Auf diesen ganzen printf/scanf Müll pfeifen.
Ist auf einem µC sowieso viel Aufwand für nichts.

Eine Routine, die einen String ausgeben kann ist schnell geschrieben und 
mehr braucht man Low-Level nicht. Maximal das man sich mit sprintf an 
die Formatiermöglichkeiten anhängt, die das printf Zeugs mitbringt.
Und für alles was mit Benutzereingabe zu tun hat, ist scanf sowieso 
nicht vernünftig zu gebrauchen.
> main.c: printf("\nHallo\n") => puts(const char *pStr)
> => pStr @ 0x8002bb4 => "\nHallo\0" (ohne \n)
>
> printf.c: puts() => fputs(pStr, stdout)

Dann lass doch mal dein puts aus printf.c ansehen!

Dort muss nach dem String noch ein \n ausgegeben werden.
Tut es das nicht, dann hat der, der das geschrieben hat, geschlampt.

Von diesen Funktionen ...
> printf.c: puts() => fputs(pStr, stdout)
> printf.c: fputs(const char *pStr, FILE *pStream) => fputc(*pStr,
>           pStream)
> printf.c: fputc(signed int c, FILE *pStream) => PrintChar(c)
> printf.c: PrintChar(char c) => USART_SendData(Open_USART, (uint8_t) c);
... sind puts, fputs und fputc Standardfunktionen, die eine genau 
vorgeschriebene Funktionalität bereit stellen müssen. Der Compiler 
verlässt sich darauf. Tun die Funktionen das nicht, dann geht natürlich 
alles weitere schief.
OP
>Dann lass doch mal dein puts aus printf.c ansehen!
1
signed int puts(const char *pStr)
2
{
3
  return fputs(pStr, stdout);
4
}

habe ich nun ersetzt durch
1
signed int puts(const char *pStr)
2
{
3
  signed int num = 0;
4
  while (*pStr != 0)
5
  {
6
    if (fputc(*pStr, stdout) == EOF)
7
    {
8
      return EOF;
9
    }
10
    num++;
11
    pStr++;
12
  }
13
  if (fputc('\n', stdout) == EOF)
14
  {
15
    return EOF;
16
  }
17
  return num++;
18
}

Muss der Rückgabewert des \n mitzählen?

Aber nun tut es, danke für eure Hilfe!
Peter S. schrieb:
>>Dann lass doch mal dein puts aus printf.c ansehen!
>
1
signed int puts(const char *pStr)
2
> {
3
>   return fputs(pStr, stdout);
4
> }
5
>

Na da haben wirs doch.

> habe ich nun ersetzt durch
Was um alles in der Welt machst du da?

1
int puts(const char *pStr)
2
{
3
  if( fputs(pStr, stdout) != EOF )
4
    return fputc( '\n', stdout );
5
  return EOF;
6
}
und gut ists.

C-Standard (OK, die Draft-Version)

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf

1
7.19.7.10 The puts function
2
Synopsis
3
1 #include <stdio.h>
4
  int puts(const char *s);
5

6
Description
7
2 The puts function writes the string pointed to by s to the stream
8
  pointed to by stdout, and appends a new-line character to the output.
9
  The terminating null character is not written.
10

11
Returns
12
3 The puts function returns EOF if a write error occurs; otherwise it
13
  returns a nonnegative value.

Es ist nicht definiert, was der Returnwert genau aussagt, ausser das er 
in bestimmten Situationen EOF sein soll und wenn die nicht vorliegen, 
dann ist der Wert nicht negativ.
Gast
>> habe ich nun ersetzt durch...
>Was um alles in der Welt machst du da?
Ich wollte einfach die Functioncall-Tiefe nicht weiter aufblähen, daher 
habe ich fputs() ge-inlined! ;o)

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 14 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.