mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik C++: Objekt mit Adresse einer Variablen initialisieren klappt nicht


Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich bringe mir gerade selbst C++ bei, und bin auf ein Problem bei der 
Initialisierung von Objekten gestoßen.
struct fifo_char_t Empfangskanal;
static Shell prompt(&Empfangskanal);

Ich erzeuge eine globale Variable vom Typ 'struct fifo_char_t'.
Anschließend erzeuge ich ein 'prompt'-Objekt und übergebe die Adresse 
der erwähnten Variable dem Initializer. Diese soll dann als data member 
im Objekt abgelegt werden.

Der Initializer sieht folendermaßen aus:
class Shell {
  public:
          Shell(struct fifo_char_t *);
    void UpdateBuffer();
    bool IsFullLineAvailable();
    bool IsBufferEmpty();
    char ReadBuffer();
  private:
    struct fifo_char_t * input_buffer_;
    struct fifo_char_t buffer_;
    bool line_received_;
};
Shell::Shell(struct fifo_char_t * input_buffer)
{
        input_buffer_ = input_buffer;
}

Im Debugger sehe ich, dass 'Empfangskanal' auch korrekt in dem RAM 
meines Mikrocontrollers gelinkt wurde. Das Befüllen dieser Variable 
klappt reibungslos.

Allerdings: Wenn ich mir zur Laufzeit die data member meines Objektes 
'prompt' ansehe, sehe ich, dass 'input_buffer_' auf 0x0 zeigt.

Wie gesagt bin ich noch recht neu was C++ anbelangt. Hat das 
irgendwelche offensichtlichen Gründe?

Danke
Manuel

: Bearbeitet durch User
Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manuel W. schrieb:
> Allerdings: Wenn ich mir zur Laufzeit die data member meines Objektes
> 'prompt' ansehe,

Wie und womit?

Autor: Sven B. (scummos)
Datum:

Bewertung
-2 lesenswert
nicht lesenswert
Soweit ich weiß findet die Initialisierung aller statics statt, bevor 
deine globale Variable initialisiert wird. Bin aber nicht sicher.

Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rufus Τ. F. schrieb:
> Manuel W. schrieb:
>> Allerdings: Wenn ich mir zur Laufzeit die data member meines Objektes
>> 'prompt' ansehe,
>
> Wie und womit?

Mit dem Debugger. Ich benutze Em::Blocks. Das Ding hat ein GCC-Backend 
und verwendet zum debuggen soweit ich weiß GDB.

: Bearbeitet durch User
Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sven B. schrieb:
> Soweit ich weiß findet die Initialisierung aller statics statt,
> bevor
> deine globale Variable initialisiert wird. Bin aber nicht sicher.

Also ich habe jetzt mal testweise das 'static' weggenommen, leider 
besteht das Problem jedoch weiterhin.

Autor: Mikro 77 (mikro77)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manuel W. schrieb:
> Hat das irgendwelche offensichtlichen Gründe?

Wenn es welche gibt, dann sehe ich sie auch nicht.

Da du dem constructor eine Adresse übergibst, kann der Wert niemals NULL 
sein.

Es gibt keinen (impliziten) default constructor, der auf NULL 
initialisieren könnte.

Der implizite copy constructor kopiert die Adresse (kann also auch in 
der Kopie nicht NULL sein).

Da brauchen wir wohl mehr Infos.

Autor: Sven B. (scummos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mikro 7. schrieb:
> Manuel W. schrieb:
>> Hat das irgendwelche offensichtlichen Gründe?
> Da du dem constructor eine Adresse übergibst, kann der Wert niemals NULL
> sein.

??

Foo(nullptr);

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sven B. schrieb:
> ??

Du hast die Formulierung nicht verstanden.

Natürlich kann man einen Nullpointer übergeben. Das aber geschieht hier 
nicht, hier wird die Adresse eines existierenden Objektes übergeben. Und 
die kann nicht NULL sein.

Manuel W. schrieb:
> Mit dem Debugger.

Und was bedeutet in diesem Kontext Deine Formulierung "zur Laufzeit"?

Autor: Sebastian E. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hast du mal einen Breakpoint (oder LED/printf-Debug) in den Konstruktor 
gesetzt, um zu schauen, ob dieser überhaupt aufgerufen wird? Wenn er 
nicht aufgerufen wird, stimmt wahrscheinlich mit deinem Startup-Code 
etwas nicht.

Wird dein Objekt korrekt initialisiert, wenn du es innerhalb einer 
Funktion (z.B. main) auf dem Stack anlegst (ohne new)?

Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rufus Τ. F. schrieb:
> Manuel W. schrieb:
>> Mit dem Debugger.
>
> Und was bedeutet in diesem Kontext Deine Formulierung "zur Laufzeit"?

Damit meine ich, dass ich einen Breakpoint auf den Beginn der 
main()-Funktion gelegt habe, und mir Inhalt des Objektes anzeigen hab 
lassen.

Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sebastian E. schrieb:
> Hast du mal einen Breakpoint (oder LED/printf-Debug) in den Konstruktor
> gesetzt, um zu schauen, ob dieser überhaupt aufgerufen wird? Wenn er
> nicht aufgerufen wird, stimmt wahrscheinlich mit deinem Startup-Code
> etwas nicht.

Nein, habe ich nicht. Mein Verständnis war, dass Objekte die global 
angelegt werden (also außerhalb einer Funktion) bereits zur 
Kompilierzeit instanziert werden. (Dh. die dahinterliegenden Strukturen 
werden so wie .data oder .bss section vom Startup Code initialisiert.) 
Ich hatte nicht angenommen, dass tatsächlich ein Funktionseinsprung in 
den Initializer erfolgt, da zu diesem Zeitpunkt die Vorraussetzungen zum 
Ausführen einer C-Funktion ja evtl. noch nicht gegeben sind. 
(Initialisierter Stackpointer usw.)

Sebastian E. schrieb:
> Wird dein Objekt korrekt initialisiert, wenn du es innerhalb einer
> Funktion (z.B. main) auf dem Stack anlegst (ohne new)?

Ja, dann funktioniert es es reibungslos!

Autor: Sven B. (scummos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manuel W. schrieb:
> Ich hatte nicht angenommen, dass tatsächlich ein Funktionseinsprung in
> den Initializer erfolgt, da zu diesem Zeitpunkt die Vorraussetzungen zum
> Ausführen einer C-Funktion ja evtl. noch nicht gegeben sind.
> (Initialisierter Stackpointer usw.)

Deshalb passiert das ja auch erst später. Aber wie soll der Konstruktor 
zur Compilezeit ausgeführt werden? Das passiert nur, wenn er als 
constexpr markiert ist.

Autor: Mikro 77 (mikro77)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
struct Foo { char *p ; Foo(char *p) : p(p) {} } ;

static char text[] = "Is it you, Kurt?" ;

static Foo foo(text) ;

int main()
{
  return 0 ;
}
prompt> g++ -g3 -Wall -o foo foo.cc
prompt> gdb foo
...
(gdb) break main
Breakpoint 1 at 0x4004f1: file foo.cc, line 9.
(gdb) run
Starting program: /var/export/Data/Projects/rpi/lib/foo 

Breakpoint 1, main () at foo.cc:9
9    return 0 ;
(gdb) print foo
$1 = {p = 0x601040 <text> "Is it you, Kurt?"}

Autor: Daniel Abrecht (daniel-a)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kann dein Debugger einen watchpoint auf input_buffer_ setzen, so dass du 
siehst wenn dieser gesetzt wird? Dann könnte man die Addresse von this 
ausgeben und ein backtrace machen. Wenn die Variable wieder auf 0 
gesetzt wird könnte man das wiederholen, und wenn man später die 
Addresse von prompt ausgibt wenn input_buffer_ 0 ist kann man schauen, 
ob es das gleiche Objekt wie bei bei der Ausgabe von this damals war, 
indem man die Addresse mit der von damals vergleicht.

Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sven B. schrieb:
> Manuel W. schrieb:
>> Ich hatte nicht angenommen, dass tatsächlich ein Funktionseinsprung in
>> den Initializer erfolgt, da zu diesem Zeitpunkt die Vorraussetzungen zum
>> Ausführen einer C-Funktion ja evtl. noch nicht gegeben sind.
>> (Initialisierter Stackpointer usw.)
>
> Deshalb passiert das ja auch erst später. Aber wie soll der Konstruktor
> zur Compilezeit ausgeführt werden? Das passiert nur, wenn er als
> constexpr markiert ist.

Ich dachte, dass der Konstruktor dann nicht im eigentlichen Sinne 
"ausgeführt" wird, sondern sich der Compiler bereits zur Kompilierzeit 
herleiten würde, was dann an den entsprechenden Speicherstellen zu 
stehen hat. In meinem Fall wäre das einfach: Da das Object als data 
member die Adresse der anderen globalen Struktur 'Empfangskanal' hat, 
müsste der Compiler wohl nur das symbol dieser anderen Struktur 
referenzieren. Das Eintragen der konkreten Adresse macht dann der 
Linker.

Von constexpr habe ich schonmal gehört. Das kam mit C++11, oder? Derzeit 
bin ich in meinem C++-Verständnis noch meilenweit davon entfernt... 
Leider.

: Bearbeitet durch User
Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Was ist denn nun eigentlich das Problem? Kommt beim Zugriff Mist raus? 
Wird überhaupt zugegriffen? Oder hast du ein im Programm komplett 
ungenutztes Objekt per Debugger angeschaut?

Manuel W. schrieb:
> Ich dachte, dass der Konstruktor dann nicht im eigentlichen Sinne
> "ausgeführt" wird, sondern sich der Compiler bereits zur Kompilierzeit
> herleiten würde, was dann an den entsprechenden Speicherstellen zu
> stehen hat.

Meine Vermutung ist, dass er noch einen Schritt weiter geht: Er erkennt, 
dass die Variable nirgends benutzt wird und der Konstruktor keine 
Nebeneffekte hat und optimiert deshalb die Initialisierung komplett weg. 
Was nicht gebraucht wird, muss ja nicht extra befüllt werden.

Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich benutze das Objekt tatsächlich. Der Mikrocontroller dereferenziert 
den Pointer und schreibt in's Nirvana.

Ich danke euch allen vielmals für die Unterstützung! Leider muss ich 
mich jetzt auf den Weg machen zu einer 30er-Feier. Sofern es mein 
Zustand zulässt melde ich mich morgen wieder!

Danke!!

Autor: Sven B. (scummos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manuel W. schrieb:
> Ich dachte, dass der Konstruktor dann nicht im eigentlichen Sinne
> "ausgeführt" wird, sondern sich der Compiler bereits zur Kompilierzeit
> herleiten würde, was dann an den entsprechenden Speicherstellen zu
> stehen hat. In meinem Fall wäre das einfach: Da das Object als data
> member die Adresse der anderen globalen Struktur 'Empfangskanal' hat,
> müsste der Compiler wohl nur das symbol dieser anderen Struktur
> referenzieren. Das Eintragen der konkreten Adresse macht dann der
> Linker.
Nein, i.A. geht das nicht. Der Konstruktur kann ja die Member auch mit 
Sachen initialisieren die er aus einer Datei liest, oder vom Netzwerk, 
oder sonstwoher. Was du beschreibst geht nur in eng eingegrenzten 
Spezialfällen, nämlich wenn der Konstruktur constexpr ist.

> Von constexpr habe ich schonmal gehört. Das kam mit C++11, oder? Derzeit
> bin ich in meinem C++-Verständnis noch meilenweit davon entfernt...
Ja, in 11 kann man es aber wirklich nur auf sehr wenige Dinge anwenden. 
In C++14 ist es etwas flexibler.

Autor: Rolf Magnus (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sven B. schrieb:
> Nein, i.A. geht das nicht. Der Konstruktur kann ja die Member auch mit
> Sachen initialisieren die er aus einer Datei liest, oder vom Netzwerk,
> oder sonstwoher. Was du beschreibst geht nur in eng eingegrenzten
> Spezialfällen, nämlich wenn der Konstruktur constexpr ist.

Er kann es als Optimierung auch ohne constexpr machen, wenn er den 
Inhalt des Konstruktors kennt und da eben die oben genannten Aktionen 
nicht gemacht werden.

Manuel W. schrieb:
> Ich benutze das Objekt tatsächlich. Der Mikrocontroller dereferenziert
> den Pointer und schreibt in's Nirvana.

Hmm, dann würde mir nur noch einfallen, dass im Startup-Code deiner 
nicht näher genannten Laufzeitumgebung die Konstruktoren von statischen 
Objekten nicht aufruft. Manche handhaben C++-Code da nicht korrekt.

Autor: Manuel W. (multisync)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Manuel W. schrieb:
>> Ich benutze das Objekt tatsächlich. Der Mikrocontroller dereferenziert
>> den Pointer und schreibt in's Nirvana.
>
> Hmm, dann würde mir nur noch einfallen, dass im Startup-Code deiner
> nicht näher genannten Laufzeitumgebung die Konstruktoren von statischen
> Objekten nicht aufruft. Manche handhaben C++-Code da nicht korrekt.

Ich benutze Em::Blocks mit dem mitgelieferten GCC.
C:\Program Files (x86)\EmBlocks\2.30\share\em_armgcc\arm-none-eabi\bin>gcc.exe --version
gcc.exe (EmBlocks ARM Embedded Processors GNU tools) 4.7.3

Der Startup-Code ist anbei. Bei der Durchsicht habe ich tatsächlich 
nichts gefunden was einen Aufruf von Konstruktoren andeutet... 
Interessant.

Dieser Startup Code war bei Em::Blocks dabei. Ich habe mir jetzt 
allerdings auch den angesehen, der bei CMSIS von ARM und bei der 
Standard-Peripheral-Library von ST dabei ist, im wesentlichen dürfte 
sich der aber nicht all zu groß unterscheiden.

Auch das im Map-File 'prompt' (das ist der Name des Objekts dessen 
Konstruktor wohl nicht aufgerufenn wird) nur an einer Stelle vorkommt 
wundert mich.


P.S.: Bei dem Mikrocontroller handelt es sich um einen STM32F103. 
(Cortex-M3)

: Bearbeitet durch User
Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Welchen Compiler verwendest Du?

Wenn Du nicht mit dem g++ (C++) sondern dem gcc (C) Frontend 
compilierst, werden die static initializer nicht aufgerufen.
Wenn Du's tust, wirst Du wahrscheinlich feststellen, daß dein 
Startupcode nicht ausreichend ist.

Autor: Marc (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
gcc ist der Frontend-Treiber. Für C, C++, Java, Ada, Fortran und wer 
weiß, was noch.

Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Welchen Compiler verwendest Du?
>
> Wenn Du nicht mit dem g++ (C++) sondern dem gcc (C) Frontend
> compilierst, werden die static initializer nicht aufgerufen.
> Wenn Du's tust, wirst Du wahrscheinlich feststellen, daß dein
> Startupcode nicht ausreichend ist.

Um ehrlich zu sein: Ich weiß es nicht genau.

Ich denke, ich bin schon langsam so weit, dass mir die Abstraktion die 
mir ein fertiges Buildsystem bietet mehr schadet als nutzt.

Standardmäßig war für alle Dateien in der IDE (= Em::Blocks) als 
"compiler variable" 'CC' angegeben. Für meine C-Files habe ich das so 
gelassen, für die .cpp-Dateien habe ich das jedoch auf 'CPP' geändert.

Über Google habe ich jetzt folgendes gefunden: (Em::Blocks benutzt make 
im Hintergrund.)
https://www.gnu.org/software/make/manual/html_node...

Anscheinend steht 'CC' für den C-Compiler, während 'CPP' für den 
C-Preprozessor steht. Für meine C++-Dateien hätte ich also wohl 'CXX' 
benutzen müssen.

Gesagt getan -- jetzt bekomme ich im Startup Code jedoch tatsächlich 
eine 'undefined reference to `main''-Fehlermeldung! War das das was du 
meintest? Liegt das daran, dass main() jetzt ge-name-mangled wird? Aber 
warum war das dann bis dato nicht der Fall? C++-Code (der auch wirklich 
C++-Features (Klassen) benutzt) war's ja trotzdem.


(Und wie die GCC im Hintergrund funktioniert ist mir ehrlich gesagt ein 
Rätsel: C, CC, CXX, CPP, c++, g++, usw. usf.)

: Bearbeitet durch User
Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manuel W. schrieb:
> Gesagt getan -- jetzt bekomme ich im Startup Code jedoch tatsächlich
> eine 'undefined reference to `main''-Fehlermeldung!

main, _main, oder __main?

Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Manuel W. schrieb:
>> Gesagt getan -- jetzt bekomme ich im Startup Code jedoch tatsächlich
>> eine 'undefined reference to `main''-Fehlermeldung!
>
> main, _main, oder __main?

'main'.

Startup code:
_start:

    /* Zero fill the bss segment. */
    ldr   r1, = __bss_start__
    ldr   r2, = __bss_end__
    movs  r3, #0
    b  .fill_zero_bss
.loop_zero_bss:
    str  r3, [r1], #4

.fill_zero_bss:
    cmp  r1, r2
    bcc  .loop_zero_bss

    /* Jump to our main */
    bl main
    b .
    .size    _start, . - _start

/*    Macro to define default handlers. Default handler
 *    will be weak symbol and just dead loops. They can be
 *    overwritten by other handlers */
blablabla

C++-Code:
int main(void)
{
     blablbala
}

Ein 'extern "C"' (um das name mangling zu deaktivieren) ändert aber auch 
nichts.
extern "C" int main(void)
{
     blablbala
}

: Bearbeitet durch User
Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe jetzt mal testweise folgenden Code ausgeführt, und mir am 
Haltepunkt den Inhalt des Objects angesehen. Ergebnis: 'run_' war 0, dh. 
der Konstruktor wurde nicht ausgeführt.

Sehr seltsam das Ganze.

class A {
public:
        int run_;
        A(int num) {run_ = num;};
};

A test(1);

int main(void)
{
    A.run_ = 5; // break point here
}

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit meinem Hinweis auf g++ als Frontend habe ich wahrscheinlich in die 
falsche Richtung geschickt. Genauso wie mit dem Hinweis auf _main(). Ich 
habe einen älteren (non-elf) Compiler, bei dem das nur so geht, das ist 
aber nicht (mehr) Standard. Sorry dafür.

Bei moderneren gcc-xxx-xxx-elf-Compilern ist es offensichtlich wurscht, 
welches Frontend man benutzt. Trotzdem müssen die static initializers 
vor dem Start von main() (also noch im Startupcode) aufgerufen werden.
Wie das geht, ist - so meine ich - immer noch vom Compiler abhängig.
Der arm-none-eabi-g++ packt die in die .init_array section (die 
natürlich gelinkt werden und deshalb im Linker Skript extra 
berücksichtigt werden muß).

Wenn Du normalerweise mit -nostdlib linkst, mußt Du zusätzlich die 
Funktionszeiger in dieser Section im Startupcode "von Hand" aufrufen 
(sonst würde das die C/C++-Library erledigen):
extern "C"
{
    void static_init(void)
    {
        typedef void (InitFunc)(void);
        extern InitFunc __init_array_start;
        extern InitFunc __init_array_end;

        InitFunc pFunc = &__init_array_start;

        for ( ; pFunc < &__init_array_end; ++pFunc )
        {
            (*pFunc)();
        }
    }
}

Ich hab's nicht komplett selbst durchgespielt, denke aber, daß Du damit 
weiterkommen müsstest. Laß' hören, ob's funktioniert.

Autor: Jim Meba (turboj)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manuel W. schrieb:
> 'main'.
>
> Startup code:

Das ist ein reiner "C" Startup Code. Für die C++ Static Konstruktoren 
muss das etwa so aussehen:
/* Call static constructors */
  bl __libc_init_array
/* Call the application's entry point.*/
  bl main

  b .
Anderenfalls wundert man sich wieso da was nicht tut.

Viele ARM (Cortex M) Startups sehen so aus,
weil vorher noch Low Level Initialisierung nötig ist - wie Clocks oder 
FPU:
/* Call the clock system intitialization function.*/
  bl  SystemInit

/* Call static constructors */
  bl __libc_init_array

/* Call the application's entry point.*/
  bl main

  b .

Autor: Manuel W. (multisync)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Markus F. schrieb:
> Mit meinem Hinweis auf g++ als Frontend habe ich wahrscheinlich in die
> falsche Richtung geschickt. Genauso wie mit dem Hinweis auf _main(). Ich
> habe einen älteren (non-elf) Compiler, bei dem das nur so geht, das ist
> aber nicht (mehr) Standard. Sorry dafür.

Kein Problem! War gut gemeint, und zumindest hatte ich somit Anlass mich 
in die Unterschiede zwischen das gcc- und g++-Frontend einzuarbeiten. 
Das wird sicher nicht umsonst gewesen sein.

~~~

Der Tipp von dir und Jim Meba war goldrichtig!

Ich musste einfach nur ein
bl __libc_init_array
unmittelbar vor den Aufruf meiner main() in meinen Startup-Code 
einfügen, und schon war ich annähernd am Ziel.

Der Linker beschwerte sich dann nur noch wegen eines 'undefined 
reference to `init''. Da das jedoch irgendwas mit mit der Schnittstelle 
zum Host-Betriebssystem zu tun hat, und ich jedoch bare-metal 
programmiere, reichte es aus ihn mit einer Dummy-Definition dieses 
Symbols ruhig zu stellen.
extern "C" { void _init(void){}; }

Es scheint jetzt wirklich alles zu funktionieren! Die Konstruktoren 
werden aufgerufen, und den data membern werden die erwarteten Werte 
zugewiesen.


Vielen lieben Dank (an alle!) für die tatkräftige Unterstützung!


Ich sehe schon, wenn ich von C++ wirklich Ahnung haben will werde mich 
bei nächster Gelegenheit auf die Interna von C++ fokussieren müssen. Für 
mich als Neuling ist es ein Wahnsinn, was da im Hintergrund durch das 
Zusammenspiel der diversen Libraries, Compiler-/Linkerflags usw. 
geschieht...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.