Forum: PC-Programmierung Wie baut man in C Strings zusammen?


von StefanK (stefanka)


Lesenswert?

Hallo,
dies ist eine Anfängerfrage. Ich will in einem C-Programm, das auf einem 
AVR läuft, einen String programmatisch zusammen setzen und stehe etwas 
auf dem Schlauch (in anderen Sprachen wie z.B. JAVA, VB oder C# für 
PC-Programmierung ist das einfach).

Der String testString soll aus Zeichenfolgen wie z.B. "A = " und aus 
beliebigen Zahlen, die in uint8_t Variablen vorliegen, zusammen gesetzt 
werden. Die Zahlen sollen einfach nur als Byte-Wert in den String 
eingefügt werden. Sie sollen nicht dezimal oder formattiert erscheinen, 
sondern nur als einzelner character, auch wenn es dafür kein 
menschen-lesbares ASCII-Zeichen gibt.
Danke schon Mal für eure Hilfe.

Beispiel:
String testString = "";
uint8_t byte1 = 0x4B; //entspricht ASCII-Zeichen für K
uint8_t byte2 = 0xC8; //keine lesbare ASCII-Zeichen Entsprechung bzw. 
Sonderzeichen

testString = "A = " + byte1 + ", B = " + byte2 + "." + "\n"; //Abschluss 
mit Zeilenvorschub

So soll der zusammengesetzte String (als Text) aussehen:
  A = K, B = irgendein Zeichen für byte2.
  neue Zeile

: Verschoben durch Moderator
von Gunnar F. (gufi36)


Lesenswert?

faule Menschen verwenden dafür sprintf(,,). Der kann mit Formatstrings 
beladen werden.

von Matthias S. (dachs)


Lesenswert?


von Klaus (feelfree)


Lesenswert?

Es ist nicht wirklich sinnvoll, nicht druckbare Zeichen in einem String 
zu speichern, der für druckbare Zeichen vorgesehen ist.

Natürlich geht das trotzdem.
1
char myString[] = ("A = x, B = x.\n");
2
myString[4] = byte1;
3
myString[11] = byte2;

: Bearbeitet durch User
von Sebastian S. (amateur)


Lesenswert?

> Gunnar F.
sprintf(,,) ist tatsächlich dafür geeignet ...
aber da es eine Art eierlegende Wollmilchsau ist, braucht es mächtig 
viel Platz im Speicher.

von StefanK (stefanka)


Lesenswert?

Danke schon Mal für die ersten Antworten. Die Zahlen, die in byte1 und 
byte2 enthalten sind müssen nicht in für Menschen lesbaren Zeichen im 
testString erscheinen. Der testString soll seriell übertragen werden.Es 
reicht, wenn er maschinenlesbar ist.

sprintf verbraucht zu viel Flash. Ich will nicht mehr als ein paar 
Code-Bytes spendieren, bzw. soviel wie minimal nötig.

von Benedikt M. (bmuessig)


Lesenswert?

StefanK schrieb:
> Die Zahlen, die in byte1 und
> byte2 enthalten sind müssen nicht in für Menschen lesbaren Zeichen im
> testString erscheinen.

Du musst nur aufpassen, dass die rohen Bytes am Ende dein Protokoll 
nicht brechen. Schwierig sind da mindestens '\x00', '\n', '\r', und ','.

von Klaus (feelfree)


Lesenswert?

Benedikt M. schrieb:
> Du musst nur aufpassen, dass die rohen Bytes am Ende dein Protokoll
> nicht brechen

Eben deshalb benutzt man für binäre Daten keine Strings.
Es reicht ein Magic-Word als StartWort, dann die beiden Bytes und zum 
Abschluss noch eine einfache Prüfsumme, z.B. die Addition der beiden 
Bytes.
5 Bytes in der Übertragung für 2 Nutzbytes sind schon mehr als genug 
Overhead.

: Bearbeitet durch User
von StefanK (stefanka)


Lesenswert?

Benedikt M. schrieb:
> Du musst nur aufpassen, dass die rohen Bytes am Ende dein Protokoll
> nicht brechen. Schwierig sind da mindestens '\x00', '\n', '\r', und ','.

Interessanter Hinweis, danke. Ich implementiere das in einem Soft-UART. 
Wenn der String übertragen ist, soll ein Zeilenumbruch erfolgen.
Wie erreiche ich den Zeilenumbruch? Bzw womit muss ich den String 
abschliessen?

von StefanK (stefanka)


Lesenswert?

Es muss kein String sein, wenn das damit nicht sinnvoll ist. War nur ein 
erster Gedanke.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

StefanK schrieb:
> Wie erreiche ich den Zeilenumbruch?
1
"Vielleicht mal einen Blick in ein C-Buch werfen?\n"

Gruss
WK

von Mario M. (thelonging)


Lesenswert?

StefanK schrieb:
> Der testString soll seriell übertragen werden.

Dann kann man die Teile auch nacheinander ausgeben.
1
puts("A =");
2
putc(byte1);
3
puts(", B =");
4
putc(byte2);
5
puts(".\n");

von StefanK (stefanka)


Lesenswert?

Dergute W. schrieb:
>
1
"Vielleicht mal einen Blick in ein C-Buch werfen?\n"

Nee, bin grad zu beschäftigt mit Datenblatt-Lesen ;-)

von Nemopuk (nemopuk)


Lesenswert?

Mario M. schrieb:
> puts

Puts hängt Zeilenumbrüche an.

von Mario M. (thelonging)


Lesenswert?

Ja. Das Beispiel war symbolisch gemeint.

von Oliver S. (oliverso)


Lesenswert?

Ein String in C ist ja nun nichts anderes als ein Array. Dafür gibts 
memcpy.

Falls dir das für Strings und den abschliessenden /0 noch zu kompliziert 
ist, gibts dafür passende String-Funktionen. Ich werfe mal strcat und 
strcpy in den Ring.

Oliver

von StefanK (stefanka)


Lesenswert?

Ich würde gern ohne das Einbinden von Funktionen auskommen. Wie gesagt, 
wenn String ungeeignet ist, nehme ich lieber ein Array.

von Harald K. (kirnbichler)


Lesenswert?

Benedikt M. schrieb:
> Du musst nur aufpassen, dass die rohen Bytes am Ende dein Protokoll
> nicht brechen. Schwierig sind da mindestens '\x00', '\n', '\r', und ','.

NUL ist ein Problem, wenn man die für strings vorgesehenen Funktionen à 
la strlen verwendet. Aber was soll an den anderen Zeichen "schwierig" 
sein?

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Dann nimm doch den Vorschlag von Klaus (feelfree) von 15:22

Gruss
WK

von Oliver S. (oliverso)


Lesenswert?

Ist das denn alles zur Compilezeit bekannt, oder sind die Bytes 
variabel?

Oliver

von StefanK (stefanka)


Lesenswert?

Oliver S. schrieb:
> Ist das denn alles zur Compilezeit bekannt, oder sind die Bytes
> variabel?

Die Bytes bzw. die Zahlenwerte werden im Hauptprogramm berechnet und 
sind variabel. Der Textanteil wie z.B. "A = " ist konstant und steht zur 
Compile-Zeit schon fest.

von Benedikt M. (bmuessig)


Lesenswert?

Harald K. schrieb:
> NUL ist ein Problem, wenn man die für strings vorgesehenen Funktionen à
> la strlen verwendet. Aber was soll an den anderen Zeichen "schwierig"
> sein?

Je nach Implementierung der entgegennehmenden Software wird zeilenweise 
eingelesen und verarbeitet. Ein \n (und meistens auch ein \r) würden 
dann zu zwei ungültigen, partiellen Nachrichten führen. Das Komma stört 
beim Trennen der Werte in einer Zeile.

: Bearbeitet durch User
von Cartman E. (cartmaneric)


Lesenswert?

StefanK schrieb:
> sprintf verbraucht zu viel Flash. Ich will nicht mehr als ein paar
> Code-Bytes spendieren, bzw. soviel wie minimal nötig.

Mit ein wenig Pointerakrobatik, geschickt genutzten Unions auf
Arrays usw., ist das ja auch kein Problem.
Nur muss man den Wald und seine Bäume kennen, sonst herrscht da nur
Dunkelheit.

von Nemopuk (nemopuk)


Lesenswert?

Wie gefällt dir das?
1
  char testString[] = "A = ?, B = ?.\n";
2
  testString[4]='x';  // 4 = Position vom ersten Fragezeichen
3
  testString[11]='y'; // 11 = Position vom zweiten Fragezeichen

Ausgabe:
1
A = x, B = y.

Wenn du die Positionen nicht hart kodieren willst, kannst du sie mit 
strchr() suchen. Siehe https://cplusplus.com/reference/cstring/strchr/

: Bearbeitet durch User
von Klaus (feelfree)


Lesenswert?

Nemopuk schrieb:
> Wie gefällt dir das?

Gut, schließlich habe ich es ja schon vor 3 Stunden gepostet.

Nemopuk schrieb:
> mit strchr() suchen

Von hinten durch die Brust ins Auge?
Dann kann man ja gleich sprintf nutzen.

von Nemopuk (nemopuk)


Lesenswert?

Klaus schrieb:
> Gut, schließlich habe ich es ja schon vor 3 Stunden gepostet.

oh, habe ich übersehen 😳

von Peter D. (peda)


Lesenswert?

StefanK schrieb:
> sprintf verbraucht zu viel Flash.

Dann nimmste eben nicht den ATtiny13, sondern den ATtiny85.
Die Lib kostet doch nur beim ersten Aufruf mehr Flash.

von Bruno V. (bruno_v)


Lesenswert?

StefanK schrieb:
> Ich würde gern ohne das Einbinden von Funktionen auskommen. Wie gesagt,
> wenn String ungeeignet ist, nehme ich lieber ein Array.

> Soft-Uart

StefanK schrieb:
> Wie erreiche ich den Zeilenumbruch? Bzw womit muss ich den String
> abschliessen?

StefanK schrieb:
> ohne das Einbinden von Funktionen

In Summe ist m.E. Marios Ansatz der einzig sinnvolle, möglicherweise 
sogar noch eine Nummer "rudimentärer" für den Anfang. Statt

Mario M. schrieb:
> Dann kann man die Teile auch nacheinander ausgeben.
> puts("A =");
> putc(byte1);
> puts(", B =");
> putc(byte2);
> puts(".\n");

eher
1
  
2
    sendUartc('A');
3
    sendUartc(' ');
4
    sendUartc('=');
5
    sendUartc(byte1);
6
    sendUartc(',');
7
    ...

Abschließen kannst Du mit "Linefeed" \n
1
    sendUartc('\n');

oder auch mit '\r' ("carridge return") davor

von Bauform B. (bauformb)


Lesenswert?

Wie oft soll Benedikt das noch sagen?

Benedikt M. schrieb:
> Je nach Implementierung der entgegennehmenden Software wird zeilenweise
> eingelesen und verarbeitet. Ein \n (und meistens auch ein \r) würden
> dann zu zwei ungültigen, partiellen Nachrichten führen. Das Komma stört
> beim Trennen der Werte in einer Zeile.

Ja, es gibt Fälle, wo byte1 und byte2 nie kleiner als 32 werden können. 
Aber selbst dann geht's dank ',' immer noch schief. Solange man den 
möglichen Wertebereich nicht kennt, kann man Strings vergessen.

Ich würde je 2 Zeichen spendieren und Hex senden. Das kostet nur ein 
paar Byte Flash.

von Adam P. (adamap)


Lesenswert?

StefanK schrieb:
> Die Zahlen sollen einfach nur als Byte-Wert in den String
> eingefügt werden. Sie sollen nicht dezimal oder formattiert erscheinen,
> sondern nur als einzelner character, auch wenn es dafür kein
> menschen-lesbares ASCII-Zeichen gibt.

Also wenn du das wirklich so möchtest, dann hab ich dir da eben mal kurz 
was zusammengebastelt. Vielleicht kannst es noch auf deine Bedrüfnisse 
anpassen, aber im großen und ganzen funktioniert es.

Getest in der Konsole (Visual Studio).
1
#include <stdio.h>
2
#include <stdint.h>
3
#include <string.h>
4
5
/**
6
* str        Input String with placeholder '%' for values
7
* values     Pointer to buffer with values
8
* cnt        Number of values in the buffer
9
* 
10
* Return     Number of inserted values
11
*/
12
uint8_t insert_values(char* str, const uint8_t* values, uint8_t cnt)
13
{
14
  uint8_t replaced = 0;
15
  size_t length = strlen(str);
16
17
  for (size_t i=0; i<length; i++)
18
  {
19
    if ((str[i] == '%') && (replaced < cnt))
20
    {
21
      str[i] = values[replaced];
22
      replaced++;
23
    }
24
  }
25
26
  return replaced;
27
}
28
29
int main()
30
{
31
  char string[80];
32
  uint8_t values[10];
33
  uint8_t ret;
34
35
  strcpy(string, "My insert test: % % %");
36
37
  /**
38
  * Um das Ergebnis besser kontrolieren zu können,
39
  * werden hier die Buchstaben A, B und C verwendet.
40
  */
41
  values[0] = 65;  // A
42
  values[1] = 66;  // B
43
  values[2] = 67; // C
44
45
  ret = insert_values(string, values, 3);
46
47
  printf("%d values / new string: [%s]\n", ret, string);
48
49
  return 0;
50
}

von Daniel A. (daniel-a)


Lesenswert?

StefanK schrieb:
> Die Zahlen sollen einfach nur als Byte-Wert in den String
> eingefügt werden.
...
> So soll der zusammengesetzte String (als Text) aussehen:
>   A = K, B = irgendein Zeichen für byte2.
>   neue Zeile

Und wenn du nun 10 / 0xA als Wert von dem Byte hast, dann hast du dort 
ein newline. Also das erscheint mir nicht sinnvoll. Entweder du machst 
alles ein Binärprotokoll, oder alles ein Text Protokoll, aber doch nicht 
gemischt!

In diesem Fall, wie wärs mit 2 Bytes Hexadezimal stattdessen?
1
#include <stdio.h>
2
#include <stdint.h>
3
4
static inline char hex(uint8_t x){
5
  return x<10 ? x+'0' : x<16 ? x-10+'A' : '#';
6
}
7
#define HEX(X) hex((X) / 16), hex((X) % 16)
8
9
int main(void){
10
  int byte1 = 0x4B;
11
  int byte2 = 0xC8;
12
  char testString[] = {
13
    'A',' ','=',' ', HEX(byte1), ',', ' ',
14
    'B',' ','=',' ', HEX(byte2), '.', '\n',
15
    0
16
  };
17
  puts(testString);
18
}

In solchen Fällen wäre es echt praktisch, wenn man sowas wie "char 
x[]={"A = ", 'A', 0};" schreiben könnte, aber leider kann das C noch 
nicht...

Oder man nimmt halt snprintf:
1
char testString[17];
2
snprintf(testString, sizeof(testString), "A = %02X, B = %02X.\n", byte1, byte2);

: Bearbeitet durch User
von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Irgendwie ist das echt so eine Macke von C, oder? Irgendwie scheint C 
Probleme mit Strings zu haben. Merke das gerade bei Arduino-Sprech, was 
ich für meinen MPP-Tracker mit ESP32-Webserver "lernen" musste. Die 
Ausgaben mittels getrennten Funktionsaufrufen aneinander zu basteln ist 
wirklich die einfachste Methode, ein Char an einen String anhängen geht 
auch noch, aber den Versuch, mehrere Strings zusammenfügen und in einer 
Funktion ausgeben, habe ich auch erstmal nach hinten geschoben.

von Dergute W. (derguteweka)


Lesenswert?

Moin,

Ben B. schrieb:
> Irgendwie ist das echt so eine Macke von C, oder? Irgendwie scheint C
> Probleme mit Strings zu haben.

Ja, es ist wie verhext. Um in C mit Strings arbeiten zu koennen, muss 
man C koennen. Ein Teufelskreis.

scnr,
WK

von Oliver S. (oliverso)


Lesenswert?

Dergute W. schrieb:
> Um in C mit Strings arbeiten zu koennen, muss man C koennen.

"Arduino" macht einem das Leben mit Strings ja etwas einfacher. Und aber 
in "Arduino" mit Strings zu arbeiten, muss man "Arduino" können.

Ist schon blöd mit der Programmiererei...

Oliver

von Nemopuk (nemopuk)


Lesenswert?

Ben B. schrieb:
> aber den Versuch, mehrere Strings zusammenfügen ...
> habe ich auch erstmal nach hinten geschoben.

Kennst du etwa strncat() und snprintf() nicht? Vielleicht sollte man die 
Anleitung lesen, bevor man ein Werkzeug benutzt und dabei scheitert.

von Frank D. (Firma: LAPD) (frank_s634)


Lesenswert?

Ben B. schrieb:
> Irgendwie scheint C
> Probleme mit Strings zu haben.

Nein, das Problem sitzt vor dem Computer, C ist nicht das Problem.

von Peter D. (peda)


Lesenswert?

C ist eigentlich sehr durchdacht. Nur die '\0' ist reserviert für 
Stringfunktionen als Endezeichen. Damit ist es theoretisch möglich, 
selbst 1TB lange Strings anzulegen.

von Axel R. (axlr)


Lesenswert?

Beim Arduino kannst du mit "Serial.print" alles wild 
aneinanderklatschen. Der macht das dann schon irgendwie. Ist dort ja 
auch egal, wenn man da einen "dicken" oder einen noch dickeren Prozessor 
auf dem Steckbrett hat.

von Nemopuk (nemopuk)


Lesenswert?

Axel R. schrieb:
> eim Arduino kannst du mit "Serial.print" alles wild
> aneinanderklatschen. Der macht das dann schon irgendwie.

Es geht hier aber um C, nicht um Arduino und auch nicht um C++. Dass es 
in anderne Sprachen einfach ist, hat der TO schon in den 
Eröffnungsbeitrag geschrieben.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Ich kenne die printf-Varianten, aber sie nerven. Man merkt da halt 
irgendwie eine Schwäche der Programmiersprache, und zwar eine, die ich 
von einer Hochsprache so nicht erwarte. In Hochsprachen sollten einem 
solche Probleme eigentlich abgenommen werden bzw. da darf sich der 
Compiler drum kümmern und in anderen Programmiersprachen funktionierts 
ja schließlich auch. Nur manche kriegens irgendwie nicht hin, egal wie 
alt sie sind und wie lange daran schon weiterentwickelt wird.

Und übrigens muss ich euch enttäuschen, ich bin nicht gescheitert. In 
diesem Forum scheinen es ein paar Kranke besonders gerne zu sehen, wenn 
irgend jemand an irgendwas scheitert - solange sie es nicht selber sind 
versteht sich. Es tut mir furchtbar leid, aber der MPP-Tracker und der 
darauf laufende Webserver funktioniert bislang einwandfrei.

von Philipp K. (philipp_k59)


Lesenswert?

Wenn man mit Strings anfängt, bleibt nur strcat oder eigene 
Pointer-Arithmetik.

Letztendlich baut man sich dann auch nur die Funktion von strcat 
Benutzerdefiniert zusammen. (man spart sich aber printf etc. die 
Overhead erzeugen.)

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Ben B. schrieb:
> In Hochsprachen sollten einem solche Probleme eigentlich abgenommen
> werden bzw. da darf sich der Compiler drum kümmern und in anderen
> Programmiersprachen funktionierts ja schließlich auch.

Das sind aber alles Programmiersprachen für weit größere Computer als 
ATiny13. Bedenke, daß C von allen "Hochsprachen" mit Absicht eine der 
niedrigsten ist. Sie stammt aus den frühen 70er Jahren, da hatten die 
Computer nur wenige kB RAM. Deswegen ist C ja für kleine Mikrocontroller 
fast alternativlos.

Arduinos Stream und String Klassen gehen in die Richtung, die dir 
vorschwebt, brauchen aber auch etwas größere Mikrocontroller. Der 
ATtiny85 wurde schon genannt.

: Bearbeitet durch User
von Sebastian S. (amateur)


Lesenswert?

Wenn ich mal unterstelle, dass DU keine eierlegende Wollmilchsau 
brauchst, solltest DU es mal ins Auge fassen, selber eine Funktion zu 
schreiben.

Ist gar nicht so schwer. Vor allem wenn DU genau weist, wie groß der 
Wertebereich ist und nicht noch 1000 Sonderfälle abgedeckt werden 
müssen.

Normale Sensoren liefern nicht plötzlich: "keine Ahnung" sondern immer 
irgendwelche Werte in einem genau vorhersagbaren Bereich.

von Bruno V. (bruno_v)


Lesenswert?

Ben B. schrieb:
> Man merkt da halt
> irgendwie eine Schwäche der Programmiersprache, und zwar eine, die ich
> von einer Hochsprache so nicht erwarte. In Hochsprachen sollten einem
> solche Probleme eigentlich abgenommen werden bzw. da darf sich der
> Compiler drum kümmern und in anderen Programmiersprachen funktionierts
> ja schließlich auch.

Eigentlich nicht. Der Compiler kann nur sehr begrenzt ahnen, was 
dargestellt wird. Meist wird ein Vielfaches an RAM und Rechenleistung 
vorausgesetzt und der String mit dynamischem Speicher zur Laufzeit 
gebastelt.

Für den TO ist selbst die C-Standardlösung noch zu ressourcenhungrig.

von Philipp K. (philipp_k59)


Lesenswert?

Nemopuk schrieb:
> Deswegen ist C ja für kleine Mikrocontroller
> fast alternativlos.

Überhaupt nicht.. es ist ja egal worauf portiert oder gewrapped wird..

Letztendlich ist egal welche Sprache man nimmt, es bleibt nur ein 
"Wrapper" auf optimiertes ASM in einem kompiliertem Binary..

Es kommt halt nur darauf an wie nah es umgesetzt wurde.

z.B. SprintF .. Wäre es Perfekt im Compiler umgesetzt, sollte es nicht 
mehr aufblähen als vergleichbare Strcat Aufrufe. Das macht beim Attiny 
Sinn, beim Mega nicht mehr.. deswegen ist es wohl eher "nice to Have"

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Philipp K. schrieb:
> z.B. SprintF .. Wäre es Perfekt im Compiler umgesetzt, sollte es nicht
> mehr aufblähen als vergleichbare Strcat Aufrufe.

Also überhaupt keine Ahnung...

von Philipp K. (philipp_k59)


Lesenswert?

Johann L. schrieb:
> Also überhaupt keine Ahnung...

und du keinen Horizont ..

bei AVR haben die nunmal das avr-libc Framework für die Teile, es ist 
nur alternativlos weil womöglich alles andere darauf basiert und niemand 
etwas anderes braucht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Philipp K. schrieb:
> Johann L. schrieb:
>> Also überhaupt keine Ahnung...
>
> und du keinen Horizont ..
>
> bei AVR haben die nunmal das avr-libc Framework für die Teile, es ist
> nur alternativlos weil womöglich alles andere darauf basiert und niemand
> etwas anderes braucht.

Kannst du irgendeine Toolchain nennen, gleich ob für AVR oder nicht, ob 
GCC oder nicht, die sprintf zu strcat transformiert?

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

Johann L. schrieb:
> Kannst du irgendeine Toolchain nennen, gleich ob für AVR oder nicht, ob
> GCC oder nicht, die sprintf zu strcat transformiert?

Er wird eine sprintf-Implementierung finden, die auch die 3 Zeilen von 
strcat enthält.

(Für alle nicht C-ler: strcat hat z.B. den Code unten, ohne jeden 
Funktionsaufruf. sprintf braucht je nach Implementierungsumfang ~ 100 
oder 1000 Mal mehr Zeilen Quelltext)
1
    register char * p = d;
2
    while(*p) {p++;}
3
    while (*p = *s) {p++;s++;}
4
    return d;

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Bruno V. schrieb:
> Johann L. schrieb:
>> Kannst du irgendeine Toolchain nennen, gleich ob für AVR oder nicht, ob
>> GCC oder nicht, die sprintf zu strcat transformiert?
>
> Er wird eine sprintf-Implementierung finden, die auch die 3 Zeilen von
> strcat enthält.

Selbst wenn, hat das absolut nix mit meiner Frage zu tun.

von Rahul D. (rahul)


Lesenswert?

Dergute W. schrieb:
> Moin,
>
> StefanK schrieb:
>> Wie erreiche ich den Zeilenumbruch?
> "Vielleicht mal einen Blick in ein C-Buch werfen?\n"
>
> Gruss
> WK
ASCII-Tabelle wäre besser geeignet.

von Markus K. (markus-)


Lesenswert?

Johann L. schrieb:
> Philipp K. schrieb:
>> z.B. SprintF .. Wäre es Perfekt im Compiler umgesetzt, sollte es nicht
>> mehr aufblähen als vergleichbare Strcat Aufrufe.
>
> Also überhaupt keine Ahnung...

Naja, moderne Compiler verstehen den Formatstring und vergleichen ihn 
mit den angegebenen Parametern und bringen ggf. eine Fehlermeldung. Es 
wäre also nicht so schwer, so eine Optimierung einzubauen. Geht 
natürlich nur für statische Formatstrings.

Man hat damit aber die sprintf-Funktion in den Compiler verlagert. Sowas 
möchte ich nicht warten müssen.

von Daniel A. (daniel-a)


Lesenswert?

Markus K. schrieb:
> Es wäre also nicht so schwer, so eine Optimierung einzubauen. Geht
> natürlich nur für statische Formatstrings.

Wird manchmal sogar gemacht. z.B. hier https://godbolt.org/z/snE5Tj3WP 
ist die Funktion komplett weg.

von Re (r42)


Angehängte Dateien:

Lesenswert?

Daniel A. schrieb:
> Markus K. schrieb:
>> Es wäre also nicht so schwer, so eine Optimierung einzubauen. Geht
>> natürlich nur für statische Formatstrings.
>
> Wird manchmal sogar gemacht. z.B. hier https://godbolt.org/z/snE5Tj3WP
> ist die Funktion komplett weg.

Leider nur für den trivialen Formatstring "%s" als Ausnahmefall, 
vermutlich weil da einfach gar nichts für snprintf() zu tun ist und 
deshalb der ganze Aufruf ersatzlos (!) wegoptimiert werden kann.

Aber schon bei Anreicherung des Formatstrings um konstante Zeichen oder 
um ein zweites Stringargument "%s%s" wird auch hier (wie Johann ja 
weiter oben schon nahelegte) nicht etwa zu strcat() transformiert, 
sondern wieder ganz stumpf snprintf() aufgerufen.


Markus K. schrieb:
> Man hat damit aber die sprintf-Funktion in den Compiler verlagert. Sowas
> möchte ich nicht warten müssen.

Zumal strcat() und snprintf() afaik ja auch noch aus verschiedenen 
Bibliotheken kommen. Trotzdem schade, eigentlich...

(re)

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ja, zum Beispiel wird auch printf("Hallo\n") durch puts("Hallo") 
ersetzt.

: Bearbeitet durch User
von Hans (ths23)


Lesenswert?

Nemopuk schrieb:
> Das sind aber alles Programmiersprachen für weit größere Computer als
> ATiny13. Bedenke, daß C von allen "Hochsprachen" mit Absicht eine der
> niedrigsten ist. Sie stammt aus den frühen 70er Jahren, da hatten die
> Computer nur wenige kB RAM. Deswegen ist C ja für kleine Mikrocontroller
> fast alternativlos.
Das ist wohl etwas kurz gesprungen.

Zur damaligen Zeit gab es auch schon andere Hochsprachen, z.B. Pascal 
(1971 und damit 1 Jahr früher als C 1972), die auf den damaligen 
Systemen problemlos liefen. Mikropozessoren und embedded Systeme standen 
zum damaligen Zeitpunkt noch nicht zur Verfügung bzw. waren gerade am 
Start (TMS1000 von TI 1971). Der legendäre Z80 kamm rund 5 Jahre später. 
Diese kleine Systeme wurden  seinerzeit auch nicht mit Hochsprachen 
programmiert da war Assemblercode das höchste der Gefühle und nicht 
selten wurde die SW als Opcode in diese kleinen Systeme eingehackt.
Natürlich gab es auch schon Hochsprachen wie Algol, Fortran, Cobol oder 
Basic, um nur mal eine kleine Auswahl zu nennen, allerdings waren diese 
den damaligen "Großrechnern" vorbehalten - Personalcomputer gab es 1971 
auch noch nicht. Computer waren damals eben vom Volumen her noch 
ordentlich groß.

Welche Hochsprache nun zur Programmierung von Mirocontrollern/embedded 
Systemen benutzt wird ist keine Frage der Sprache selbst, sondern ob es 
einen Compiler/Linker für diese Sprache gibt, der den für den Controller 
passenden Binärcode in möglichst compakter Form erzeugen kann - es gibt 
ja mittlerweile auch Pascalcompiler die embedded Code erzeugen.
Dennoch hat sich da offenbar C als recht vorteilhaft erwiesen. Ich 
zitiere mal aus Wikipedia 
(https://de.wikipedia.org/wiki/C_(Programmiersprache):
"Der Grund liegt in der Kombination von erwünschten Charakteristiken wie 
Portabilität und Effizienz mit der Möglichkeit, Hardware direkt 
anzusprechen und dabei niedrige Anforderungen an eine Laufzeitumgebung 
zu haben." (Zitatende).
C in seiner Grundversion hat eigentlich nur 4 Datentypen (char, int, 
float, double) und nur 32 Schlüsselworte, was die Sprache sehr schlank 
und damit prädisteniert für embedded Systeme macht. Einen Stringtyp, um 
den es in diesem Thread geht, kennt C gar nicht. Das ist halt ein 
char-Array. Genau das macht das Stringhandling für den Programmierer 
(Anfänger) eben schwieriger und ungewohnter, zumindest für Leute die von 
anderen Sprachen kommen.
Neben den vielen Vorteilen, muß man eben auch einige Nachteile in Kauf 
nehmen - Stichwort: undefined behavior.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Philipp K. schrieb:
> z.B. SprintF .. Wäre es Perfekt im Compiler umgesetzt, sollte es nicht
> mehr aufblähen als vergleichbare Strcat Aufrufe. Das macht beim Attiny
> Sinn, beim Mega nicht mehr.. deswegen ist es wohl eher "nice to Have"

Eben.

Es gibt noch vieles, was der Compiler idealerweise optimieren können
sollte. Bspw. könnte ich mir vorstellen, dass er jegliche Arithmetik im
Programmcode mittels eines integrierten CAS erst einmal mathematisch
vereinfacht, bevor er Assembler-/Maschinencode daraus generiert.

Wahrscheinlich fällt jedem irgendetwas ein, was der Compiler noch
zusätzlich können sollte. Integriert man das alles in den Compiler,
passt dieser halt irgendwann nicht mehr in 64 GB Hauptspeicher ;-)

Eigentlich finde es ja auch ganz gut, dass man beim Programmieren noch
etwas mitdenken darf. Für das Problem des TE sind mehrere effiziente
Lösungen vorgeschlagen worden, auf die mit etwas Nachdenken jeder hätte
kommen können. Auch arithmetische Vereinfachungen können IMHO durchaus
dem Programmierer zugemutet werden.

Und wenn der Schalter für die Aktivierung des Gehirns dann doch einmal
klemmt, gibt es ja immer noch die KI :)

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Die Optimierung ist ja auch das, was Compiler generell am besten können 
sollten. Da kann man mit massivem Rechenleistungsbedarf bzw. Ressourcen 
draufhauen, um das beste Ergebnis zu bekommen. Macht man nur ein 
einziges Mal, danach ist es fertig und spart hinterher Ressourcen im 
Betrieb ein.

Wenn man sowas warten muss, dann wartet man ja normalerweise den 
ursprünglichen Quelltext und nicht das Compilat. Das ist doch der Kern, 
den ich von einer Hochsprache erwarte - möglichst gute Ergebnisse in (im 
Vergleich zu Assembler) deutlich kürzeren Programmierzeit, 
Portierbarkeit über mehrere Prozessor-Architekturen und daß ich mich 
eben genau nicht um das kümmern muss, was da an Maschinencode aus dem 
Compiler rauskommt, bzw. noch nicht mal das System (oder spezielle 
Prozessor-Eigenschaften) kennen muss, auf dem das Programm irgendwann 
mal laufen soll.

von Markus K. (markus-)


Lesenswert?

Ben B. schrieb:
> Die Optimierung ist ja auch das, was Compiler generell am besten können
> sollten. Da kann man mit massivem Rechenleistungsbedarf bzw. Ressourcen
> draufhauen, um das beste Ergebnis zu bekommen. Macht man nur ein
> einziges Mal, danach ist es fertig und spart hinterher Ressourcen im
> Betrieb ein.

Im Prinzip schon, aber es gibt viele Leute, die Interpreter bevorzugen, 
weil man da nicht ständig auf den Compiler warten muss.

> Wenn man sowas warten muss, dann wartet man ja normalerweise den
> ursprünglichen Quelltext und nicht das Compilat.

Mit "warten" meinte ich den Compiler. Wenn normalerweise ein Bug in 
sprintf ist, dann wird er in der stdlib gefixt und es gibt eine neue 
Version der stdlib. Wenn aber der Compiler diese Optimierung macht, dann 
muss man den Bug im Compiler fixen und je nach Implementation vielleicht 
auch noch die Stdlib. Dann ist es plötzlich wichtig, dass die Entwickler 
immer die neueste Version vom Compiler installiert haben.

Auch vom Verständnis her: Eine einzelne Funktion in der stdlib zu 
verstehen (Sourcecode lesen) ist nicht so schwierig, denn die sind ja 
relativ unabhängig voneinander, aber in den Compiler schaut man nicht 
mal so kurz rein.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

Achso.

Naja, daß man Bugs in seiner Software (also auch in Compilern) fixen 
sollte, habe ich mal vorausgesetzt. Und natürlich ist ein Compiler ein 
eher komplexes Stück Software, wer sowas mit hoher Qualität schreiben 
kann, der kann es hinterher auch warten. Das werden ja Teams sein, sowas 
schreibt man heutzutage wohl nicht mehr alleine im stillen Kämmerlein.

Das generelle Problem bei sowas ist eher, sich in fremden Quellcodes 
zurechtzufinden. Vor allem wenn sie (mit dem Alter zunehmend) schlecht 
dokumentiert und kommentiert sind.

von Peter D. (peda)


Lesenswert?

Wenn man aus der Assemblerecke kommt, dann sieht printf erstmal riesig 
aus. Aber nennenswert Flash kostet immer nur der erste Aufruf. Danach 
kostet es nur noch weitere Formatstrings und den Call.
Float printf paßt sogar spielend in den kleinen ATTiny85.

Daß der Formatstring eine Variable sein kann, ist durchaus ein 
nützliches Feature. Ich habe das schon mehrmals benutzt.

In Assembler zu programmieren war vorwiegend ein notwendiges Übel. Vor 
den µCs hatte ich schon auf dem PC in Turbo-C programmiert.
Den Keil C51 hatte ich erst 1995 verfügbar und das war immer ein Hassle, 
ständig den Dongle umzustecken.
Der AVR-GCC habe ich nicht zum Laufen bringen können. Den konnten die 
nicht Linuxer erst mit WINAVR auch benutzen.
Es soll immer noch Leute geben, die den WINAVR2010 benutzen.

Assemblerkenntnisse helfen aber durchaus zu analysieren, wenn der 
Compiler mal etwas besonders umständlich macht oder ob er überhaupt das 
macht, was man dachte.

: Bearbeitet durch User
von Bauform B. (bauformb)


Lesenswert?

Markus K. schrieb:
> Mit "warten" meinte ich den Compiler. Wenn normalerweise ein Bug in
> sprintf ist, dann wird er in der stdlib gefixt und es gibt eine neue
> Version der stdlib. Wenn aber der Compiler diese Optimierung macht, dann
> muss man den Bug im Compiler fixen und je nach Implementation vielleicht
> auch noch die Stdlib.

...und die newlib und die dietlibc und... eine Dose voller Würmer. Und 
wer mit "-ffreestanding" kompiliert, hat vielleicht eine eigene libc. 
Ich habe selten so gestaunt, wie damals, als der gcc mein printf() durch 
puts() ersetzt hat.

Zu der ursprünglichen Frage: will man überhaupt Strings zusammen bauen? 
Ich finde printf() viel übersichtlicher. Oft muss man sowieso warten, 
bis die Ausgabe fertig ist. Dann kann printf() direkt ausgeben und 
braucht nur ein paar Byte Stack. Wer fprintf() nicht mag, kann auch 
stdout umschalten.

Damit sich die Ausgabe per DMA oder Interrupt wirklich lohnt, braucht 
printf() beliebig viel RAM. Oder man muss doch wieder warten, aber nur 
manchmal, und man weiß trotzdem nicht, wann die Ausgabe fertig ist.

von Markus K. (markus-)


Lesenswert?

Ben B. schrieb:
> Naja, daß man Bugs in seiner Software (also auch in Compilern) fixen
> sollte, habe ich mal vorausgesetzt.

Aber das ist genau der Punkt. Wenn Du Maintainer der glibc bist, dann 
ist der Compiler eben nicht Deine Software. Das ist ein ganz anderes 
Projekt, mit anderen Leuten. Da kommt dann ein Bugreport rein und dann 
ist das 10 Jahre her, dass das implementiert wurde und der Compiler 
sieht intern mittlerweile ganz anders aus.

Natürlich wird man dann einen Bugreport beim Compiler-Projekt einkippen 
usw. usf. aber insgesamt ist das alles sehr viel komplexer geworden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ben B. schrieb:
> Die Optimierung ist ja auch das, was Compiler generell am besten können
> sollten.

An erster Stelle sehe ich da die Korrektheit des erzeugten Codes.

> Da kann man mit massivem Rechenleistungsbedarf bzw. Ressourcen
> draufhauen, um das beste Ergebnis zu bekommen.

Nicht wirklich.  In GCC zum Beispiel müssen alle Algorithmen 
linearistisch in der Eingabe sein, d.h. Komplexität ist O(x^{1+eps}) für 
beliebig kleines eps (zum Beispiel ist Komplexität x*log(x) ok, x^2 ist 
es nicht).  Viele Algorithmen haben jedoch sogar exponentielle Laufzeit, 
etwa beste Registerallokation, bestes Scheduling, etc.  Die 
nichttriviale Kunst besteht dann darin, eine abgeschwächte 
linearistische Version des Algorithmus zu finden, die keine allzu großen 
Abstricht bei der Codegüte macht.  Das beste Ergebnis (also an der 
Pareto-Grenze) ist jedoch ein nicht erreichbares Ziel.

Yalu X. schrieb:
> Es gibt noch vieles, was der Compiler idealerweise optimieren können
> sollte. Bspw. könnte ich mir vorstellen, dass er jegliche Arithmetik im
> Programmcode mittels eines integrierten CAS erst einmal mathematisch
> vereinfacht, bevor er Assembler-/Maschinencode daraus generiert.

Im GCC kein voll ausgewachsenes CAS (wäre zu langsam), aber immerhin 
eine Beschreibungssprache die Vereinfachungen beschreibt:

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=gcc/match.pd
https://gcc.gnu.org/onlinedocs/gccint/The-Language.html

Leider liegt es im Auge des Betrachters, was unter "optimal" zu 
verstehen ist, und match.pd beschreibt sowohl Optimierungen als auch 
Kanonisierungen.  Das kann auch gerne mal in die Hose gehen, wie zum 
Beispiel hier:
1
typedef unsigned long T;
2
3
T func (T a, T b, T c)
4
{
5
    if (c & 1)
6
        a |= b;
7
    return a;
8
}
1
$ avr-gcc-14 foo.c -S -Os -mmcu=atmega8
1
func:
2
  push r8
3
  push r9
4
  push r10
5
  push r11
6
  push r14
7
  push r15
8
  push r16
9
  push r17
10
/* prologue end */
11
  movw r8,r22
12
  movw r10,r24
13
  movw r24,r20
14
  movw r22,r18
15
  bst r14,0
16
  clr r18
17
  clr r19
18
  movw r20,r18
19
  bld r18,0
20
  rcall __mulsi3
21
  or r22,r8
22
  or r23,r9
23
  or r24,r10
24
  or r25,r11
25
/* epilogue start */
26
  pop r17
27
  pop r16
28
  pop r15
29
  pop r14
30
  pop r11
31
  pop r10
32
  pop r9
33
  pop r8
34
  ret
https://gcc.gnu.org/PR118012
https://godbolt.org/z/v6xrdfM1r

Macht also eine 32-Bit Multiplikation!

Auch bei Clang/LLVM tun einem die Augen weh; auch da wird auf Teufel 
komm raus versucht, linearen Code zu erzeugen:
1
func:
2
  movw  r30, r14
3
  andi  r30, 1
4
  andi  r31, 0
5
  neg  r31
6
  neg  r30
7
  sbc  r31, r1
8
  and  r18, r30
9
  and  r19, r31
10
  or  r22, r18
11
  or  r23, r19
12
  and  r30, r20
13
  and  r31, r21
14
  or  r24, r30
15
  or  r25, r31
16
  ret
Wie jeder AVR Programmierer weiß, geht das alles in 6 Instruktionen. 
Mit avr-gcc v8 und v15 besser, aber auch da reibt man sich die Augen:
1
func:
2
  push r14
3
  push r15
4
  push r16
5
  push r17
6
/* prologue: function */
7
  sbrs r14,0
8
  rjmp .L2
9
  or r22,r18
10
  or r23,r19
11
  or r24,r20
12
  or r25,r21
13
.L2:
14
/* epilogue start */
15
  pop r17
16
  pop r16
17
  pop r15
18
  pop r14
19
  ret

Hier ist das Problem, dass c als long R14 übergeben wird, also in 
callee-saved Registern und es in GCC keine Möglichkeit gibt zu 
unterscheiden, ob R14 nur lesend zugegriffen wird oder verändert 
(PR109910).

Alles Problem(chen), die man in Compilern des 21 Jahrhunderts nicht mehr 
erwartet...

: Bearbeitet durch User
von Daniel A. (daniel-a)


Lesenswert?

Johann L. schrieb:
> Die nichttriviale Kunst besteht dann darin, eine abgeschwächte
> linearistische Version des Algorithmus zu finden, die keine allzu großen
> Abstricht bei der Codegüte macht.

https://xkcd.com/3026/

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> An erster Stelle sehe ich da die Korrektheit des erzeugten Codes.
Na sowas ist bei mir allein schon aus der Logik impliziert, das sehe ich 
nicht als herausstechende elementare Funktion. Einen Compiler, der aus 
einem beliebigen Quellcode nur Scheiße produziert, kann ich auch 
alleine. Aber mit sowas kann niemand was anfangen.

Und dann ist noch die Frage, worauf man gerne hin optimieren möchte. 
Geringste Code-Größe, geringsten RAM-Bedarf? Höchste Geschwindigkeit 
bzw. höchste Geschwindigkeit auf einer bestimmten Plattform? Da müsste 
man dem Compiler entsprechende Vorgaben machen können, was man gerne als 
Ergebnis hätte.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ben B. schrieb:
>> An erster Stelle sehe ich da die Korrektheit des erzeugten Codes.
> Na sowas ist bei mir allein schon aus der Logik impliziert,

Welche Logik soll das denn sein, die Fehler ausschließt oder unmöglich 
macht?

von Klaus (feelfree)


Lesenswert?

Ben B. schrieb:
> Da müsste
> man dem Compiler entsprechende Vorgaben machen können, was man gerne als
> Ergebnis hätte.

Die Option -O existiert.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> Welche Logik soll das denn sein, die Fehler
> ausschließt oder unmöglich macht?
Möchtest Du einen Compiler haben, der Dir fehlerhaften Code produziert?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Klaus schrieb:
> Ben B. schrieb:
>> Da müsste man dem Compiler entsprechende Vorgaben machen können,
>> was man gerne als Ergebnis hätte.
>
> Die Option -O existiert.

GCC kennt mehr als 500 Optionen und Parameter (--param) alleine zum 
Tunen der Optimizer:

https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html

Hinzu kommen die genannten Sammeloptionen wie -O0, -O1, -O2, -O3, -Og, 
-Os, -Oz, -Ofast die 99% aller praktischen Belange abdecken.

Zusätzlich gibt es Optimierungsoptionen für einzelne Architekturen (für 
AVR ca 1 Duzend).

Und schließlich wird auch gerne das ABI gepimpt um die Performance zu 
beeinflussen (-f[no-][un]signed-bitfields, -f[no-]short-enums, 
-f[no-][un]signed-char, -f[no-]wrapv, -f[no-]strict-overflow, 
-f[no-]strict-aliasing, -f[no-]common, -f[no-]jump-tables, ...)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ben B. schrieb:
>> Welche Logik soll das denn sein, die Fehler
>> ausschließt oder unmöglich macht?
> Möchtest Du einen Compiler haben, der Dir fehlerhaften Code produziert?

Das ist keine Antwort auf meine Frage.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

>>> Welche Logik soll das denn sein, die Fehler
>>> ausschließt oder unmöglich macht?
>> Möchtest Du einen Compiler haben,
>> der Dir fehlerhaften Code produziert?
> Das ist keine Antwort auf meine Frage.
Kannst Du Dir die Antwort darauf nicht selbst geben?
Falls doch, ist das glasklare Logik.

Genauso wie es eine Tatsache ist, daß sich Programmfehler niemals völlig 
ausschließen oder unmöglich machen lassen, erst recht nicht wenn man das 
komplette System betrachtet (Stichwort Bitfehler). Man kann sie durch 
Qualitätskontrolle und Tests minimieren, aber niemals ganz und für alle 
Situationen ausschließen.

von Rbx (rcx)


Lesenswert?

Hm, der GHC wurde in C programmiert, so gesehen kann man von gekonnter C 
Programmierung so einiges erwarten.

https://de.wikibooks.org/wiki/C-Programmierung:_Verkettete_Listen

Beim PC kann man auch die SSE für Stringbearbeitung nutzen.

von Pandur S. (jetztnicht)


Lesenswert?

C-Strings sind absolut ungeeignet fuer Controller, da man den String 
durchgehen muss, um zur Laenge zu kommen. Besser Pascal Strings 
verwenden, bei welchem die Laenge binaer im 0-ten byte steht.
Die C-String-Routinen sollte ma eh gleich sein lassen, da viel zu 
klotzig.

: Bearbeitet durch User
von Nemopuk (nemopuk)


Lesenswert?

Pandur S. schrieb:
> C-Strings sind absolut ungeeignet fuer Controller

So ungeeignet, wie Bleistifte zum schreiben. WTF?

> Die C-String-Routinen sollte ma eh
> gleich sein lassen, da viel zu klotzig.

Werfe mal einen Blick in den Quelltext der avr-libc. Danach hast du 
wenigstens einen Funken Ahnung von dem Thema.

: Bearbeitet durch User
von Pandur S. (jetztnicht)


Lesenswert?

Ein printf mit einem Float verschleudert 16k auf einem AVR

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Pandur S. schrieb:
> Ein printf mit einem Float verschleudert 16k auf einem AVR

Wo siehst du bei der eingangs gestellten Frage denn float oder printf?

Da geht es darum, jeweils einzelne Bytes in einem String zu setzen, und 
die Antwort wurde bereits hier gegeben:

Beitrag "Re: Wie baut man in C Strings zusammen?"

Da braucht es nich nicht mal String-Funktionen aus string.h, geschweide 
denn stdio.h.

Außerdem braucht es für float -> ASCII kein printf, sondern es gibt 
dafür auch spezielle Funktionen.  Dafür muss der Horizont aber weiter 
als printf reichen...

> Ein printf mit einem Float verschleudert 16k auf einem AVR

Und zudem sind wir hier in PC-Programmierung?

: Bearbeitet durch User
von Nemopuk (nemopuk)


Angehängte Dateien:

Lesenswert?

Pandur S. schrieb:
> Ein printf mit einem Float verschleudert 16k auf einem AVR

Ich komme auf ganz andere Zahlen. Im angehängten Projekt habe ich drei 
Varianten implementiert, um einen Millisekunden-Zähler seriell 
aufzugeben.

Ohne Ausgabe: 303 Bytes
Mit utoa() und puts(): 997 Bytes
Mit printf() dezimal: 2279 Bytes
Mit printf() float (erfordert eine Änderung im Makefile): 3931 Bytes

Compiliert mit avr-gcc 5.4.0

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Nemopuk schrieb:
> Pandur S. schrieb:
>> Ein printf mit einem Float verschleudert 16k auf einem AVR
>
> Ich komme auf ganz andere Zahlen. [...]
> Mit printf() float (erfordert eine Änderung im Makefile): 3931 Bytes

Die Benchmarks der AVR-LibC listen ca. 3 KiB für sprintf für float 
(sprintf_flt).  Dies enthält alle Abhängigkeiten von sprintf, allerdings 
kein Startup-Code und keine Vektortabelle.

https://avrdudes.github.io/avr-libc/avr-libc-user-manual/benchmarks.html

Die 16k von Pandur sind also aus dem Finger genuckelt.

Aktuellere Benchmarks mit AVR-LibC v2.3 + avr-gcc v15 bleiben bei ca 3 
KiB für sprintf + float.

von Philipp K. (philipp_k59)


Lesenswert?

Rbx schrieb:
> Beim PC kann man auch die SSE für Stringbearbeitung nutzen.

Das witzige ist, z.B. bei Linux bekommt man C-Sourcen über den 
Paketinstaller, bei denen SSE in den direkten Stringfunktionen zu finden 
ist.

Bei Windows ist an den Stellen verschachtelte Pointer Arithmetik.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Philipp K. schrieb:
> Rbx schrieb:
>> Beim PC kann man auch die SSE für Stringbearbeitung nutzen.
>
> Das witzige ist, z.B. bei Linux bekommt man C-Sourcen über den
> Paketinstaller, bei denen SSE in den direkten Stringfunktionen zu finden
> ist.
>
> Bei Windows ist an den Stellen verschachtelte Pointer Arithmetik.

Das ist doch weniger eine Windows / Linux Sache als vielmehr, welche 
Toolchain bzw. Libc man verwendet?

von Philipp K. (philipp_k59)


Lesenswert?

Johann L. schrieb:
> Das ist doch weniger eine Windows / Linux Sache als vielmehr, welche
> Toolchain bzw. Libc man verwendet?

Mag sein, ich hatte in Linux nur die Standard Version damals.. bei 
Windows habe ich da keine Ahnung. "Einfach installiert und go".

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.