<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de">
	<id>https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=194.208.80.202</id>
	<title>Mikrocontroller.net - Benutzerbeiträge [de]</title>
	<link rel="self" type="application/atom+xml" href="https://www.mikrocontroller.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=194.208.80.202"/>
	<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/articles/Spezial:Beitr%C3%A4ge/194.208.80.202"/>
	<updated>2026-04-10T23:39:47Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.39.7</generator>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=MMC-_und_SD-Karten&amp;diff=52084</id>
		<title>MMC- und SD-Karten</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=MMC-_und_SD-Karten&amp;diff=52084"/>
		<updated>2010-10-23T09:32:36Z</updated>

		<summary type="html">&lt;p&gt;194.208.80.202: /* Bibliotheken zur Ansteuerung */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;MMC- und SD-Speicherkarten lassen sich im [[SPI]]-Modus relativ einfach mit einem Mikrocontroller ansteuern. Prinzipiell git es zwischen SD-Card und MMC nicht viele Unterschiede, allerdings sind SD-Karten weiter verbreitet, in der Regel schneller als MMCs, und haben eine besser implementiertes SPI-Interface. Es existieren diverse Varianten (miniSD, microSD) die zur normalen SD-Card kompatibel sind.&lt;br /&gt;
&lt;br /&gt;
Die Karte liest das anliegende Datenbit mit der steigenden Taktflanke ein, als SPI-Modi eignen sich somit Mode 0 (CPOL=0, CPHA=0) und Mode 3 (CPOL=1, CPHA=1) (siehe auch [[Serial Peripheral Interface]]). Bei MMCs ist der SPI-Modus nicht genau spezifiziert, somit kommt es durchaus mal vor dass der SPI-Modus je nach Karte unterschiedlich gewählt werden muss, oder dass die Karte überhaupt nicht zuverlässig funktioniert (siehe [http://www.mikrocontroller.net/forum/read-1-343528.html Beitrag im Forum]).&lt;br /&gt;
&lt;br /&gt;
== DOs und DON&#039;Ts bei der Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;Lasst euch nicht verrückt machen&#039;&#039;&#039; wenn es einfach nicht funktioniert, sondern probiert zu allererst mal eine SD-Karte eines anderen Herstellers aus. Die MMC-Implementierung für AVR von Elm Chan z.&amp;amp;nbsp;B. funktioniert mit SanDisk problemlos hat aber mit Platinum Karten ein Problem.&lt;br /&gt;
* Taktfrequenz bei der Initialisierung nicht höher als 400kHz&lt;br /&gt;
* Ein Pullup-Widerstand am Ausgang der MMC/SD Karte (DO) ist für eine saubere Initialisierung per SPI notwendig! [http://www.mikrocontroller.net/topic/112421#1001693 (Thread mit Erklärung dazu)]&lt;br /&gt;
* Saubere Versorgung: Kein Dioden-Pfusch, mit dem eine vorhandene 5V Versorgung mittels in Reihe geschalteter Dioden auf irgendwas im Bereich 3V &amp;quot;geregelt&amp;quot; wird. Stattdessen einen guten 3,3V-Regler verwenden. Die Karte mag es nicht, wenn mehr als 60mV Ripple auf Vcc ist. LM317 oder LM1117-ADC/-3.3 mit entspechenden Kondensatoren reicht zumindest bei Basteleien allemal.&lt;br /&gt;
* Sauberer Anschluss der Digitalschnittstelle: Spannungsteiler &amp;quot;verschleifen&amp;quot; die Signale bei hohen Frequenzen und die Übertragungsrate muss dann begrenzt werden. Also entweder ein [http://www.mikrocontroller.net/articles/Pegelwandler Pegelwandler] oder gleich an ein 3,3V I/O anschließen.&lt;br /&gt;
* Ein Pullup-Widerstand an der Select-Leitung (/CS) schadet nicht und stellt sicher, dass die Karte erst mit Absicht selektiert wird.&lt;br /&gt;
* Nachdem die Karte deselektiert wurde (/CS auf high), die Taktleitung noch einige Male pulsen, damit die Karte DO hochohmig/tri-state schaltet (vgl. Chans Erläuterungen).&lt;br /&gt;
* Die Karten verfügen weder über einen Reset- noch einen Sleep-Anschluss. Moderne Karten reduzieren bei Nichtbenutzung ihren Stromverbrauch, einen vollständigen Reset kann man jedoch nicht per Software auslösen. Daher sollten die Karten per P-Channel-FET oder Spannungsregler/-wandler mit Enable-Funktion so angeschlossen werden, dass über Versorgung an/aus ein (Power-On-)Reset ausgelöst werden kann. Dabei darauf achten, dass vorhandene Pull-Up-Widerstände bei abgeschalteter Versorgung ebenfalls deaktiviert werden (vgl. z.&amp;amp;nbsp;B. Schaltplan für den Anschluss von SD-Card/MMC per SPI an AVR in Chans Beispielen. Link unten).&lt;br /&gt;
* Guter Kontakt im Steckplatz, sehr gut eignen sich mit der Zange verbogene Stiftleisten, oft sieht es aus als ob es &amp;quot;passt&amp;quot;, aber es gibt doch keinen Kontakt, daher bei Fehlern: Immer Durchmessen! Auch zu erwähnen wären da alte ISA-Bus Buchsen, die auf jedem alten PC Mainboard drauf sind. Um sicher zu gehen, dass der Kontakt wirklich gut ist, sollte man aber trotzdem SD-Slots benutzen. Diese bekommt man u.a. bei CSD (günstig), Reichelt (teuer) oder aus alten Kartenlesern.&lt;br /&gt;
* Guter Kontakt #2: Was sich im übrigen auch sehr gut eignet sind Adapter von MiniSD auf normales SD-Format, um dann MiniSD zu benutzen. Wenn man eine Stiftleiste im 2.54mm-Format oder Lötnägel im selben Format auf der Platine hat, kann man daran wunderbar den SD-Kartenadapter anlöten. Das ist mechanisch recht stabil. Ein kleines Manko ist allerdings, daß dann eine Gold-Lötzinn-Legierung durch die vergoldeten Kontakte entsteht und das soll ja dem Lötzinn langfristig nicht sehr zuträglich sein. Aber für&#039;s Hobby funktioniert das wunderbar.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||[[Bild:SD Steck Stift.jpg]]&amp;lt;br/&amp;gt;&lt;br /&gt;
&amp;lt;sup&amp;gt;(Liefere Bild in besserer Qualität nach!)&amp;lt;/sup&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Bibliotheken zur Ansteuerung ==&lt;br /&gt;
&lt;br /&gt;
* [http://elm-chan.org/fsw/ff/00index_e.html ELM ChaN FatFs]  FAT(12,16,32)-Dateisystem. Klein und übersichtlich, hochoptimiert, frei auch für kommerzielle Anwendung. Beispiele für AVR, H8, LPC2k mit MCI u.a. enthalten (&amp;quot;samples&amp;quot;), neuere Versionen mit LFN-Unterstützung. Beispiel für AT91SAM7 inkl. DMA im Projekt [[ARM MP3/AAC Player]]. (siehe auch [http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/arm_memcards/ M. Thomas&#039; ARM+SD/MMC Seite])&lt;br /&gt;
&lt;br /&gt;
* [http://elm-chan.org/fsw/ff/00index_p.html ELM ChaN Petit FatFS] FAT(12,16,32)-Dateisystem. Sehr klein. Beispiele für AVR.&lt;br /&gt;
&lt;br /&gt;
* [http://sourceforge.net/projects/efsl EFSL] FAT16/32-Dateisystem, unterstützt Partitionen und Superfloppys, Beispielcode für AVR, LPC2000 und AT91SAM7 enthalten (siehe auch [http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/arm_memcards/ M. Thomas&#039; ARM+SD/MMC Seite])&lt;br /&gt;
&lt;br /&gt;
* [http://www.holger-klabunde.de/avr/avrboard.htm#cf Holger Klabundes FAT16/32] mit Beispielen für AVR MMC/SD und CF, LPC2k mit SPI&lt;br /&gt;
&lt;br /&gt;
* libfat aus dem [http://sourceforge.net/projects/devkitpro devkitpro-Projekt] u.a. LFN-Unterstützung.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikro-control.de/Joomla/index.php?option=com_content&amp;amp;task=view&amp;amp;id=18&amp;amp;Itemid=30 SD-Logger] - FAT 16, für den privaten Einsatz kostenfrei&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/read-4-125350.html MMC-Ansteuerung mit FAT16 von Ulrich Radig]&lt;br /&gt;
&lt;br /&gt;
* [http://www.zws.com/products/dosfs/index.html DOSFS Free FAT12/FAT16/FAT32 Filesystem] &amp;quot;DOSFS is a free FAT-compatible filesystem intended for fairly low-end embedded applications. Intended target systems would be in the ballpark of 1K RAM, 4K ROM or more&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/topic/48481 MMC/SD-Karte mit FAT16 an AVR] von Roland Riegel&lt;br /&gt;
&lt;br /&gt;
* [ftp://ftp.circuitcellar.com/pub/Circuit_Cellar/2005/176/Sham176.zip Circuit Cellar FAT16 MMC/SD] mit MMC/SD Hardwaretreiber für MSP430&lt;br /&gt;
&lt;br /&gt;
* [http://www.analog.com &amp;quot;Implementing FAT32 File Systems on ADSP-BF533 Blackfin Processors&amp;quot;] Application Note und Code von ADI&lt;br /&gt;
&lt;br /&gt;
* [http://www.atmel.com/dyn/products/tools.asp?family_id=682 FAT-Code in Atmel&#039;s AVR32 UC3 Software-Library]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/articles/FAT32 AVR FAT16/32] Bibliothek mit Wiki. Unterstützt LFN und Ordner Rekursiv löschen. SVN für neuste Version.&lt;br /&gt;
&lt;br /&gt;
* [http://www.embedded-os.de/index.html?pcfat_port.htm pC/FAT driver] &amp;quot;using SPI for sector read/write to MMC/MMCplus/HD-MMC/M-Bridge/SD/SDHC-cards on different platforms&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* [http://www.dharmanitech.com/2009/01/sd-card-interfacing-with-atmega8-fat32.html SD Card Interfacing with ATmega8] (FAT32 implementation) by CC Dharmani&lt;br /&gt;
&lt;br /&gt;
* [[AVR FAT32]]&lt;br /&gt;
&lt;br /&gt;
== Allgemeine Informationen ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.digitalspirit.org/file/index.php/obj/docs/sd/ Datenblätter] ( [http://www.digitalspirit.org/file/index.php/obj-download/docs/sd/ProductManualSDCardv2.2final.pdf SD Card Product Manual 2.2] )&lt;br /&gt;
&lt;br /&gt;
* [http://elm-chan.org/docs/mmc/mmc_e.html ELM ChaN - How to Use an MMC]&lt;br /&gt;
&lt;br /&gt;
* [http://elm-chan.org/fsw/ff/00index_e.html ELM ChaN - MMC/SD Benchmarks]&lt;br /&gt;
&lt;br /&gt;
* [http://www.mikrocontroller.net/forum/list-1-1.html?filter=MMC*+SD Beiträge zum Thema MMC/SD im Forum (ca. 200 Threads)]&lt;br /&gt;
&lt;br /&gt;
* [http://www.shop.display3000.com/pi8/pi14/pd102.html Weiter unten im Text (runterscrollen) gibt es interessante Oszi-Bilder zu den oft genannten Spannungsteilern oder Transistorlösungen als Pegelwandler]&lt;br /&gt;
&lt;br /&gt;
* [http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&amp;amp;nodeId=2680&amp;amp;dDocName=en547784 Microchip Memory Disk Drive System mit FAT32 und SDHC Unterstützung]&lt;br /&gt;
&lt;br /&gt;
* [http://www.ifas.htwk-leipzig.de/easytoweb/download/D&amp;amp;E_11_2006_Anbindung_von_SD-Karten.pdf Gute Beschreibung der SDIO-Architektur und wie man eine SD-Karte mit ARM bzw. AVR benutzt]&lt;br /&gt;
&lt;br /&gt;
== Bauteile ==&lt;br /&gt;
&lt;br /&gt;
* [http://www.instructables.com/id/EZD6Q18LYGES1762SJ/ DIY SD-Card Fassung aus einem alten Floppy Kabel]&lt;br /&gt;
&lt;br /&gt;
* [http://www.watterott.com/index.php?page=search&amp;amp;page_action=query&amp;amp;desc=on&amp;amp;sdesc=on&amp;amp;keywords=multicomp&amp;amp;x=0&amp;amp;y=0 Halter für SD-Karten] (Shop www.watterott.com)&lt;br /&gt;
&lt;br /&gt;
== Projekte ==&lt;br /&gt;
&lt;br /&gt;
* [http://retromaster.wordpress.com/ufe/ Ultimate Floppy Emulator] von Retromaster (PIC32 @ 80 Mhz, 16 Mb SDRAM)&lt;br /&gt;
&lt;br /&gt;
[[Category:Bauteile]]&lt;br /&gt;
[[Category:Datenübertragung]]&lt;br /&gt;
[[Kategorie:Speicher und Dateisysteme]]&lt;/div&gt;</summary>
		<author><name>194.208.80.202</name></author>
	</entry>
	<entry>
		<id>https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=46949</id>
		<title>FAQ</title>
		<link rel="alternate" type="text/html" href="https://www.mikrocontroller.net/index.php?title=FAQ&amp;diff=46949"/>
		<updated>2010-05-01T20:44:18Z</updated>

		<summary type="html">&lt;p&gt;194.208.80.202: /* Wie funktioniert String-Verarbeitung in C? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ein Verzeichnis von im Forum oft gestellten und immer wieder beantworteten Fragen und den zugehörigen Antworten:&lt;br /&gt;
&lt;br /&gt;
=Wie kann ich Zahlen auf [[LCD]]/[[UART]] ausgeben?=&lt;br /&gt;
&lt;br /&gt;
Aber die Bibliothek, die du benutzt, stellt nur eine Funktion zur Verfügung, mit der man einen String ausgeben kann... Was tun? &lt;br /&gt;
&lt;br /&gt;
In den folgenden Beispielen wird eine selbstgeschriebene Funktion zur Stringausgabe auf LCD - die Funktion lcd_string() - aus dem [[AVR-GCC-Tutorial/LCD-Ansteuerung|LCD-Teil des AVR-GCC-Tutorials]] verwendet:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
lcd_string( &amp;quot;Hallo Welt&amp;quot; );  // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Um also eine Zahl (numerische Konstante oder Variableninhalt) auszugeben, muss von dieser Zahl zunächst ihre String-Repräsentation ermittelt werden. Hier geht es aber nur darum, zu zeigen wie man diese String Repräsenation erzeugen kann. Was man dann mit diesem String weiter macht, ob das dann eine LCD-Ausgabe oder eine UART-Übertragung oder das Abspeichern auf SD-Karte oder ... ist, spielt eine untergeordnete Rolle.&lt;br /&gt;
&lt;br /&gt;
Es gibt mehrere Möglichkeiten, sich die Stringrepräsentation zu erzeugen:&lt;br /&gt;
&lt;br /&gt;
===itoa()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; ist keine C-Standardfunktion (wohl aber ihre Umkehrung &amp;lt;b&amp;gt;atoi()&amp;lt;/b&amp;gt; ). Auf manchen Compilern heisst diese Funktion dann folgerichtig &amp;lt;b&amp;gt;_itoa()&amp;lt;/b&amp;gt;, wobei der führende _ eben anzeigt, dass es sich um eine Erweiterung des C-Standards handelt. Bei [[WinAVR]] ist itoa() Bestandteil der mitgelieferten Library avr-libc, in der Libary [http://www.nongnu.org/avr-libc/user-manual/group__avr__stdlib.html&#039;&#039;stdlib.h&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  #include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  itoa( i, Buffer, 10 );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;itoa( i, Buffer, 10 );&amp;lt;/b&amp;gt; - Die Zahl i wird nach ASCII gewandelt und die String Repräsentierung davon wird in Buffer abgelegt. Die Basis, in der diese Wandlung erfolgt, ist das 10-er System. Wird das dritte Argument von 10 in zb. 2 oder auch 16 abgewandelt, erhält man die binäre oder eben eine hexadezimale Repräsentierung des Wertes. Auch wenn 10, 2 und 16 die häufigsten Angaben an dieser Stelle sind, kann itoa aber grundsätzlich in jedes beliebige Zahlensystem wandlen.&lt;br /&gt;
&lt;br /&gt;
Wichtig ist, darauf zu achten, dass das Array &amp;lt;i&amp;gt;Buffer&amp;lt;/i&amp;gt; groß genug dimensioniert wird, um alle Zeichen der Textrepräsentation der Zahl aufzunehmen - inklusive der 0, die den String abschließt, sowie ein mögliches Vorzeichen.&lt;br /&gt;
&lt;br /&gt;
Anzumerken bleibt weiter, dass es normalerweise für alle Datentypen entsprechende Umwandlungsfunktionen gibt, wenn es sie für einen Datentyp gibt. Die Namensgebung lehnt sich an das Schema an: &#039;&#039;Kürzel_für_den_Datentyp to a&#039;&#039;. Eine Funktion die einen unsigned int wandelt, heißt dann utoa (oder _utoa), Floating Point heißt dann ftoa (oder _ftoa), etc.&lt;br /&gt;
&lt;br /&gt;
===sprintf()===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;%d&amp;quot;, i );&lt;br /&gt;
  lcd_string( Buffer ); // ggf. auch lcd_out() o.ä. in anderen Libraries&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Diese Methode funktioniert auch bei long oder float Werten. Unbedingt beachtet werden muss allerdings, dass die Typkennzeichnungen im sog. Format-String (hier &amp;quot;%d&amp;quot;) mit den tatsächlichen Typen der auszugebenden Werten übereinstimmt. Und dass der Buffer, der den Text aufnimmt, auch groß genug dimensioniert wird. Dabei sollte die 0, die den String terminiert, nicht vergessen werden.&lt;br /&gt;
&lt;br /&gt;
Mit sprintf() hat man dieselben Möglichkeiten zur Formatierung wie bei &amp;lt;b&amp;gt;printf()&amp;lt;/b&amp;gt; (siehe unten). Insbesondere gibt es natürlich die Möglichkeit die Zahl gleich in einen umgebenden Text einzubetten bzw. Formatierungen anzugeben:&lt;br /&gt;
  &lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  char Buffer[20];&lt;br /&gt;
  int i = 25;&lt;br /&gt;
&lt;br /&gt;
  sprintf( Buffer, &amp;quot;Anzahl: %d Stueck&amp;quot;, i );&lt;br /&gt;
  lcd_out( Buffer );&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Der &amp;quot;Haken&amp;quot; an der mächtigen Funktion sprintf() ist, daß sie auch bei minimalisierter Konfiguration verhältnismäßig viel Programmspeicher (Flash-ROM) belegt und relativ viel Prozesszeit benötigt. Daher sollte man sprintf() nur verwenden, wenn kein Speicher- und Prozesszeitmangel besteht. Sonst sollte itoa() oder eine eigene, auf die Bedürfnisse optimierte Implementierung auf jeden Fall vorgezogen werden.&lt;br /&gt;
&lt;br /&gt;
====Formatierungen mit printf====&lt;br /&gt;
&lt;br /&gt;
Für jedes auszugebende Argument muss es im Formatstring einen entsprechenden Formatbezeichner geben. Der Aufbau eines Formatbezeichners ist immer&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;%[Modifizierer][Feldbreite][.Präzision]Typ&amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;Typ&amp;lt;/b&amp;gt; ist dabei eine Kennung, der mit dem Datentyp des jeweiligen auszugebenden Argumentes übereinstimmen muss. Einige oft benutzte Kennungen, ohne Anspruch auf Vollständigkeit, sind:&lt;br /&gt;
  &amp;lt;b&amp;gt;c&amp;lt;/b&amp;gt;    char&lt;br /&gt;
  &amp;lt;b&amp;gt;d&amp;lt;/b&amp;gt;    int&lt;br /&gt;
  &amp;lt;b&amp;gt;f&amp;lt;/b&amp;gt;    float, double&lt;br /&gt;
  &amp;lt;b&amp;gt;ld&amp;lt;/b&amp;gt;   long&lt;br /&gt;
  &amp;lt;b&amp;gt;u&amp;lt;/b&amp;gt;    unsigned int&lt;br /&gt;
  &amp;lt;b&amp;gt;lu&amp;lt;/b&amp;gt;   unsigned long&lt;br /&gt;
  &amp;lt;b&amp;gt;p&amp;lt;/b&amp;gt;    pointer&lt;br /&gt;
  &amp;lt;b&amp;gt;s&amp;lt;/b&amp;gt;    string&lt;br /&gt;
  &amp;lt;b&amp;gt;x&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt aber als Hexadezimalzahl&lt;br /&gt;
  &amp;lt;b&amp;gt;X&amp;lt;/b&amp;gt;    ein int wird ausgegeben, die Ausgabe erfolgt aber als Hexadezimalzahl, wobei Grossbuchstaben verwendet werden.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;b&amp;gt;Feldbreite&amp;lt;/b&amp;gt; gibt die Breite des Ausgabefeldes an, in die die Ausgabe durchgeführt werden soll. Reicht die angegebene Feldbreite nicht aus, so vergrößert printf diese Breite eigenmächtig. Die Feldbreite muß nicht angegeben werden. In diesem Fall bestimmt printf selbst die Feldbreite, so dass die Ausgabe darin Platz findet. Mit der Feldbreite hat man eine simple Möglichkeit dafür zu sorgen, dass der erzeugte String immer eine konstante Länge hat, selbst wenn die auszugebende Zahl diese Länge gar nicht benötigen würde (wichtig zb. bei Tabellen).&lt;br /&gt;
&lt;br /&gt;
Der &amp;lt;b&amp;gt;Modifizierer&amp;lt;/b&amp;gt; bestimmt, wie und womit nicht benutzte Felder des Ausgabefeldes gefüllt werden sollen, wie die Ausrichtung innerhalb des Feldes erfolgen soll und ob ein Vorzeichen auch dann ausgegeben werden soll wenn die auszugebende Zahl positiv ist. Wird kein Modifizierer angegeben, so werden nicht benutzte Felder mit einem Leerzeichen gefüllt, positive Vorzeichen unterdrückt und die Ausgabe im Feld rechts ausgerichtet.&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;b&amp;gt;+&amp;lt;/b&amp;gt;    Vorzeichen wird immer ausgegeben&lt;br /&gt;
  &amp;lt;b&amp;gt;-&amp;lt;/b&amp;gt;    Die Ausgabe wird im Ausgabefeld linksbündig ausgerichtet&lt;br /&gt;
  &amp;lt;b&amp;gt;0&amp;lt;/b&amp;gt;    anstelle von Leerzeichen werden führende 0-en ausgegeben&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;b&amp;gt;Präzision&amp;lt;/b&amp;gt; kommt nur bei float oder double Zahlen zum Einsatz. Sie legt fest, wieviele Positionen der kompletten Feldbreite für die Ausgabe von Nachkommastellen reserviert werden sollen. Auch sie muss wiederrum nicht angegeben werden und printf benutzt in so einem Fall Standardvorgaben.&lt;br /&gt;
&lt;br /&gt;
===== Beispiele =====&lt;br /&gt;
* &amp;lt;b&amp;gt;&amp;quot;%d&amp;quot;&amp;lt;/b&amp;gt; Ausgabe eines Integer&lt;br /&gt;
* &amp;lt;b&amp;gt;&amp;quot;%5d&amp;quot;&amp;lt;/b&amp;gt; Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite&lt;br /&gt;
* &amp;lt;b&amp;gt;&amp;quot;%05d&amp;quot;&amp;lt;/b&amp;gt; Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite, wobei das Feld links mit führenden Nullen auf 5 Zeichen aufgefüllt wird&lt;br /&gt;
* &amp;lt;b&amp;gt;&amp;quot;%-5d&amp;quot;&amp;lt;/b&amp;gt; Ausgabe eines Integer in einem Feld mit 5 Zeichen Breite. Die Zahl wird linksbündig in das Feld gestellt.&lt;br /&gt;
* &amp;lt;b&amp;gt;&amp;quot;%6.3f&amp;quot;&amp;lt;/b&amp;gt; Ausgabe eines float (oder double). DIe Ausgabe erfolgt in einem Feld mit 6 Zeichen Breite, wobei 3 Nachkommastellen ausgegeben werden. Achtung: In der Feldbreite ist auch ein eventuelles Vorzeichen sowie der Dezimalpunkt enthalten. Bei einer Feldbreite von 6 Zeichen und 3 Nachkommastellen, bleiben bei einer positiven Zahl daher nur 2 Positionen für den Vorkommaanteil, bei negativen sogar nur 1 Stelle ( 6 - 3 Nachkommastellen - 1 Dezimalpunkt - 1 Vorzeichen == 1 )&lt;br /&gt;
&lt;br /&gt;
===Eigene Umwandlungsfunktionen===&lt;br /&gt;
&lt;br /&gt;
Möchte man &amp;lt;b&amp;gt;itoa()&amp;lt;/b&amp;gt; nicht benutzen oder hat es gar auf seinem System nicht zur Verfügung, dann ist es auch nicht schwer, sich selbst eine Funktion dafür zu schreiben:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void ItoA( int z, char* Buffer )&lt;br /&gt;
{&lt;br /&gt;
  int i = 0;&lt;br /&gt;
  int j;&lt;br /&gt;
  char tmp;&lt;br /&gt;
  unsigned u;    // In u bearbeiten wir den Absolutbetrag von z.&lt;br /&gt;
  &lt;br /&gt;
    // ist die Zahl negativ?&lt;br /&gt;
    // gleich mal ein - hinterlassen und die Zahl positiv machen&lt;br /&gt;
    if( z &amp;lt; 0 ) {&lt;br /&gt;
      Buffer[0] = &#039;-&#039;;&lt;br /&gt;
      Buffer++;&lt;br /&gt;
      // -INT_MIN ist idR. größer als INT_MAX und nicht mehr &lt;br /&gt;
      // als int darstellbar! Man muss daher bei der Bildung &lt;br /&gt;
      // des Absolutbetrages aufpassen.&lt;br /&gt;
      u = ( (unsigned)-(z+1) ) + 1; &lt;br /&gt;
    }&lt;br /&gt;
    else { &lt;br /&gt;
      u = (unsigned)z;&lt;br /&gt;
    }&lt;br /&gt;
    // die einzelnen Stellen der Zahl berechnen&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&lt;br /&gt;
    // den String in sich spiegeln&lt;br /&gt;
    for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
      tmp = Buffer[j];&lt;br /&gt;
      Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
      Buffer[i-j-1] = tmp;&lt;br /&gt;
    }&lt;br /&gt;
    Buffer[i] = &#039;\0&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip ist einfach:&amp;lt;br&amp;gt;&lt;br /&gt;
Die Ermittlung der einzelnen Stellen erfolgt in der zentralen Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    do {&lt;br /&gt;
      Buffer[i++] = &#039;0&#039; + u % 10;&lt;br /&gt;
      u /= 10;&lt;br /&gt;
    } while( u &amp;gt; 0 );&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
durch fortgesetzte Division durch 10 und Restbildung.&lt;br /&gt;
&lt;br /&gt;
    8392&lt;br /&gt;
&lt;br /&gt;
    8392 % 10           -&amp;gt; &amp;lt;b&amp;gt;2&amp;lt;/b&amp;gt;&lt;br /&gt;
    8392 / 10  -&amp;gt; 839&lt;br /&gt;
&lt;br /&gt;
     839 % 10           -&amp;gt; &amp;lt;b&amp;gt;9&amp;lt;/b&amp;gt;&lt;br /&gt;
     839 / 10  -&amp;gt; 83&lt;br /&gt;
&lt;br /&gt;
      83 % 10           -&amp;gt; &amp;lt;b&amp;gt;3&amp;lt;/b&amp;gt;&lt;br /&gt;
      83 / 10  -&amp;gt; 8&lt;br /&gt;
&lt;br /&gt;
       8 % 10           -&amp;gt; &amp;lt;b&amp;gt;8&amp;lt;/b&amp;gt;&lt;br /&gt;
       8 / 10  -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nur leider erhält man dadurch die einzelnen Ziffern der Zahl in umgekehrter Reihenfolge im String (&#039;2&#039; &#039;9&#039; &#039;3&#039; &#039;8&#039; anstelle von &#039;8&#039; &#039;3&#039; &#039;9&#039; &#039;2&#039;). Dies ist aber kein Problem, die nachfolgende Schleife&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  for( j = 0; j &amp;lt; i / 2; ++j ) {&lt;br /&gt;
    tmp = Buffer[j];&lt;br /&gt;
    Buffer[j] = Buffer[i-j-1];&lt;br /&gt;
    Buffer[i-j-1] = tmp;&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
spiegelt den String in sich, sodass danach der String eine korrekte Repräsentation der ursprünglichen Zahl darstellt. Der Funktionsteil vor der &#039;Zerlegeschleife&#039; behandelt den Sonderfall daß die Zahl negativ ist. Negative Zahlen werden behandelt indem im Endergebnis ein &#039;-&#039; vermerkt wird und danach die Zahl positiv gemacht wird.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Siehe auch:&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/67405#541885 Integer-Zahl in String mit bestimmter Zeichenlänge]&lt;br /&gt;
* Forenbeitrag [http://www.mikrocontroller.net/topic/84005#704736 (Resourcenschonend) Wert einer Variable am LCD ausgeben] von Niels Hüsken &lt;br /&gt;
* [[Festkommaarithmetik]]&lt;br /&gt;
&lt;br /&gt;
=Datentypen in Operationen=&lt;br /&gt;
Ein häufiges Problem betrifft die Auswertung von Ausdrücken. Konkret die Frage nach den beteiligten Datentypen.&lt;br /&gt;
zb&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
int j, k;&lt;br /&gt;
&lt;br /&gt;
i = j / k;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Die Frage lautet dann: Warum erhalte ich keine Kommastellen, ich weise doch das Ergebnis einem double zu?&lt;br /&gt;
&lt;br /&gt;
Dazu ist zu sagen, dass C nicht so funktioniert. Die Tatsache dass i eine double Variable ist, ist für die Auswahl der Operation, welche die Division durchführt, völlig irrelevant. C orientiert sich ausschliesslich an den &lt;br /&gt;
Datentypen der beteiligten Operanden, um zu entscheiden ob die Division als Integer- oder als Gleitkommadivision durchzuführen ist. Und da sowohl j als auch k ein Integer sind, wird die Division als Integerdivision durchgeführt&lt;br /&gt;
unabhängig davon, was mit dem Ergebnis weiter passiert. Erst nach der Division wird das Ergebnis in einen double überführt, um es an i zuweisen zu können. Zu diesem Zeitpunkt gibt es aber keine Kommastellen mehr, eine Integerdivision erzeugt keine. Und damit tauchen klarerweise auch im Ergebnis keine auf.&lt;br /&gt;
&lt;br /&gt;
Aus genau diesem Grund ist zb das Ergebnis von&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
double i;&lt;br /&gt;
i = 5 / 8;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
eine glatte 0 und nicht 0.625. Die Division 5 / 8 wird als Integer Division gemacht und liefert als solche keine Nachkommastellen.&lt;br /&gt;
&lt;br /&gt;
Will man den Compiler dazu zwingen, die Division als Gleitkommadivision durchzuführen, so muss man daher dafür sorgen, dass mindestens einer der beteiligten Operanden ein double Wert ist. Dann bleibt dem Compiler nichts anderes übrig, als auch den zweiten Operanden, so er nicht sowieso schon ein double Wert ist, ebenfalls zu einem double zu machen und die Operation als Gleitkommaoperation durchzuführen.&lt;br /&gt;
&lt;br /&gt;
Generell implementiert der Compiler eine Operation immer im &#039;höchsten&#039; Datentyp, der in einer Operation vorkommt. Operanden in einem &#039;niedrigeren&#039; Datentyp werden automatisch immer in diesen &#039;höchsten&#039; Datentyp umgewandelt, zumindest aber int. Die Reihung orientiert sich dabei an der Regel: Ein &#039;höherer&#039; Datentyp kann alle Werte eines &#039;niedrigeren&#039; Datentyps aufnehmen.&lt;br /&gt;
&lt;br /&gt;
    int&lt;br /&gt;
    unsigned int&lt;br /&gt;
    long&lt;br /&gt;
    unsigned long&lt;br /&gt;
    long long&lt;br /&gt;
    unsigned long long&lt;br /&gt;
    double&lt;br /&gt;
    &lt;br /&gt;
float kommt in dieser Tabelle gar nicht vor, da Gleitkommaoperationen grundsätzlich immer als double-Operationen durchgeführt werden.&lt;br /&gt;
&lt;br /&gt;
Hat man also in einer Operation 2 Operanden der Datentypen int und long, so wird der int implizit zu einem long gemacht und die Operation als long Operation durchgeführt. Das Ergebnis hat dann den Datentyp long.&lt;br /&gt;
&lt;br /&gt;
==Welche Datentypen haben Konstante?==&lt;br /&gt;
&lt;br /&gt;
Auch Zahlenkonstante besitzen einen Datentyp, der selbstverständlich vom Compiler bei der Auswahl der Operation berücksichtigt wird. Hier gilt die Regel: Benutzt wird der Datentyp, der die Zahl gerade noch aufnehmen kann. Eine Zahlenkonstante 5 hat daher den Datentyp int. Die Zahl 32767 passt gerade noch in einen int, und hat daher den Datentyp int. 32768 ist für einen int bereits zu groß und hat daher den Datentyp long. 5.0 ist hingegem immer eine double-Konstante. Der Dezimalpunkt erzwingt dieses.&lt;br /&gt;
&lt;br /&gt;
Eine kleine Feinheit gibt es noch zu beachten. Konstanten in dezimaler Schreibweise haben immer einen signed Datentyp, während Konstante in hexadezimaler bzw. oktaler Schreibweise immer einen unsigned Datentyp haben.&lt;br /&gt;
&lt;br /&gt;
Möchte man einer Konstanten einen bestimmten Datentyp aufzwingen, so gibt es dazu 2 (ein halb) Möglichkeiten:&lt;br /&gt;
* Entweder man castet die Konstante in den gewünschten Datentyp&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
   (long)5&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
* oder man benutzt die in C dafür vorgesehene Schreibweise, indem man der Konstanten einen Suffix anhängt, der den gewünschten Datentyp beschreibt&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    5L&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Beides ergibt eine dezimale 5, die vom Datentyp long ist.&lt;br /&gt;
&lt;br /&gt;
* eine Zahl in mit einem Dezimalkomma&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    5.0&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
ist immer eine double Zahl. Es sei denn sie hat explizit eien Suffix&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
    5.0F&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
dann hat man es mit einer float Zahl zu tun.&lt;br /&gt;
&lt;br /&gt;
Die gültigen Suffixe für Zahlen sind U, L und F:&lt;br /&gt;
&lt;br /&gt;
* U wie unsigned; dabei wird zuerst int angenommen. Es erfolgt eine automatische Ausweitung auf long, wenn die Zahl den Wertebereich eines unsigned int überschreitet. &lt;br /&gt;
* L wie long; die Zahl selbst kann int oder double sein.&lt;br /&gt;
* F wie float.&lt;br /&gt;
&lt;br /&gt;
=Aktivieren der Floating Point Version von sprintf beim WinAVR mit AVR-Studio=&lt;br /&gt;
Beim WinAVR/AVR-Studio wird standardmässig eine Version der printf-Bibliothek verwendet, die keine Floating Point Verarbeitung unterstützt. Die meisten Programme benötigen keine Floating Point Unterstützung, sodass hier wertvoller Programmspeicherplatz gespart werden kann.&lt;br /&gt;
&lt;br /&gt;
Benutzt man dann allerdings eine printf Variante für die Ausgabe von Floating Point Zahlen, so erscheint an Stelle der korrekt formatierten Zahl lediglich ein &#039;?&#039;. Dies ist ein Indiz, dass die Floating Point Verarbeitung im Projekt aktiviert werden muss.&lt;br /&gt;
&lt;br /&gt;
Um die Floating Point Verarbeitung zu aktivieren, geht man im AVR-Studio wie folgt vor:&lt;br /&gt;
Menüpunkt: &amp;quot;&amp;lt;b&amp;gt;Project&amp;lt;/b&amp;gt;&amp;quot;/&amp;quot;&amp;lt;b&amp;gt;Configuration Options&amp;lt;/b&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Im sich öffnenden Dialog wird in der linken Navigationsleiste der Eintrag &amp;quot;&amp;lt;b&amp;gt;Libraries&amp;lt;/b&amp;gt;&amp;quot; ausgewählt.&lt;br /&gt;
Unter &#039;&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;Available Link Objects&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt;&#039; werden alle möglichen Bibliotheken angeboten. Für die Aktivierung der Floating Point Unterstützung sind 2 interessant: &lt;br /&gt;
* &amp;lt;b&amp;gt;libprintf_flt.a&amp;lt;/b&amp;gt;&lt;br /&gt;
* &amp;lt;b&amp;gt;libm.a&amp;lt;/b&amp;gt;&lt;br /&gt;
Beide Bibliotheken werden durch aktivieren und einen Druck auf &amp;quot;&amp;lt;b&amp;gt;Add Library --&amp;gt;&amp;lt;/b&amp;gt;&amp;quot; in die rechte Spalte übernommen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;libc.a&amp;lt;/b&amp;gt; sollte übrigens &amp;lt;u&amp;gt;nicht&amp;lt;/u&amp;gt; zu den Bibliotheken hinzugefügt werden da sie automatisch eingebunden wird. Etwas Hintergrundinformationen gibt es in [http://www.mikrocontroller.net/topic/173630 diesem Thread.]&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_Studio_float1.gif]]&lt;br /&gt;
&lt;br /&gt;
Danach wählt man in der Navigationsleiste den Eintrag &amp;quot;&amp;lt;b&amp;gt;Custom Options&amp;lt;/b&amp;gt;&amp;quot;. Unter &#039;&amp;lt;b&amp;gt;&amp;lt;i&amp;gt;Custom Compilation Options&amp;lt;/i&amp;gt;&amp;lt;/b&amp;gt;&#039; wird &#039;&amp;lt;i&amp;gt;[Linker Options]&amp;lt;/i&amp;gt;&#039; ausgewählt und in das Textfeld rechts/unten wird der Text&lt;br /&gt;
&amp;lt;b&amp;gt;-Wl,-u,vfprintf&amp;lt;/b&amp;gt;&lt;br /&gt;
eingegeben. Ein Druck auf &amp;quot;&amp;lt;b&amp;gt;Add&amp;lt;/b&amp;gt;&amp;quot; befördert die Zeile in das Listenfeld darüber, welches die Kommandos an den Linker enthält.&lt;br /&gt;
&lt;br /&gt;
[[Bild:AVR_Studio_float2.gif]]&lt;br /&gt;
&lt;br /&gt;
Damit ist die Konfiguration abgeschlossen, &amp;quot;&amp;lt;b&amp;gt;OK&amp;lt;/b&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
=Wie funktioniert String-Verarbeitung in C?=&lt;br /&gt;
&lt;br /&gt;
In C gibt es, anders als in anderen Programmiersprachen, keinen eigenen String-Datentyp. Als Ersatz dafür werden Character-Arrays benutzt, in denen die einzelnen Character (=Zeichen) gespeichert werden. Allerdings gibt es noch einen Zusatz: Das letzte Zeichen eines Strings ist immer ein &#039;\0&#039;-Zeichen, dass das Ende des Strings markiert. Schlieslich kann ja das Array wesentlich größer sein, als der in ihm gespeicherte String und irgendwie müssen ja diverse Funktionen das tatsächliche Ende eines Strings erkennen können.&lt;br /&gt;
&lt;br /&gt;
Möchte man also die Zeichenkette &amp;quot;Hello World&amp;quot; in einem String speichern, so wird dafür ein Array mit mindestens der Länge 12 benötigt. 11 für die Zeichen die &amp;quot;Hello World&amp;quot; bilden, plus eine zusätzliche Position für das abschliesende &#039;\0&#039;-Zeichen.&lt;br /&gt;
&lt;br /&gt;
Da Strings in char-Arrays gespeichert werden, können selbstverständlich normale Array Operationen dafür benutzt werden:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  char Test[12];&lt;br /&gt;
&lt;br /&gt;
  Test[0] = &#039;H&#039;;&lt;br /&gt;
  Test[1] = &#039;e&#039;;&lt;br /&gt;
  Test[2] = &#039;l&#039;;&lt;br /&gt;
  Test[3] = &#039;l&#039;;&lt;br /&gt;
  Test[4] = &#039;o&#039;;&lt;br /&gt;
  Test[5] = &#039; &#039;;&lt;br /&gt;
  Test[6] = &#039;W&#039;;&lt;br /&gt;
  Test[7] = &#039;o&#039;;&lt;br /&gt;
  Test[8] = &#039;r&#039;;&lt;br /&gt;
  Test[9] = &#039;l&#039;;&lt;br /&gt;
  Test[10] = &#039;d&#039;;&lt;br /&gt;
  Test[11] = &#039;\0&#039;;   // das abschliessende \0 nicht vergessen! Sonst ist&lt;br /&gt;
                     // das kein String!&lt;br /&gt;
&lt;br /&gt;
  char Temp[6];&lt;br /&gt;
&lt;br /&gt;
  for( i = 0; i &amp;lt; 6; ++i )&lt;br /&gt;
    Temp[i] = Test[ i + 6 ];&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Hinweis:&#039;&#039;&#039; Das &amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;&#039;&amp;lt;/font&amp;gt;&amp;lt;font color=&amp;quot;blue&amp;quot;&amp;gt;&#039;&#039;&#039;\0&#039;&#039;&#039;&amp;lt;/font&amp;gt;&amp;lt;font color=&amp;quot;red&amp;quot;&amp;gt;&#039;&amp;lt;/font&amp;gt; ist nichts anderes als eine binäre Null. Diese spezielle Schreibweise soll explizit die Verwendung dieser &#039;&#039;0&#039;&#039; als Stringende - Character &#039;&#039;(char)0&#039;&#039; hervorheben.&lt;br /&gt;
&lt;br /&gt;
Wird im C-Quelltext ein String in der Form &amp;quot;Hello World&amp;quot; geschrieben, also nicht als einzelne Zeichen, so muss man sich um das abschliessende &#039;\0&#039; Zeichen nicht kümmern. Der Compiler ergänzt das stillschweigend von selbst.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Einige Stringfunktionen ==&lt;br /&gt;
Arrays sind in C keine vollwertigen Datentypen, z.&amp;amp;nbsp;B. ist es nicht möglich einem Array in einem Rutsch ein anderes Array zuzuweisen oder 2 Arrays miteinander zu vergleichen. Genau das möchte man aber in der Stringverarbeitung häufig, sodass es dafür Standardfunktionen gibt, die allesamt im Headerfile &amp;quot;string.h&amp;quot; zusammengefasst sind und deren Namen alle mit str... beginnen. Allen diesen Funktionen gemeinsam ist, dass sie sich &amp;lt;font color=FF0000&amp;gt;&amp;lt;b&amp;gt;nicht&amp;lt;/b&amp;gt;&amp;lt;/font&amp;gt; um die korrekte Bereitstellung von Arrays kümmern, sondern davon ausgehen, dass dies vom Programmierer korrekt erledigt wird.&lt;br /&gt;
&lt;br /&gt;
=== strcpy( char* dest, const char* src ) ===&lt;br /&gt;
Kopieren eines Strings von der Speicherfläche auf die src zeigt, zur Speicherfläche, auf die dest zeigt.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  char Ziel1[20];&lt;br /&gt;
  char Ziel2[20];&lt;br /&gt;
&lt;br /&gt;
  strcpy( Ziel1, &amp;quot;Hallo Welt&amp;quot; );&lt;br /&gt;
  strcpy( Ziel2, Ziel1 );&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== strcat( char* dest, const char* src ) ===&lt;br /&gt;
Anhängen eines Strings an einen bestehenden String.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  char Ziel[20];&lt;br /&gt;
  char Temp[20];&lt;br /&gt;
&lt;br /&gt;
  strcpy( Ziel, &amp;quot;Hallo &amp;quot; );    // Ziel enthält jetzt den String &amp;quot;Hallo &amp;quot;&lt;br /&gt;
  strcat( Ziel, &amp;quot;Welt&amp;quot; );      // Ziel enthält jetzt den String &amp;quot;Hallo Welt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  strcpy( Temp, &amp;quot; !&amp;quot; );&lt;br /&gt;
  strcat( Ziel, Temp );        // Ziel enthält jetzt den String &amp;quot;Hallo Welt !&amp;quot;&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== strcmp( const char* str1, const char* str2 ) ===&lt;br /&gt;
Vergleichen 2-er Strings. Das Ergebnis ist 0, wenn die beiden Strings identisch sind.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  char Ziel[20];&lt;br /&gt;
&lt;br /&gt;
  strcpy( Ziel, &amp;quot;Hallo Welt&amp;quot; );  // Ziel enthält jetzt den String &amp;quot;Hallo Welt&amp;quot;&lt;br /&gt;
&lt;br /&gt;
  if( strcmp( Ziel, &amp;quot;Hallo Welt&amp;quot; ) == 0 )&lt;br /&gt;
    printf( &amp;quot;Ziel war &#039;Hallo Welt&#039;\n&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
  if( strcmp( Ziel, &amp;quot;test&amp;quot; ) != 0 )&lt;br /&gt;
    printf( &amp;quot;Ziel war NICHT &#039;test&#039;\n&amp;quot; );&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== strlen( const char* str ) ===&lt;br /&gt;
Die Länge eines Strings feststellen. Die Länge beinhaltet nicht das abschliessende &#039;\0&#039; Zeichen.&lt;br /&gt;
&lt;br /&gt;
Unter &#039;Länge&#039; wird hier die tatsächliche Länge des Strings (also die Anzahl der im String gespeicherten Zeichen) verstanden und nicht die &#039;Länge&#039; des Arrays in dem der String gespeichert ist. Wird der Text &amp;quot;test&amp;quot; in einem char-Array der Größe 20 gespeichert, so lautet das Ergebnis von strlen() 4 und nicht etwa 20&lt;br /&gt;
&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
  char string[20];&lt;br /&gt;
  strcpy( string, &amp;quot;test&amp;quot; );&lt;br /&gt;
  i = strlen( string );      // i bekommt hier den Wert 4&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Beispiele ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;quot;string.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  char Meldung[14];&lt;br /&gt;
  strcpy( Meldung, &amp;quot;Hello World&amp;quot; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Mittels der Definition&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
  char Meldung[14];&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
wird ein Array bereitgestellt, welches maximal 14 Zeichen aufnehmen kann. &amp;lt;b&amp;gt;Hello World&amp;lt;/b&amp;gt; verbraucht für die lesbaren Zeichen 11 Array-Positionen, dazu noch das obligatorische abschliessende &#039;\0&#039; Zeichen, macht in Summe 12 Positionen. Eine Definition von 14 Zeichen ist also mehr als minimal notwendig&lt;br /&gt;
wäre. Das macht aber nichts, da durch das abschliessende &#039;\0&#039; Zeichen immer feststellbar ist, an welcher Stelle der tatsächliche String zu Ende ist. Die restlichen 2 Array-Positionen sind zur Zeit halt einfach unbenutzt.&lt;br /&gt;
&amp;lt;b&amp;gt;strcpy()&amp;lt;/b&amp;gt; kopiert den 2.ten angegebenen String an die Position auf die sein erstes Argument zeigt. Im obigen Beispiel zeigt das 1.te Argument auf den Beginn von &amp;lt;i&amp;gt;Meldung&amp;lt;/i&amp;gt;, also auf das Array. Folgerichtig wird der String &amp;quot;Hello World&amp;quot; in das Array &amp;lt;i&amp;gt;Meldung&amp;lt;/i&amp;gt; umkopiert. Man beachte auch, dass der Compiler den direkt angegebenen String &amp;quot;Hello World&amp;quot; automatisch mit einem &#039;\0&#039; Zeichen ergänzt hat.&lt;br /&gt;
Nach Ausführung der &amp;lt;b&amp;gt;strcpy()&amp;lt;/b&amp;gt; Funktion enthält also &amp;lt;i&amp;gt;Meldung&amp;lt;/i&amp;gt; den Inhalt:&lt;br /&gt;
&lt;br /&gt;
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+&lt;br /&gt;
     | H | e | l | l | o |   | W | o | r | l | d | \0|   |   |&lt;br /&gt;
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+&lt;br /&gt;
     &lt;br /&gt;
Möchte man an diesen Text jetzt noch etwas anfügen, z.&amp;amp;nbsp;B. ein &amp;quot;?&amp;quot;, so würde das so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;quot;string.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  char Meldung[14];&lt;br /&gt;
  strcpy( Meldung, &amp;quot;Hello World&amp;quot; );&lt;br /&gt;
  strcat( Meldung, &amp;quot;?&amp;quot; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font color=&amp;quot;FF0000&amp;quot;&amp;gt;&lt;br /&gt;
Man beachte: auch wenn hier scheinbar nur ein einzelnes Zeichen angehängt wird, so handelt es sich doch um einen String. Strings werden in C immer mit einem &amp;quot; eingeleitet und abgeschlossen. Im Gegensatz zu einzelnen Zeichen, die in einfache &#039; eingefasst werden. &amp;quot;?&amp;quot; ist also nicht dasselbe wie &#039;?&#039;! Das&lt;br /&gt;
erste ist ein String (der mit dem obligatorischen &#039;\0&#039; Zeichen insgesamt aus 2 Zeichen besteht), während letzteres ein einzelnes Zeichen darstellt! Die meisten str... Funktionen arbeiten nur mit Strings!&lt;br /&gt;
&amp;lt;/font&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Da &amp;lt;i&amp;gt;Meldung&amp;lt;/i&amp;gt; maximal 14 Zeichen umfassen kann, der Text &amp;quot;Hello World?&amp;quot; aber nur aus 13 Zeichen besteht, funktioniert Obiges auch ohne Probleme. Der Array-Inhalt sieht dann wie folgt aus:&lt;br /&gt;
&lt;br /&gt;
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+&lt;br /&gt;
     | H | e | l | l | o |   | W | o | r | l | d | ? | \0|   |&lt;br /&gt;
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+&lt;br /&gt;
&lt;br /&gt;
Ein schwerwiegender Fehler wäre es, wenn der komplette String nach dem &amp;lt;b&amp;gt;strcat()&amp;lt;/b&amp;gt; aus mehr als 14 Zeichen (das &#039;\0&#039;-Zeichen nicht vergessen!) bestehen würde.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;quot;string.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  char Meldung[14];&lt;br /&gt;
  strcpy( Meldung, &amp;quot;Hello World&amp;quot; );&lt;br /&gt;
  strcat( Meldung, &amp;quot; von mir&amp;quot; );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
würde also das Array überlaufen lassen.&lt;br /&gt;
&lt;br /&gt;
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+&lt;br /&gt;
     | H | e | l | l | o |   | W | o | r | l | d |   | v | o | n       m   i   r   \0&lt;br /&gt;
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+&lt;br /&gt;
&lt;br /&gt;
Man sieht sehr schön, daß in diesem Fall die weiteren Zeichen einfach an die Folgepositionen im Speicher geschrieben werden und dadurch ungewollt Speicher verändern, der nicht zu &amp;lt;i&amp;gt;Meldung&amp;lt;/i&amp;gt; gehört. Abhängig von den Details des Programmes können aber an dieser Stelle im Speicher z.&amp;amp;nbsp;B. ganz andere Variablen liegen, die dann verändert werden. &amp;lt;b&amp;gt;strcpy()&amp;lt;/b&amp;gt;, &amp;lt;b&amp;gt;strcat()&amp;lt;/b&amp;gt; oder alle anderen String-Funktionen können den Programmierer gegen diesen Fall nicht schützen! Dazu müssten sie die Größe des Speicherbereichs kennen, was sie nicht tun. Es obliegt einzig und alleine der Sorgfalt des Programmierers, das Programm gegen solche Fälle abzusichern!&lt;br /&gt;
&lt;br /&gt;
Seit einiger Zeit wurde das Sammelsurium der str... Funktionen durch Varianten ergänzt, die sich anschicken dieses Manko zu entschärfen. Diesen Funktionen wird die maximale Anzahl der zu bearbeitenden Zeichen mitgegeben. Mit der Kenntnis der Größe des Zielbereichs und der Länge des bereits darin enthaltenen Strings ist es damit möglich eine Obergrenze auszurechnen, wieviele Zeichen von einer Funktion gefahrlos bearbeitet werden dürfen, ehe der Zielbereich überlaufen würde.&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen heißen grundsätzlich gleich wie die str... Funktionen, nur befindet sich ein kleines &#039;n&#039; im Funktionsnamen. Aus &amp;lt;b&amp;gt;strcpy&amp;lt;/b&amp;gt; wird so &amp;lt;b&amp;gt;strncpy&amp;lt;/b&amp;gt;, aus &amp;lt;b&amp;gt;strcat&amp;lt;/b&amp;gt; wird &amp;lt;b&amp;gt;strncat&amp;lt;/b&amp;gt; usw. Für Details dazu sei auf Literatur oder Web-Recherche verwiesen. Auch wenn einen diese Funktionen gegen die gefürchteten Array-Overflows schützen können, so muß man sich trotzem klarmachen, daß dieser Schutz nur die halbe Miete ist. Denn was soll &amp;lt;b&amp;gt;strncpy&amp;lt;/b&amp;gt; denn tun, wenn der zu kopierende String nicht in das Zielarray passt? &amp;lt;b&amp;gt;strncpy&amp;lt;/b&amp;gt; kopiert soviel wie es kann und gibt dann auf. Aber: Dadurch ist der String aber nicht zur Gänze in den Zielbereich kopiert worden. Programmteile die darauf angewiesen sind, daß der String vollständig kopiert wurde, werden dann nicht mehr oder nicht richtig funktionieren usw. Auch wenn die strn... Funktionen eine gewisse Abhilfe bringen und zumindest den Absturz eines Programmes verhindern können, stellen sie dennoch keine Allheilmittel dar. Um die korrekte Abschätzung der benötigten Arraygrößen kommt man nicht umhin.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;strlen()&amp;lt;/b&amp;gt; liefert die Länge eines Strings. Die Längenangabe beinhaltet dabei nicht das abschliessende &#039;\0&#039; Zeichen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
#include &amp;quot;string.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  char Meldung[14];&lt;br /&gt;
  int  Len;&lt;br /&gt;
  &lt;br /&gt;
  strcpy( Meldung, &amp;quot;Hello World&amp;quot; );&lt;br /&gt;
  Len = strlen( Meldung );&lt;br /&gt;
  &lt;br /&gt;
  /* Hier enthaelt Len den Wert 11 */&lt;br /&gt;
&lt;br /&gt;
  Len = strlen( &amp;quot;Hallo Welt&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
  /* Hier enthält Len den Wert 10 */&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;strcmp()&amp;lt;/b&amp;gt; schlussendlich vergleicht 2 Strings auf Gleichheit. Der Rückgabewert spiegelt dabei die Position des ersten Unterschiedes in den beiden Strings wieder. Folgerichtig sagt ein Wert von 0 daher aus, dass die beiden Strings identisch sind:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#include &amp;quot;string.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  char Meldung1[14];&lt;br /&gt;
  char Meldung2[14];&lt;br /&gt;
&lt;br /&gt;
  strcpy( Meldung1, &amp;quot;Hello World&amp;quot; );&lt;br /&gt;
  strcpy( Meldung2, &amp;quot;Hallo Welt&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
  if( strcmp( Meldung1, Meldung2 ) == 0 ) {&lt;br /&gt;
    /* die Strings sind identisch */&lt;br /&gt;
  }&lt;br /&gt;
  else {&lt;br /&gt;
    /* die Strings sind nicht identisch */&lt;br /&gt;
  }&lt;br /&gt;
  &lt;br /&gt;
  if( strcmp( Meldung2, &amp;quot;Hallo Welt&amp;quot; ) == 0 ) {&lt;br /&gt;
    /* Meldung2 war &amp;quot;Hallo Welt&amp;quot; */&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Es gibt noch weitere String-Funktionen, dafür sei aber auf die Verwendung der zum Compiler gehörenden Dokumentation bzw. auf einführende Literatur zum Thema &#039;Programmieren in C&#039; verwiesen.&lt;br /&gt;
&lt;br /&gt;
=Funktionszeiger=&lt;br /&gt;
Um Menüs oder ähnliche Dinge aufzubauen ist es oft praktisch ein Array von Funktionszeigern zu definieren. Der Aufruf einer Funktion kann dann indirekt über eine Variable erfolgen, wobei die Variable die Adresse der aufzurufenden Funktion enthält.&lt;br /&gt;
&lt;br /&gt;
Um mit Funktionszeigern zu arbeiten ist es in der Praxis sinnvoll sich einen &amp;lt;b&amp;gt;typedef&amp;lt;/b&amp;gt; für den Typ des Funktionszeigers zu definieren. Ein &amp;lt;b&amp;gt;typedef&amp;lt;/b&amp;gt; definiert einen neuen (kürzeren) Namen für einen Datentyp. Und wie wir sehen werden, ist der Datentyp eines Funktionszeigers in der Schreibweise ganz schön umfangreich.&lt;br /&gt;
&lt;br /&gt;
==typedef==&lt;br /&gt;
Einen &amp;lt;b&amp;gt;typedef&amp;lt;/b&amp;gt; zu definieren ist eigentlich ganz einfach: Man schreibt die Deklaration so, als ob man eine Variable definieren würde. Vor das ganze Konstrukt kommt das Schlüsselwort &amp;lt;b&amp;gt;typedef&amp;lt;/b&amp;gt;. Es bewirkt, dass der Name an der Position des Variablennamens zum Namen für den neuen Datentyp wird, der dann in weiterer Folge wie jeder andere Datentyp benutzt werden kann.&lt;br /&gt;
&lt;br /&gt;
Wir wollen einen Funktionszeiger auf eine Funktion definieren, die keine Argumente entgegen nimmt und auch nichts liefert. Also Funktionen nach&lt;br /&gt;
dem Muster:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
void foo( void )&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein entsprechender &amp;lt;b&amp;gt;typedef&amp;lt;/b&amp;gt; würde zB so aussehen:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef void (*VoidFnct)( void );&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Das vereinbart einen neuen Datentyp &amp;lt;b&amp;gt;VoidFnct&amp;lt;/b&amp;gt;. Dieser ist ein Funktionszeiger auf Funktionen, die keine Argumente nehmen und auch nichts zurückliefern.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef int (*IntFnct)( void );&lt;br /&gt;
typedef int (*IntFnct2)( int );&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;IntFnct&amp;lt;/b&amp;gt; ist ein Zeiger auf eine Funktion, die keine Argumente nimmt aber einen &amp;lt;b&amp;gt;int&amp;lt;/b&amp;gt; zurückliefert. &amp;lt;b&amp;gt;IntFnct2&amp;lt;/b&amp;gt; hingegen ist ein Zeiger auf eine Funktion, die einen &amp;lt;b&amp;gt;int&amp;lt;/b&amp;gt; als Argument nimmt und einen &amp;lt;b&amp;gt;int&amp;lt;/b&amp;gt; zurückliefert. Andere Argumenttypen bzw. Rückgabetypen folgen dem gleichen Muster. Wichtig ist, dass sowohl Argumenttypen als auch Rückgabetypen Teil der Signatur eines Funktionszeigers ist. Es ist also nicht möglich einen Funktionszeigertyp zu vereinbaren, der auf beliebige Funktionen mit beliebigen Argumenttypen bzw. Rückgabetypen verweist. Hier muss man ev. auf einen cast ausweichen. Generell ist das aber meist keine gute Idee.&lt;br /&gt;
&lt;br /&gt;
Die Bildung des Datentyps ist im Grunde eigentlich sehr einfach: Man nimmt in Gedanken den Funktionskopf her.&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
double CelsiusToFahrenheit( double Celsius )&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Dann werden in der Argumentliste alle Argumentnamen entfernt&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
double CelsiusToFahrenheit( double )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Der Funktionsname durch den Namen des Datentyps getauscht&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
double CallbackFnct( double )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
Vor den Datentypnamen kommt ein * (wie bei allen Zeigerdefinitionen) und rund um dieses Gebilde kommen Klammern (die den * an den Datentypnamen binden und nicht an den Datentyp des Returnwertes)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
double (*CallbackFnct)( double )&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
und fertig ist der Datentyp einer Funktion die einen double als Argument annimmt und einen double als Returnwert liefert. Davor noch der typedef und das abschliessende ; und wir haben einen neuen Datentyp namens CallbackFnct, der einen Funktionszeiger auf Funktionen mit genau dieser Signatur darstellt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef double (*CallbackFnct)( double );&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Funktionszeigertabellen==&lt;br /&gt;
Mit einem &amp;lt;b&amp;gt;typedef&amp;lt;/b&amp;gt; ist es nun ein leichtes ein Array von Funktionszeigern zu vereinbaren:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef void (*VoidFnct)( void );&lt;br /&gt;
&lt;br /&gt;
VoidFnct MeineFunktionen[5];&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dies vereinbart &amp;lt;i&amp;gt;MeineFunktionen&amp;lt;/i&amp;gt; als ein Array von Funktionszeigern, wobei jeder Funktionszeiger auf eine Funktion vom Typ void-void zeigt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef void (*VoidFnct)( void );&lt;br /&gt;
&lt;br /&gt;
void Funct1()&lt;br /&gt;
{&lt;br /&gt;
  printf( &amp;quot;Dies ist Funktion 1\n&amp;quot; );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void Funct2()&lt;br /&gt;
{&lt;br /&gt;
  printf( &amp;quot;Dies ist Funktion 2\n&amp;quot; );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
VoidFnct MeineFunktionen[] = { Funct1, Funct2 };&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  //&lt;br /&gt;
  // ruft die Funktion auf, deren Adresse in MeineFunktionen[0]&lt;br /&gt;
  // steht. In diesem Fall wäre das Funct1()&lt;br /&gt;
  //&lt;br /&gt;
  MeineFunktionen[0]();&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // und jetzt die MeineFunktionen[1]&lt;br /&gt;
  //&lt;br /&gt;
  MeineFunktionen[1]();&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // jetzt wird MeineFunktionen[0] umgeleitet auf Funct2()&lt;br /&gt;
  // Achtung: Auf der rechten Seite wird kein () angegeben.&lt;br /&gt;
  // Ansonsten würde ja die Funktione Funct2 aufgerufen. Wir&lt;br /&gt;
  // wollen aber nur ihre Speicheradresse haben! Daher unterbleibt&lt;br /&gt;
  // das ()&lt;br /&gt;
  //&lt;br /&gt;
  MeineFunktionen[0] = Funct2;&lt;br /&gt;
&lt;br /&gt;
  //&lt;br /&gt;
  // welche Funktion wird jetzt aufgerufen?&lt;br /&gt;
  //&lt;br /&gt;
  MeineFunktionen[0]();&lt;br /&gt;
  // Richtig: Die Funktion, deren Adresse in MeineFunktionen[0]&lt;br /&gt;
  //          steht. Und das ist jetzt Funct2.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Menüs mit Funktionszeigern==&lt;br /&gt;
Besonders bei Menüs ist es oft hilfreich, sich eine Struktur bestehend&lt;br /&gt;
aus dem Menütext und der aufzurufenden Funktion zu definieren&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
typedef void (*VoidFnct)( void );&lt;br /&gt;
&lt;br /&gt;
struct MenuEntry {&lt;br /&gt;
  char     Text[20];&lt;br /&gt;
  VoidFnct Function;&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ein Menü ist dann einfach ein Array aus derartigen Strukturelementen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void HandleEdit()&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void HandleCopy()&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
void HandlePaste()&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
struct MenuEntry MainMenu[] = {&lt;br /&gt;
 { &amp;quot;Edit&amp;quot;, HandleEdit },&lt;br /&gt;
 { &amp;quot;Copy&amp;quot;, HandleCopy },&lt;br /&gt;
 { &amp;quot;Paste&amp;quot;, HandlePaste }&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
#define ARRAY_SIZE(X) ( sizeof(X) / sizeof(*(X)) )&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
void DoMenu( int NrEntries, struct MenuEntry Menu[] )&lt;br /&gt;
{&lt;br /&gt;
  int i;&lt;br /&gt;
  int Auswahl;&lt;br /&gt;
  &lt;br /&gt;
  do {&lt;br /&gt;
    //&lt;br /&gt;
    // Das Menue anzeigen. Für jeden Menuepunkt noch eine Zahl&lt;br /&gt;
    // davor stellen, damit der Benutzer auch was zum Eingeben hat&lt;br /&gt;
    //&lt;br /&gt;
    for( i = 0; i &amp;lt; NrEntries; ++i )&lt;br /&gt;
      printf( &amp;quot;%d) %s\n&amp;quot;, i + 1, Menu[i].Text ); &lt;br /&gt;
&lt;br /&gt;
    printf( &amp;quot;9) Exit\n\n&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
    // &lt;br /&gt;
    // Jetzt die Benutzereingabe abwarten ...&lt;br /&gt;
    //&lt;br /&gt;
    printf( &amp;quot;Ihre Eingabe: &amp;quot; );&lt;br /&gt;
    scanf( &amp;quot;%d&amp;quot;, &amp;amp;Auswahl );&lt;br /&gt;
&lt;br /&gt;
    //&lt;br /&gt;
    // ... und auswerten&lt;br /&gt;
    //&lt;br /&gt;
    if( Auswahl == 9 )&lt;br /&gt;
      return;&lt;br /&gt;
&lt;br /&gt;
    Auswahl = Auswahl - 1;&lt;br /&gt;
    if( Auswahl &amp;lt; 0 || Auswahl &amp;gt; NrEntries )&lt;br /&gt;
      printf( &amp;quot;Ungültige Eingabe\n&amp;quot; );&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
      // Die Eingabe war gültig. Zugehörige Funktion aufrufen&lt;br /&gt;
      Menu[Auswahl].Function();&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  // Das Menü arbeiten lassen.&lt;br /&gt;
  // Die Funktion DoMenu ruft selbsttätig die zu den jeweiligen&lt;br /&gt;
  // Menüpunkten gehörenden Funktionen auf. DoMenu kommt erst&lt;br /&gt;
  // dann wieder zurück, wenn der Benutzer den Menüpunkt&lt;br /&gt;
  // 9) Exit&lt;br /&gt;
  // ausgewählt hat.&lt;br /&gt;
&lt;br /&gt;
  DoMenu( ARRAY_SIZE( MainMenu ), MainMenu );&lt;br /&gt;
&lt;br /&gt;
  while( 1 )&lt;br /&gt;
    ;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf einem Mikrocontroller wird man natürlich die Ein/Ausgabe nicht über &amp;lt;b&amp;gt;printf&amp;lt;/b&amp;gt;/&amp;lt;b&amp;gt;scanf&amp;lt;/b&amp;gt; abwickeln. Hier geht es aber um das Prinzip der Funktionszeiger und wie man mit ihnen arbeitet, daher wurde die allereinfachste Art der Benutzerinteraktion gewählt. Gegebenenfalls muss &amp;lt;b&amp;gt;printf&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;scanf&amp;lt;/b&amp;gt; durch die Möglichkeiten auf dem konkreten System ersetzt werden. Auch ist die Art und Weise wie das Menü präsentiert bzw. die Benutzereingabe ausgewertet wird, nicht der Weisheit letzter Schluss. Anstatt den Benutzer Zahlen eingeben zu lassen, könnte man auch einen Auswahl-Balken vom Benutzer mit 2 Tasten über die Menüeinträge bewegen lassen. Oder einen Drehencoder nehmen, ...&lt;br /&gt;
&lt;br /&gt;
=Ich hab da mehrere *.c und *.h Dateien. Was mache ich damit?=&lt;br /&gt;
Zunächst ist es wichtig, sich zu vergegenwärtigen wie denn der C Compiler/Linker überhaupt arbeitet. Ein komplettes Programmier-Projekt kann und wird im Normalfall aus mehreren Source Code Dateien bestehen die alle zusammengenommen das komplette Programm bilden.&lt;br /&gt;
&lt;br /&gt;
Der Prozess des Erstellens des Programmes geschieht in mehrerern Schritten:&lt;br /&gt;
* zunächst werden alle Einzelteile (jede *.c Datei) für sich &#039;&#039;compiliert&#039;&#039;. Dabei ensteht für jede *.c Datei eine sog. Object-Datei in der bereits der Maschinencode für die im *.c programmierten Funktionen enthalten ist&lt;br /&gt;
* danach werden die einzelnen Object-Dateien zusammen mit zusätzlichen Bibliotheken zum fertigen Programm &#039;&#039;gelinkt&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Angenommen das komplette Projekt besteht aus 2 Dateien&lt;br /&gt;
&lt;br /&gt;
Datei: &amp;lt;b&amp;gt;main.c&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
int twice(int i);&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  twice( 5 );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datei: &amp;lt;b&amp;gt;func.c&amp;lt;/b&amp;gt;&lt;br /&gt;
&amp;lt;c&amp;gt;&lt;br /&gt;
int twice( int number )&lt;br /&gt;
{&lt;br /&gt;
  return 2 * number;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/c&amp;gt;&lt;br /&gt;
&lt;br /&gt;
dann werden &amp;lt;b&amp;gt;main.c&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;func.c&amp;lt;/b&amp;gt; &amp;lt;i&amp;gt;unabhängig&amp;lt;/i&amp;gt; voneinander compiliert. Als Ergebnis erhält man die Dateien &amp;lt;b&amp;gt;main.o&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;func.o&amp;lt;/b&amp;gt; die den besagten Object-Code enthalten. Erst diese beiden Zwischenergebnisse werden dann zusammen mit eventuellen Bibliotheken zum fertigen Programm gebunden (gelinkt), das dann ausgeführt werden kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
        +---------+                         +----------+&lt;br /&gt;
        | main.c  |                         | func.c   |&lt;br /&gt;
        +---------+                         +----------+&lt;br /&gt;
             |                                   |&lt;br /&gt;
             |                                   |&lt;br /&gt;
             v                                   v&lt;br /&gt;
         Compiler                            Compiler&lt;br /&gt;
             |                                   |&lt;br /&gt;
             |                                   |&lt;br /&gt;
             v                                   v&lt;br /&gt;
        +---------+                         +----------+&lt;br /&gt;
        | main.o  |                         | func.o   |&lt;br /&gt;
        +---------+                         +----------+&lt;br /&gt;
             |                                   |&lt;br /&gt;
             +-----------+   +-------------------+&lt;br /&gt;
                         |   |&lt;br /&gt;
                         v   v&lt;br /&gt;
                         Linker  &amp;lt;------ zus. Bibliotheken&lt;br /&gt;
                           |&lt;br /&gt;
                           v&lt;br /&gt;
                      +----------+&lt;br /&gt;
                      | fertiges |&lt;br /&gt;
                      | Programm |&lt;br /&gt;
                      +----------+&lt;br /&gt;
&lt;br /&gt;
Bekommt man also von irgendwo bereits fertige *.c (und zugehörige *.h) Dateien, so genügt es, die *.c Dateien ganz einfach in das Projekt mit aufzunehmen. Daduch wird das entsprechende *.c File compiliert und das Ergebnis davon, das Object-file, wird dann in das fertige Programm mit eingelinkt.&lt;br /&gt;
&lt;br /&gt;
Wie eine *.c Datei in das Projekt mit aufgenommen wird, hängt im wesentlichen von der benutzten Entwicklungsumgebung ab.&lt;br /&gt;
&lt;br /&gt;
==Makefile==&lt;br /&gt;
Die zusätzliche *.c Datei wird in die SRC Zeile im makefile eingetragen.&lt;br /&gt;
==AVR-Studio==&lt;br /&gt;
Hier ist es besonders einfach eine Datei in das Projekt mit aufzunehmen. Dazu wird im Projektbaum einfach der Knoten &amp;quot;Source Files&amp;quot; aktiviert und mit der rechten Maustaste das Kontextmenü geöffnet. Im Menü wird der Punkt &amp;quot;Add existing Source File(s)&amp;quot; ausgewählt und anschliessend zeigt man AVR-Studio das zusätzliche *.c File. AVR-Studio berücksicht dann dieses File bei der Projekterzeugung, compiliert es und sorgt dafür, daß es zum fertigen Programm dazugelinkt wird.&lt;br /&gt;
&lt;br /&gt;
=Globale Variablen über mehrere Dateien=&lt;br /&gt;
Ein häufige Problemkreis in der C Programmierung sind auch globale Variablen, die von mehreren *.c Dateien aus benutzt werden sollen. Was hat es damit auf sich?&lt;br /&gt;
&lt;br /&gt;
Zunächst mal muß man bei der Vereinbarung von Variablen zwischen &amp;lt;b&amp;gt;Definition&amp;lt;/b&amp;gt; und &amp;lt;b&amp;gt;Deklaration&amp;lt;/b&amp;gt; unterscheiden. Worin besteht der Unterschied?&lt;br /&gt;
* &amp;lt;b&amp;gt;Definition&amp;lt;/b&amp;gt;: Mit einer Definition wird der Compiler angewiesen eine Variable tatsächlich zu erzeugen. Damit er das kann, muß ihm selbstverständlich der exakte Datentyp und auch der Name der Variablen zur Verfügung stehen. Eine Definition sorgt also dafür, daß im späteren Programm Speicherplatz für diese Variable reserviert wird&lt;br /&gt;
* &amp;lt;b&amp;gt;Deklaration&amp;lt;/b&amp;gt;: Mit einer Deklaration teilt man dem Compiler lediglich mit, dass eine Variable existiert. An dieser Stelle soll der Compiler also keinen Speicherplatz reservieren (das muß an anderer Stelle geschehen sein), sondern der Compiler soll einfach nur zur Kenntniss nehmen, daß es eine Variable mit einem bestimmten Namen gibt und von welchem Datentyp sie ist.&lt;br /&gt;
&lt;br /&gt;
Aus obigem folgt sofort, dass eine Definition auch immer eine Deklaration ist. Denn dadurch daß der Compiler angewiesen wird eine Variable auch tatsächlich zu erzeugen folgt, dass er dazu auch dieselben Informationen benötigt, die auch in einer Deklaration angegeben werden müssen. Der einzige Unterschied: Bei einer Deklaration trägt der Compiler nur in seinen internen Tabellen ein, dass es diese Variable tatsächlich gibt, während er bei einer Definition zusätzlich auch noch dafür sorgt, dass im fertigen Programm auch noch Speicher für diese Variable bereitgestellt wird.&lt;br /&gt;
&lt;br /&gt;
Warum ist diese Unterscheidung jetzt wichtig?&lt;br /&gt;
&lt;br /&gt;
Weil es in C die sog. &amp;lt;b&amp;gt;One Definition Rule&amp;lt;/b&amp;gt; oder kurz &amp;lt;b&amp;gt;ODR&amp;lt;/b&amp;gt; gibt. Sie besagt, dass in einem vollständigem Programm, also über alle *.c Dateien gesehen, es für eine Variable nur &amp;lt;b&amp;gt;eine&amp;lt;/b&amp;gt; Definition geben darf. Es darf allerdings beliebig viele Deklarationen geben, solange diese Deklarationen alle im Datentyp übereinstimmen. Kurz gesagt: Man darf den Compiler nur einmal auffordern eine Variable zu erzeugen (Definition), kann sich aber beliebig oft auf diese eine Variable beziehen (Deklarationen). Aber Vorsicht! Da der Compiler jede einzelne *.c Datei für sich alleine übersetzt und dabei kein Wissen von ausserhalb benutzt, obliegt es der Verantwortung des Programmierers dafür zu sorgen, dass alle Deklarationen im Datentyp übereinstimmen. Der Compiler kann diese Einhaltung prinzipbedingt nicht überwachen!&lt;br /&gt;
&lt;br /&gt;
Woran erkennt man eine Definition bzw. Deklaration?&lt;br /&gt;
&lt;br /&gt;
Eine Definition einer globalen Variable steht immer ausserhalb eines Funktionsblocks. Zb.&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  MyData;         // Globale Variable namens MyData. Sie ist vom Typ int&lt;br /&gt;
char Name[30];       // Globales Array&lt;br /&gt;
long NrElements = 5; // Globale Variable, die auch noch initialisiert wird&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eine Deklaration unterscheidet sich von einer Definition in 2 Punkten&lt;br /&gt;
* Es wird das Schlüsselwort &amp;lt;b&amp;gt;extern&amp;lt;/b&amp;gt; vorangestellt.&lt;br /&gt;
* Es kann keine Initialisierung geben. Sobald eine Initialisierung vorhanden ist, wird das Schlüsselwort &amp;lt;b&amp;gt;extern&amp;lt;/b&amp;gt; ignoriert und aus der Deklaration wird eine Definition.&lt;br /&gt;
&lt;br /&gt;
Beispiele für Deklarationen&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int  MyData;&lt;br /&gt;
extern char Name[30];&lt;br /&gt;
extern long NrElements;&lt;br /&gt;
extern long NrElements = 5;  // Achtung: Dies ist keine Deklaration!&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Besitzt man also 2 *.c Dateien, main.c und helpers.c, und sollen sich diese beiden Dateien eine globale Variable teilen, so muss in eine Datei eine Definition hinein, während in die andere Datei eine Deklaration derselben Variablen erfolgen muß. Traditionell werden die Definitionen in der Datei gemacht, die auch die main() Funktion enthält. Das muss nicht so sein, ist aber eine Konvention, die oft Sinn macht. Alternativ wird auch gerne oft eine eigene *.c Datei (zb. globals.c) gemacht, die einzig und alleine die Defintionen der globalen Variablen enthält.&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int  AnzahlElemente;        // Dies ist die Definition. Hier wird die globale&lt;br /&gt;
                            // Variable AnzahlElemente tatsächlich erzeugt.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  AnzahlElemente = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
helpers.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int AnzahlElemente;   // Dies ist die Deklaration die auf die globale&lt;br /&gt;
                             // Variable AnzahlElemente in main.c verweist.&lt;br /&gt;
                             // Wichtig: Der Datentyp muss mit dem in main.c&lt;br /&gt;
                             // angegebenen übereinstimmen&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
   ...&lt;br /&gt;
  j = AnzahlElemente;&lt;br /&gt;
  AnzahlElemente = 9;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Praktische Durchführung==&lt;br /&gt;
Besteht ein vollständiges Programm aus mehreren *.c Dateien, dann kann man sich vorstellen, daß es mühsam ist, alle Deklarationen immer auf gleich zu halten. Hier bietet sich der Einsatz eines Header Files an, in der die Deklarationen stehen und welches in die jeweiligen *.c Dateien inkludiert wird&lt;br /&gt;
&lt;br /&gt;
Bsp:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int Anzahl;      // auch wenn Global.h inkludiert wurde, so muss es eine&lt;br /&gt;
                 // Definition der Variablen geben. In Global.h sind ja nur&lt;br /&gt;
                 // Deklarationen.&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
bar.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void bar()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  j = Anzahl;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Auf diese Art kann man erreichen, dass zumindest alle Deklarationen ein und derselben Variablen in einem Programm übereinstimmen. Die Datei Global.h wird auch in main.c inkludiert, obwohl man das eigentlich nicht müsste, denn dort wird die Variable ja definiert. Durch die Inclusion ermöglicht man aber dem Compiler die Überprüfung ob die Deklaration auch tatsächlich mit der Definition übereinstimmt.&lt;br /&gt;
&lt;br /&gt;
Solange kein Initialisierungen der globalen Variablen notwendig sind, gibt es noch einen weiteren Trick, um sich selbst das Leben und die Verwaltung der globalen Variablen zu erleichtern.&lt;br /&gt;
Worin besteht das Problem?&lt;br /&gt;
Das Problem besteht darin, dass man bei Einführung einer neuen globalen Variablen an 2 Stellen erweitern muss: Zum einen in der Header-Datei, die die &#039;extern&#039;-Deklaration der Variablen enthält, zum anderen muss in einer C-Datei die Definition der Variablen erfolgen. Das kann man sich mit etwas Präprozessorarbeit auch einfacher machen:&lt;br /&gt;
&lt;br /&gt;
Global.h&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#ifndef EXTERN&lt;br /&gt;
#define EXTERN extern&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
main.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 5;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
foo.c&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void foo()&lt;br /&gt;
{&lt;br /&gt;
  ...&lt;br /&gt;
  Anzahl = 8;&lt;br /&gt;
  ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Wie funktioniert das Ganze? Im Grunde muss man nur dafür sorgen, dass der Compiler an &#039;&#039;einer&#039;&#039; Stelle das Schlüsselwort &#039;&#039;&#039;extern&#039;&#039;&#039; ignoriert (hier in main.c) und bei allen anderen Inclusionen beibehält. Dadurch das ein Präprozessor-ifndef benutzt wird, kann dieses erreicht werden. Wird das Header File includiert und ist zu diesem Zeitpunkt das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; noch nicht definiert, so wird innerhalb des Header Files &#039;&#039;&#039;EXTERN&#039;&#039;&#039; zu &#039;&#039;&#039;extern&#039;&#039;&#039; definiert und damit in weiterer Folge im Quelltext &#039;&#039;&#039;EXTERN&#039;&#039;&#039; durch &#039;&#039;&#039;extern&#039;&#039;&#039; ersetzt. Wenn daher foo.c das Header File inkludiert, wird die Zeile&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
extern int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
umgewandelt.&lt;br /&gt;
&lt;br /&gt;
In main.c hingegen sieht die Include-Sequenz so aus&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
#define EXTERN&lt;br /&gt;
#include &amp;quot;Global.h&amp;quot;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
Wenn Global.h bearbeitet wird, existiert bereits ein Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039;, das auf einen leeren Text expandiert. Dadurch wird verhindert, dass innerhalb von Global.h das Makro &#039;&#039;&#039;EXTERN&#039;&#039;&#039; mit dem Text &#039;&#039;&#039;extern&#039;&#039;&#039; belegt wird und&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
EXTERN int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
wird daher vom Präprozessor zu&lt;br /&gt;
&amp;lt;C&amp;gt;&lt;br /&gt;
int Anzahl;&lt;br /&gt;
&amp;lt;/C&amp;gt;&lt;br /&gt;
erweitert, genau wie es benötigt wird.&lt;br /&gt;
&lt;br /&gt;
= Konstanten an fester Flash-Adresse =&lt;br /&gt;
&lt;br /&gt;
Wie kann man eine Konstante an entsprechender Adresse im Flash ablegen?&lt;br /&gt;
&lt;br /&gt;
Mehmet Kendi hat eine Lösung für [[AVR Studio]] &amp;amp; [[WinAVR]] in &lt;br /&gt;
[http://www.mikrocontroller.net/topic/142704#1453079] angegeben.&lt;br /&gt;
&lt;br /&gt;
[[Kategorie:Grundlagen]]&lt;br /&gt;
[[Kategorie:C]]&lt;/div&gt;</summary>
		<author><name>194.208.80.202</name></author>
	</entry>
</feed>