Forum: Compiler & IDEs der gcc, das const-Schlüsselwort und der Speicher...


von emax (Gast)


Lesenswert?

Hallo Leute!

Wenn das stimmt, was ich vermute, dann bin ich ein bischen frustriert.

Zunächst hatte ich mich gewundert, das im AvrStudio auch meine
Programm-Konstanten im "data" Bereich auftauchten (Compiler: gcc).

Ich hab dann alle Texte (Menues usw.) als "const" definiert, und war
der Auffassung, dass diese dann logischerweise im "Program"- Bereich
landen muessten, so wie ich das vom 8051 kenne. Dort gibt es den
"movc" Befehl, und wenn man "const" - Strings benutzt, muss man
sich weiter keine Gedanken machen: die Sachen landen dann alle im eprom
und gut isses.

Nachdem nun meine Applikation nicht mehr lief (weil der data-bereich
voll war) hab ich hier im Forum gesucht. Wenn ich's richtig verstanden
habe, dann muss man tatsächlich solche 'const's explizit anders
programmieren (pgmspace.h), wenn man sie nicht im data-Bereich haben
will?

DAS WAER' JA SCHEUSSLICH!

Vermutlich habe ich dann auch graessliche Laufzeiten, wenn ich Texte
aus dem PgmSpace verarbeiten will? Ist das so?

Oder hab ich nur nicht das Richtige gefunden?

Beim 8051 hatte ich auch mit nur 256 Byte Ram doch immerhin die
Möglichkeit, volle 64K für 'const' daten und Programm zu haben, ohne
so einen Zirkus wie pgmspace.h.

Und nochwas (da hab ich aber ehrlich gesagt noch nicht weiter gesucht):
Mein At90s8535 hat 512 Byte Ram. "isp" meldet mir aber

" Signature: ATMEL AT90S8535  8kB Flash
     operates with: 8192 Bytes Flash and 256 Bytes data-memory"

was ja eigentlich bei einem 8-Bitter auch logisch ist.

Wie macht man sich die 512 Byte zunutze?

e.

von Holger (Gast)


Lesenswert?

So geht es:

Deklaration/Definiton:
  static const unsigned char unshifted[][] PROGMEM = { ... };
  PGM_VOID_P index= unshifted;

Zugriff mit:
  pgm_read_byte( unshifted++ );


Das liegt daran, dass der GCC normalerweise für von
Neumann-Architekturen Code erzeugt. Darum die spezielle Deklaration.

von Jörg Wunsch (Gast)


Lesenswert?

"const" ist in C übrigens -- entgegen weitläufiger Auffassung --
nicht die Deklaration einer Konstanten, sondern lediglich die
Aufforderung an den Compiler, Zugriffe auf das so bezeichnete Objekt,
die zu einer Manipulation des Objektes führen können, anzuwarnen.  (In
C++ ist das anders.)

Wenn manche Compiler das "const" als Pseudo-Speicherklasse benutzen,
ist das IMHO eine Verletzung des C-Standards.  Aber was soll's, diese
Compiler implementieren schließlich auch PORTA.0 oder sowas, und das
hatten wir neulich schonmal, das läßt sich mit keiner C-Syntax
rechtfertigen (auch wenn es schön bequem aussieht).

> Vermutlich habe ich dann auch graessliche Laufzeiten, wenn ich Texte
> aus dem PgmSpace verarbeiten will? Ist das so?

,,gräßliche Laufzeiten'' wäre übertrieben, aber durch die Harvard-
Architektur hast Du beim AVR zwangsweise einen Overhead, wenn Du
konstante Daten im Flash ablegen möchtest, da sie zur Laufzeit erst
von da in den RAM bzw. in Register geladen werden müssen.  Dabei ist
es komplett unabhängig, ob dieses Laden explizit erfolgt (wie durch
pgm_read_byte() beim AVR-GCC) oder implizit im Rahmen einer normalen
Zuweisung (wie vielfach bei anderen AVR-Compilern) -- intern muß in
jedem Falle ein LPM-Befehl benutzt werden.

Dafür spart man eben RAM.

Nu ja, der Vergleich mit dem 8051 hinkt ein wenig: der hat (beim
Standardchip) 12 Takte gebraucht, um überhaupt mal den Finger zu
rühren, während der AVR in dieser Zeit schon eine komplette
Multiplikation hinter sich gebracht haben kann. ;-)

> Und nochwas (da hab ich aber ehrlich gesagt noch nicht weiter
> gesucht): Mein At90s8535 hat 512 Byte Ram. "isp" meldet mir aber

> " Signature: ATMEL AT90S8535  8kB Flash
>      operates with: 8192 Bytes Flash and 256 Bytes data-memory"

> was ja eigentlich bei einem 8-Bitter auch logisch ist.

Warum solle das logisch sein?  Der (derzeit) größte AVR hat 4 KB RAM
(und kann ohne Banking mit externem RAM auf 64 KB ausgebaut werden),
was soll daran ungewöhnlich sein?  Der Z80 konnte ebenfalls 64 KB
adressieren, auch wenn es ein 8-Bit-Prozessor ist.  RAM-Adressen sind
halt allesamt 16 bit breit.

Du hast drei Speicherbereiche: Flash-ROM, SRAM, und EEPROM.  Der SRAM
wird Dein Programmiertool nicht interessieren: es kann darauf sowieso
nicht zugreifen.  Was dort als data-memory bezeichnet wird, dürfte der
EEPROM sein (der sowohl vom Programmer als auch von der Applikation
aus zugreifbar ist).

von emax (Gast)


Lesenswert?

Hm.

Die Sache mit dem const - Schlüsselwort liest sich im "ANSI and ISO
Sandard-C, Programmers Reference" so:

"A const qualified type indicates that access to the designated data
object cannot alter the value stored in the data object. All other data
objects can have their values altered".

Ansi und Iso - Standard wohlgemerkt (Plauger & Brodie 1992).

Zum 8051 - ich will hier ja keinen Wettbewerb veranstalten. Was glaubst
Du wohl, warum ich auf die AVR's umgestiegen bin? Ist doch gar keine
Frage, das die Dinger sehr viel performanter sind, und jede Menge
Vorteile haben. Aber es ist doch auch verständlich, dass man zuweilen
dem ein oder anderen Goodie seines bisherigen "Lieblings-Prozessors"
nachweint (vor allem dem Datentyp "bit").

Ich beschäftige mich erst seit dem Wochenende mit den AVRs, und bin
nach wie vor begeistert. Trotzdem ist die anfängliche Euphorie auch ein
wenig der Ernüchterung gewichen (ein normaler Vorgang). Denn bei Licht
besehen erweisen sich einige der Vorteile als weniger gross, als sie in
den Marketingmassnahmen der Hersteller zunächst erscheinen.

Zum Beispiel: die eben erwähnte Speicherklasse Bit. wenn so ein 8051
auch wenig Speicher hat, so kann man da in einem einzigen Byte bereits
8 Flags speichern. Bei den AVRs brauchste dazu 8 Byte. Da relativiert
sich das mit dem Speicher schon ein wenig.

Oder die Tatsache, dass ich keine v. Neumann-Boards damit bauen kann
(muss ich ja auch nicht unbedingt, aber praktisch wars manchmal schon).
Aber das ist eben der Preis der Integration und des Komforts. Wenn ich
meine Speicherbausteien (RAM/ROM) extern aufbaue, kann ich sie
natürlich auch verdrahten, wie ich will, logo.

Im Übrigen gibts die 8051-Derivate bis 50 Mips - vergiss mal die 12
Takte, das war, wie Du selber sagst, der Core beim Standardchip, wie er
ehemals von Intel und Siemens gebaut wurde. Spätestens seit Dallas oder
SiLabs (ehem. Cygnal) ist das anders.

> Warum solle das logisch sein?

Stimmt, - ich bin mal wieder vom "internen" Speicher ausgegangen, wie
ich ihn vom 8051 Kenne, okokok, Du hast recht ...

Nur insofern muss ich einschränken, dass RAM-Adressen keineswegs immer
16 Bit breit sind. Nochmal 8051: das Interne Ram bei den 8051-ern war 7
(!) Bit breit, ergo auch seine (internen) RAM-Adressen. Erst beim 8052
wurde der Speicher um sagenhafte 128 Byte aufgestockt: das waren dann
immerhin 8 Bit (interne) Ram-Adressen.

Und da liegt halt auch mein Denkfehler: ich muss eben noch lernen, dass
es hier nicht um den "internen" Prozessorspeicher geht, sondern um
das bei anderen Prozessorarchitekturen extern vorhandene und
"sichtbare" SRAM. Nur eben schon drin im Gehäuse.

Das heisst dann aber auch nichts anderes, als das faktisch bereits ein
Latch in duie AVRs eingebaut ist, weil 2^8 nunmal nur bis 256 reicht.

e.

von emax (Gast)


Lesenswert?

Grrr!!

Und jetzt hab' ich das alles schön gemacht mit

1. Versuch:
static const char *Menue[] PROGMEM = { ... };

Steht im AVRStudio trotzdem noch im data-Bereich. Ich sehe sie aber
AUCH IM PROGRAMM-Bereich.

2. Versuch:

static const prog_char *Menue[] = { ... };

Gleiches Ergebnis.

Ich kapier da sowieso was nicht.

Wenn ich den Chip programmiere, landet das Programm im Flash (was der
Funktion nach einem Eprom entspricht). Dabei müssen nun auch meine
Konstanten muessen ja irgendwo hin.

Wo landen die?

Wenn die Im Flash landen würden (ohne die pgmspace.h - Definitionen),
dann müssten die ja bei Prozessorstart irgendwie in den Data-Bereich
rüberkommen.

WENN NICHT, wo landen sie dann? Sofort im Data-Bereich (schon bei der
Programmierung?). Kann eigentlich nicht sein: bei Abklemmen von Vcc
sind alle Daten weg. Dann wären auch die Konstanten weg, wenn sie in
Data - Bereich liegen würden.

Also: wohin werden die Texte bei der Programmierung geschrieben?

Ich kapiers nicht.

e.

von Holger (Gast)


Lesenswert?

@emax:
Was das const angeht, so hat Jörg da recht, const ist kein Datentyp,
sondern ein Typ Modifier.

"Zum Beispiel: die eben erwähnte Speicherklasse Bit": Warum nimmst Du
nicht ein Bitfeld? Das ist zwar von der Performance her weit vom 8051er
entfernt, aber es braucht in Deinem Beispiel immerhin nur ein Byte
anstatt 8.

Die 8051-Derivate gibt es im Übrigen bis 100 Mips, dazu noch mit
umfangreicherer Ausstattung, so z.B. mit zusätzlicher MAC-Einheit, dass
in einigen Fällen kein DSP mehr nötig ist.

Und wieso muss ein Latch in den AVR eingebaut sein, nur weil die
Datenbreite 8 Bit ist? Das ist völlig unabhängig von der
Adressbusbreite.

von Holger (Gast)


Lesenswert?

Die einzelnen Strings musst Du auch noch als PROGMEM deklarieren. In der
Doku steht es so:

#include <avr/pgmspace.h>

const char foo[] PROGMEM = "Foo";
const char bar[] PROGMEM = "Bar";

PGM_P array[2] PROGMEM = {
    foo,
    bar
};

int main (void)
{
    char buf[32];
    strcpy_P (buf, array[1]);
    return 0;
}

von emax (Gast)


Lesenswert?

@Holger

>Was das const angeht, so hat Jörg da recht,
>const ist kein Datentyp,

Das sagt ja auch keiner. Natürlich ist const kein Datentyp. Sonst wäre
ja z.B. "const int" ein Widerspruch in sich ....

Es ging um die Frage, ob es die Deklaration einer Konstanten oder
eine Aufforderung an den Compiler ist. Und da sagt die ANSI / ISO -
Beschreibung schlicht etwas anderes als Jörg.

Man könnte noch drüber nachdenken, wo da eigentlich der Unterschied
ist. Derartige Prüfungen gibt es bei C (im Gegensatz zu C++) zur
Laufzeit nicht. Deshalb ist bei C die Frage "Konstante oder nicht" im
Grunde nur zur Compile-Zeit relevant. Denn dann und nur dann kann mir
der Compiler einen Fehler melden. Hinterher ist's eh' zu spät, wenn
man mal vom üblichen segfault absieht. Den musses -je nach Maschine-
nämlich nicht zwangsläufig geben...

Zu den Bits: so mach ichs ja auch, sogar performanter als bei den 8051

 (die AVR sind eben einfach schneller). Es ist halt nur nicht so
komfortabel. Ausserdem ist die Reihenfolge der Bits im Bitfeld nach
C-Standard nicht definiert, und das ist - genau genommen - doch eine
Einschränkung. Ist irgendwie nicht so schön, wenn z.B. Bitfeld.Bit7 an
der 4. Stelle im Byte auftaucht...

Aber Du hast schon recht: man kann damit leben.

Für Dein Beispiel zum pgmspace vielen Dank, aber ich denke, so hab
ich's gemacht: PGM_P ist nichts anderes als "const prog_char *" ...
Aber Irgendwas hab' ich vielleicht doch übersehen ...

Geb mir doch mal nen Tip, wo die Doku zu finden ist. In der avr-libc
hab' ich nichts gefunden. Vielleicht hab' ich aber auch Tomaten auf
den Augen ...

Vor allem quält mich immer noch meine Kernfrage:

"wohin werden die Texte bei der Programmierung geschrieben?"

e.

von Holger (Gast)


Lesenswert?

Was die Bit-Vars angeht, so hast Du doch oben geschrieben, dass man bei
den AVRs 8 Bytes braucht: "Bei den AVRs brauchste dazu 8 Byte",
deshalb mein Einwand.
Und schneller sind die nur gegenüber den Standard-8051ern. Es braucht
beim AVR dafür drei Befehle anstatt einem.

Und hast Du wirklich sowohl die Array-Variable als auch die einzelnen
Stringkonstanten im Array getrennt als PGM deklariert?

In der Doku steht es in der FAQ.

von Matthias (Gast)


Lesenswert?

Hi

der GCC hat halt Probleme mit der Harvard-Architektur. Wenn du den
Komfort willst den du vom 8051 gewohnt bist (nicht der 8051 bietet
diesen Komfort sondern der Compiler) mußt du halt einen Compiler kaufen
der diesen Komfort bietet oder aber den AVR-Port des SDCC (der kann
besser mit Harvard und unterschiedlichen Speicherklassen umgehen) zur
Funktionsfähigkeit bringen.

Ich wundere mich immer wieder wie manche Leute von kostenloser
Software, mit der sie sogar Geld verdienen dürfen, gleiches erwarten
wie von Software für einige k€(Keil oder der IAR-Compiler). Und der GCC
bietet, zusammen mit der AVRlibc, genug um auch professionelle Software
zu entwickeln.

Ich habe einfach das Gefühl du hast die Doku nicht ausreichend gelesen.
Also hinsetzen und das hier
http://jubal.westnet.com/AVR/doc/avr-libc-user-manual/FAQ.html#faq_rom_array
lesen.

Matthias

von emax (Gast)


Lesenswert?

Mann oh Mann,

was ist denn das für ein Gerede?

Es wäre hilfreich, wenn Du mal wirklich lesen würdest, was ich
geschrieben habe. Aber ich schreibs auch gerne nochmal:

NATUERLICH SIND DIE AVR DEN 8051 ÜBERLEGEN! Natürlich habe ich
gewechselt, weil das Gesamtpaket AVR/GCC das bessere ist!

Deshalb HABE ICH JA gewechselt!

Ausserdem klage ich nicht über mangelnden Komfort, sondern führe eine
Diskussion über die Unterschiede. Und da gibts auf beiden Seiten Vor-
und Nachteile. Ist denn das so schwer zu kapieren?

Und wenn Du hiermit

"....Ich wundere mich immer wieder wie manche Leute von kostenloser
Software, mit der sie sogar Geld verdienen dürfen, gleiches erwarten
wie von Software für einige ..."

mich meinst, dann liegst Du schlicht schief.

Erstens bin ich selber überzeugter Opensource-Mann (weshalb ich da, wo
es geht, auch auf eine WinDAU Umgebung verzichte). Zweitens kenne ich
zwar den Keil-Compiler, habe aber eine Menge Dinge auch mit dem SDCC
gemacht. Und drittens erwarte ich nicht, dass der gcc das Selbe kann,
wie der C51 auf den 8051. Mithin kostet der ja auch ein paar Märker.

Wenn aber in einem Forum keine Diskussion geführt werden kann, weil
sich irgendjemand ans Bein gepinkelt fühlt, oder weil man Angst haben
muss, sonst irgendwelche heiligen Kühe zu schlachten, dann kannste das
Forum eigentlich vergessen.

Merke: eine Sache, die nicht verbessert werden kann, die gibts nicht.
Und wenn die Diskussion darüber unerwünscht ist, dann wird die
Entwicklung dieser Sache stehenbleiben.

Also nochmal und gaanz deutlich:

JA, DER AVR-GCC IST GANZ TOLL. ICH FREUE MICH, DASS ES IHN GIBT.

Noch mal so was am Rande: ich habe meine ersten gcc-build auf einem
286er gemacht. Das ist sehr, sehr lange her, und war noch unter einer
Kernelversion von 0.99-irgendwas. Ich finde diesen Compiler überragend,
und habe damit eine Menge Produktionssoftware unter AIX und Solaris
geschrieben. Also bleib mir " Leuten die von kostenloser Software
erwarten ..." vom Hals.

e.

PS: meine Kernfrage ist immer noch offen. Da hier ja eine Menge Leute
sind, die alle verfügbaren Dokumentationen von vorn bis hinten
durchgelesen haben ;-)) müsste mir das doch einer beantworten können:

"wohin werden die Texte bei der Programmierung geschrieben?"

von emax (Gast)


Lesenswert?

Noch mal an Matthias,

danke für den Link. Ich habe wirklich vor lauter Doku in den letzten
Tagen nicht mehr gewusstm, wo ich schauen soll (AT90s8535, HD44780,
AVR-LIBC, AVR-GCC, ISP, AVRCtrl etc. etc.).

Die Seite erklärt wie's scheint genau, was man machen muss. Allerdings
habe ich meine Menues mit typedef'ed structs angelegt, Da siehts ein
bischen anders aus, ich mus mal schauen.

manjana.

e.

von emax (Gast)


Lesenswert?

Und jetzt kann ich auch meine Kernfrage selber beantworten:

.init4:
    Copies the .data section from flash to SRAM. Also sets up and zeros
out the .bss section. In Unix-like targets, .data is normally
initialized by the OS directly from the executable file. Since this is
impossible in an MCU environment, avr-gcc instead takes care of
appending the .data variables after .text in the flash ROM image.
.init4 then defines the code (weakly bound) which takes care of copying
the contents of .data from the flash to SRAM.

Man muss es nur finden.

Ok, jetzt wissen wirs,

e.

von Jörg Wunsch (Gast)


Lesenswert?

Mußt ja hier nicht so rumschreien.

Nochmal zu "const": nein, in C gibt es keine Konstanten, sondern
bestenfalls Variablen, die sich nicht ändern lassen.  Das ist ein
Unterschied.  Wenn Du richtige Konstanten brauchst, mußt Du den
Präprozessor bemühen (dann gehen sie aber typischerweise aus der
Debug-Info verloren).  In dieser Hinsicht sind C++ oder Pascal anders:
dort deklariert const wirklich eine Konstante, und es bleibt dem
Compiler überlassen, ob er dafür tatsächlich erst Speicher belegt,
oder ob er sie inline benutzt.

Dank Harvard ist das beim AVR alles nicht so einfach.  Wenn alle
Konstanten automatisch im Flash landen würden, dann hättest Du ein
Problem mit Ausdrücken wie:

const char *foo = "hello world";

...
strcmp(something, foo) ...

strcmp() muß in der Lage sein, beliebige Strings zu vergleichen.  Wenn
Du nicht gerade OO-typisches `late binding' haben möchtest, sollte
bereits zur Compilezeit feststehen, wie auf den zugehörigen Speicher
zuzugreifen ist.  strcmp() erwartet einfach beide Argumente im RAM.
Damit würde obige Operation (die durch Weiterreichen des Zeigers an
Funktionen noch beliebig für den Compiler undurchsichtiger gemacht
werden könnte ;-) aber fehlschlagen, wenn er automatisch alle string
literals im ROM anlegt, da für ROM-Operationen strcmp_P() benutzt
werden muß.  In der Tat habe ich seltsamste Verrenkungen für
ebendieses Problem im Code für die AVR Butterfly gesehen, der ja für
den IAR geschrieben ist, der wiederum string literals von sich aus im
ROM anlegt (da er im Gegensatz zum GCC besser mit Harvard umgehen
kann).

Seit ich das gesehen habe, sehe ich AVR-GCCs explizite Notwendigkeit,
die Benutzung des ROM auf den Programmierer abzuwälzen, gar nicht mehr
als so einen großen Nachteil an. ;-)

Zurück zum Standard: ``If an attempt is made to modify an object
defined with a const-qualified type through use of an lvalue with
non-const-qualified type, the behavior is undefined. [...]''

Das ist alles, was der Standard dazu sagt (6.7.3 Type qualifiers).
Anders gesagt: der Compiler darf das auch komplett ignorieren, er ist
trotzdem noch konform.  Eine Aussage wie die von Dir gemachte sehe ich
als Anforderung durch den Standard nicht (sie beschreibt vielmehr die
Intention, die dahinter steht).

Bitfelder sind übrigens nicht wahllos anzuordnen (*): es ist lediglich
implementierungsabhängig, ob sie ,,von links nach rechts'' oder
genau
andersrum implementiert werden.  Außerdem, wenn Du Dir nur ein Bit
merken willst (also nicht etwa auf IO-Ports zugreifen), kann es Dir ja
auch egal sein, in welchem Bit er sich das wirklich merkt. ;-)

(*) 6.7.2.1 Structure and union specifiers, Absatz 10: ``The order of
allocation of bit-fields within a unit (high-order to low-order or
low-order to high-order) is implementation-defined.''  Es ist
übrigens
explizit gefordert, daß aufeinanderfolgend deklarierte Bitfelder auch
aufeinanderfolgend zu speichern sind, sofern der Platz im Objekt das
zuläßt.  (Alles andere hätte ja auch keinen Sinn.)

> "wohin werden die Texte bei der Programmierung geschrieben?"

Welche Texte denn?

Meinst Du string literals ganz allgemein?  Das sind Variablen wie jede
andere (s. o., um zu Standard-string-Funktionen trotz Harvard
kompatibel zu bleiben).  Folglich belegen sie RAM, genauso wie sie
natürlich Flash-ROM belegen, um die Initialisierungswerte
unterzubringen.  Da wir hier nicht beim VM-Unix sind, können wir diese
nicht via page fault zur Laufzeit aus der Datei lesen...  Das ist
nicht
anders, als wenn Du

int foo = 42;

oder

int char[] = {'H', 'e', 'l', 'l', 'o'};

schreibst.

Lediglich Strings, die mit __attribute__((progmem)) deklariert worden
sind (bzw. einem Makro, der das effektiv erledigt), belegen
ausschließlich ROM, dafür müssen sie (weil der Prozessor das so will)
halt zur Laufzeit erst aus dem ROM in den RAM bzw. Register kopiert
werden, bevor man mit ihnen irgendwas machen kann.  (Die
Initialiserungswerte für die Variablen müssen natürlich auch kopiert
werden, aber das passiert durch den C-Start-Code einmalig unmittelbar
nach dem Reset.)

von Peter D. (peda)


Lesenswert?

@Emax,

"Ich hab dann alle Texte (Menues usw.) als "const" definiert, und
war
der Auffassung, dass diese dann logischerweise im "Program"- Bereich
landen muessten, so wie ich das vom 8051 kenne. Dort gibt es den
"movc" Befehl, und wenn man "const" - Strings benutzt, muss man
sich weiter keine Gedanken machen: die Sachen landen dann alle im
eprom
und gut isses."

Von welchen 8051 Compiler sprichst Du denn ?
Beim Keil C51 ist es definitiv nicht so, da bedeutet const genau daß,
was Jörg beschrieben hat.

Der Keil hat dafür spezielle Schlüsselwörter (code, data, bdata, xdata
usw.) um den Speicherbereich einer Variablen festzulegen.
Und sämtliche string-Funktionen benutzen generic pointer, d.h. sie
bekommen in einem 3.Byte übergeben, auf welchem Speichertyp sie
arbeiten sollen.


Peter

von Heinz (Gast)


Lesenswert?

Hallo,

wie waere es statt "const" zu benutzen, einfach eine (vielleicht)
ueberholte Methode zu verwenden und zwar mit  #define bzw. irgendwelche
Macros oder so....

viellecht hab ich das hier auch nicht ganz verstanden... aber dann
sollten die "Arrays" im Programm-Memory sein, oder???

Heinz

von emax (Gast)


Lesenswert?

Jörg, was Du im, letzten Absatz schreibst "zur Laufzeit erst aus dem
ROM in den RAM bzw. Register kopiert werden," hatte ich ja
mittlerweile selbst rausgefunden. Trotzdem danke. Und mein Plauger &
Brodie - Buch schmeiss' ich wohl weg ...

Was Peter schreibt bringt mich wieder ins Lot: Du hast recht, ich hatte
es einfach ausgeblendet:

"code char Password [] = "xyz";

Allerdings gibt es das beschriebene strcpy Problem dort scheinbar
zunächst nicht. Man kann beliebige Strings, code oder data, kopieren
und vergleichen. Da es aber einen Unterschied geben MUSS (movc ist
nicht mov), habe ich das ganze eben mal genauer unter die Lupe
genommen. Und tatsächlich gibt es das Problem eben nur scheinbar
nicht: der Compiler verdeckt das.

Ein 3-Byte strcpy code->data: 99 Zyklen, data->data: 87 Zyklen. Also
hab' ich einen tracedown der strcpy-Funktion gemacht, und siehe da: es
gibt dort eine Sprungtabelle, die je nach übergebenem Pointer
unterschiedliche Unterfunktionen  anspringt. Diese Information wird in
Registern übergeben.

Die Zusammenhänge sind mir jetzt auch für den AVR-GCC klarer. Ich werd
mir das auch mal beim SDCC anschauen...

e.

von Jörg Wunsch (Gast)


Lesenswert?

> Man kann beliebige Strings, code oder data, kopieren und
> vergleichen.

Das schrob Peter aber schon: intern wird noch ein zusätzliches
Attributbyte für jeden char * geführt, und die Entscheidung wird zur
Laufzeit getroffen.  Klingt nicht unelegant, macht allerdings einen
Heidenaufwand (sizeof(char *) != sizeof(int *) == sizeof(any other *))
und kostet zusätzlichen Speicherbedarf (OK, minimal) und Laufzeit (für
die Entscheidung, was zu nehmen ist).  Außerdem entsteht zusätzlicher
Bloat, wenn man in einer Applikation gar nicht alle möglichen
Entscheidungspfade der generischen Funktionen braucht (weil man z. B.
gar keine ROM-Strings hat).

Soweit ich Peter mal verstanden hatte, ist das allerdings tunable,
d. h. man kann das auch alles abschalten.

von Matthias (Gast)


Lesenswert?

Hi

kann man. Zumindest beim SDCC. Man sagt dem Pointer was er für ein
Pointer ist. Also statt

char *
xdata char *

Ersteres währe ein generic pointer der zur Laufzeit auf alle
Speicherbereiche zeigen kann. Zweiteres ist ein Pointer der nur in den
XRAM zeigen kann aber eben schneller im Zugriff ist.

Matthias

von emax (Gast)


Lesenswert?

@Jörg

"Das schrob Peter aber schon: ..."

Das "aber" kling nach Widerspruch. Dabei war das doch exakt das Thema
meines letzten Beitrages, sogar mit präziser Code-Analyse.

Der "Bloat" (ich nehme an, Du meinst toten Code) wird in der Praxis
aber kaum Auswirkungen haben. Bei einem umfangreicheren Projekt werden
vemutlich die meisten der möglichen Entscheidungspfade tatsächlich auch
verwendet werden. Die wenigen, die übrig bleiben, dürften nur in Fällen
stören, wo es wirklich auf jedes ROM-Byte ankommt.

Das Code dann trotzdem äusserst kompakt ausfallen kann, zeigen die
Keil-Ergebnisse.

e.

von emax (Gast)


Lesenswert?

Die Strings sind jetzt da, wo ich sie hin haben will.
Wer weiss denn, wie ich noch diese Warnung weg bekomme, ich weiss nicht
weiter:

/*---------------------------------*/
typedef struct Menu {
  const char *Hdr1;
  const char **Hlp;
} MENU;

const char RomZ  [] PROGMEM = "";
//                            "----+----1----+-"
const char Help0 [] PROGMEM = "HilfeText1";    ;
const char Help1 [] PROGMEM = "HilfeText2";

PGM_P Help [] PROGMEM = { Help0, Help1, RomZ };

const char MenuHdr0 [] PROGMEM = "Auswahl 1";
const char MenuHdr1 [] PROGMEM = "Auswahl 2";

static const MENU MainMenu [] PROGMEM = {
 { MenuHdr0,
   MenuHdr20,
   Help },     <------ main.c 499
 { RomZ }
};
/*---------------------------------*/

avr-gcc -c -mmcu=at90s8535 -I. -g -Os -Wall -Wstrict-prototypes
-std=gnu99 main.c -o uhr2/main.o
main.c:499: warning: initialization discards qualifiers from pointer
target type

von emax (Gast)


Lesenswert?

Gefunden. Ich muss "Help" zu (PGM_P *) casten.

static const MENU MainMenu [] PROGMEM = {
 { MenuHdr0,
   MenuHdr20,
   (PGM_P *) Help },     <------ main.c 499
 { RomZ }
};


Damit, dass ich in einer struct - Deklaration nicht sowas

struct foo
{
  char **s1;  // ok
  char *s3[]; // falsch
}

machen kann, ärgere ich mich jedesmal aufs Neue rum.

Na denn. Alles happy.

e.

von Jörg Wunsch (Gast)


Lesenswert?

> "Das schrob Peter aber schon: ..."

> Das "aber" kling nach Widerspruch. Dabei war das doch exakt das
> Thema meines letzten Beitrages, sogar mit präziser Code-Analyse.

Nur daß Du eben Dinge analysiert hast, die gewissermaßen allgemein
bekannt sind. ;-)

> Der "Bloat" (ich nehme an, Du meinst toten Code) wird in der
Praxis
> aber kaum Auswirkungen haben.

Mit Bloat ist der tote Code gemeint, ja.

Außerdem hast Du halt die zusätzliche Ausführungszeit für das `late
binding' (also die Entscheidung, die erst zur Laufzeit getroffen
wird).

Etwas ähnliches müßtest Du prinzipiell auch mit C++ machen können...

von emax (Gast)


Lesenswert?

"... allgemein bekannt ..."

Das erklärt Deinen Widerspruch erst recht nicht ....

"...die zusätzliche Ausführungszeit für das `late
binding' ..."

Das ist wahr. Die Frage wäre, wieviel das ausmacht. Letztlich ist es
immer die Entescheidung, ob man näher am Komfort und der
Fehlersicherheit ist, oder näher am optimal schlanken Code.

Die Extreme findest Du am Ende in C++ oder noch heftiger: Java. Die
Konsequenzen sind dort am deutlichsten: jeder HiWi kann flugs mal ein
kleines Java-Applet mit grafischer Schnittstelle schreiben, in C ist
das nicht so einfach. In Java kümmert sich ein eigener Prozess ums
Saubermachen (Housekeeping) in C musste das selber machen (ohne jetzt
den grundsätzlichen Unterschied hinsichtlich Java-VM diskutieren zu
wollen).

Im Ergbenis ist C-Code mehrere 100 mal schneller als Java.

Nach unten fortgesetzt findet sich das gleiche (wenn auch in
abgeschwächter Form) im Vergleich C/Assembler wieder.

Je mehr ich selber "von Hand" mache, desto schlanker kann das
Resultat werden. Keine Frage. Vielmehr ist die Frage: was wollen
Compilerbauer erreichen? Was wollen Compilerbenutzer haben?

e.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.