Hallo zusammen,
ich versuche gerade mich vom Atmel Studio zu lösen und unter Linux via
make meine Programme zu übersetzen. Dabei stosse ich auf ein Problem.
Ich habe ein paar Funktionen als inline definiert. Bei dem Aufruf einer
solchen Funktion schmeisst mir avr-gcc nen Fehler. Beispiel:
1
#include<avr/io.h>
2
3
inlinevoidfoo()
4
{
5
//Irgendwelcher Code
6
}
7
8
intmain()
9
{
10
foo();
11
return0;
12
}
Das ganze kompiliere ich mit:
1
avr-gcc main.c -mmcu=atmega32
Daraufhin meldet mir der Compiler:
1
/tmp/cc2pO2mP.o: In function `main':
2
main.c:(.text+0x8): undefined reference to `foo'
Warum?
verwenden tu ich den avr-gcc in Version 5.3.0
Grüße
Higg
foo ist extern. Für den Fall, dass foo nicht geinlint wird(*) musst du
eine Implementierung für foo zur Verfügung stellen.
Gründe, eine Funktion nicht zu inlinen, können u.a. sein:
• Inlining ist zu teuer
• Optimierung oder Inlining ist deaktiviert
• Es wird die Adresse der Funktion genommen
• Die Funktion nimmt die Adresse lokaler Labels
"inline" gibt es erst seit C99. GCC akzeptiert "inline" auch bei
älteren C-Versionen falls nicht im strikt ANSI o.ö. Mode übersetzt wird.
Dieses "inline" hat aber eine andere Semantik als das "inline" von C99.
Falls du dieses alte "inline" meinst: Das gibt's in den neuen Versionen
als gnu_inline.
Falls die Funktion immer geinlint werden soll, dann nimm
1
staticinline__attribute__((__always_inline__))
Falls du die alte Semantik von inline willst, dann
Vielen dank soweit. Die zusätzliche static-Angabe scheint zu
funktionieren. Wenn ich ehrlich bin, habe ich den Grund jedoch nicht
verstanden. Werde mich mit der Doku die nächsten Tage mal beschäftigen.
Vielleicht kommt dann die Erleuchtung.
Higg G. schrieb:> Wenn ich ehrlich bin, habe ich den Grund jedoch nicht verstanden.
Ohne „static“ ist es implizit „extern“, und dann sieht die C99-Semantik
vor, dass die externen (nicht-inline) Variante zusätzlich zur
inline-Variante bereitzustellen ist. Die müssen dann auch nicht
unbedingt das gleiche tun. :-/
Allerdings ist die Info des Linkers natürlich ein Hinweis drauf, dass
der Compiler das nicht geinlinet hat.
Das ist ja eine merkwürdige Semantik in C. Das heißt, je nachdem wie der
Compiler optimiert, kompiliert das Programm (bzw. ist der Linker
zufrieden) oder nicht? :o
Da finde ich die Bedeutung in C++ viel praktischer: Man darf non-static
Inline-Funktionen in mehreren Translation-Units definieren, es muss aber
immer die exakt gleiche Definition sein. Im Klartext, man darf sie in
ein Header-File schreiben und es überall inkludieren. Falls der Compiler
die Funktion nicht inlinen kann, kümmert er sich selber drum, dass eine
Standalone-Version erzeugt wird. Die hat dann sogar im gesamten Programm
die gleiche Adresse, sie wird also nicht in jeder Translation-Unit
dupliziert.
Jörg W. schrieb:> Higg G. schrieb:>> Wenn ich ehrlich bin, habe ich den Grund jedoch nicht verstanden.>> Ohne „static“ ist es implizit „extern“, und dann sieht die C99-Semantik> vor, dass die externen (nicht-inline) Variante zusätzlich zur> inline-Variante bereitzustellen ist.
Aber genau das passiert hier nicht. Es ist fast so, als ob der Compiler
die Definition von foo() sofort komplett verwirft, nur weil es nicht
inline aufgelöst wird. Eine extern-Version wird gar nicht erstellt, ist
auch im generierten Assembler-Code nicht zu finden. Schreibt man (bei
-O0) static davor, generiert er die Funktion, aber natürlich nicht als
extern.
> Die müssen dann auch nicht unbedingt das gleiche tun. :-/>> Allerdings ist die Info des Linkers natürlich ein Hinweis drauf, dass> der Compiler das nicht geinlinet hat.
Warum generiert er denn nicht die nicht-inline-Version?
5 A function declared with an inline function specifier is an inline
4
function. The function specifier may appear more than once; the behavior
5
is the same as if it appeared only once. Making a function an inline
6
function suggests that calls to the function be as fast as possible.
7
The extent to which such suggestions are effective is implementation-defined.
8
9
6 Any function with internal linkage can be an inline function. For a
10
function with external linkage, the following restrictions apply: If a
11
function is declared with an inline function specifier, then it shall also
12
be defined in the same translation unit. If all of the file scope
13
declarations for a function in a translation unit include the inline
14
function specifier without extern, then the definition in that translation
15
unit is an inline definition. An inline definition does not provide an
16
external definition for the function, and does not forbid an external
17
definition in another translation unit. An inline definition provides an
18
alternative to an external definition, which a translator may use to
19
implement any call to the function in the same translation unit.
20
It is unspecified whether a call to the function uses the inline definition
21
or the external definition.
1) Mit extern oder static ist die Semantik mit und ohne inline gleich.
2) Ob für einen Aufruf eine inline "Kopie" eingefügt wird hängt vom
Compiler ab, z.B. von den Kosten oder dem Optimierungsgrad.
"inline" ist eine höfliche Bitte and den Compiler.
3) Wenn weder extern noch static angegeben ist, hat die Funktion
(bzw. der entsprechende Identifier) "internal" Linkage, und
"An inline definition does not provide an external definition
for the function."
Johann L. schrieb:> 1) Mit extern oder static ist die Semantik mit und ohne inline gleich.> "inline" ist eine höfliche Bitte and den Compiler.
Dachte ich auch immer.
> 2) Ob für einen Aufruf eine inline "Kopie" eingefügt wird hängt vom> Compiler ab, z.B. von den Kosten oder dem Optimierungsgrad.
Das ist klar. Es ging aber darum, ob der Compiler eine
nicht-Inline-Version generiert. Das tut er offenbar auch dann nicht,
wenn er sie eigentlich braucht.
> 3) Wenn weder extern noch static angegeben ist, hat die Funktion> (bzw. der entsprechende Identifier) "internal" Linkage, und> "An inline definition does not provide an external definition> for the function."
Es ging eigentlich nicht um externe Definitionen, sondern um folgenden
Code:
Higg G. schrieb:
1
#include<avr/io.h>
2
3
inlinevoidfoo()
4
{
5
//Irgendwelcher Code
6
}
7
8
intmain()
9
{
10
foo();
11
return0;
12
}
Und das steht alles in einer Datei. Wenn Optimierung ausgeschaltet
ist, bringt gcc im C99-Modus beim Aufruf von foo() die Fehlermeldung,
dass diese Funktion nicht definiert sei. Wie schon gesagt wird keine
non-Inline-Version generiert.
Compiliert mit
Wie man sieht, wird foo() zwar aufgerufen, aber es wird keinerlei Code
für die Implementation generiert.
Nur mit eingeschalteten Optimierungen, also wenn die Funktion
tatsächlich inline aufgleöst wird, läßt sich die Datei compilieren.
Alternativ funktioniert es auch, wenn man die Funktion "static inline"
definiert. Dann wird nämlich lustigerweise eine non-Inline-Version
generiert, wenn nötig. Folgender Code kommt dabei raus (übersetzt mit
der gleichen gcc-Kommandozeile wie oben):
Rolf M. schrieb:> Es ging eigentlich nicht um externe Definitionen, sondern um folgenden> Code:
Den Code kenn ich wohl. Und weil es KEINE extern ist und auch KEINE
static, wird eben KEINE Implementierung erzeugt. Der Aufruf
> foo();> call foo
Hat deshalb external Linkage, und weil offenbar kein solches Symbol
definiert wurde, kann der Linker es nicht auflösen.
Oder wo siehst du eine extern oder static?
> Wie man sieht, wird foo() zwar aufgerufen, aber es wird keinerlei Code> für die Implementation generiert.
Siehe oben:
> 3) Wenn weder extern noch static angegeben ist, hat die Funktion> (bzw. der entsprechende Identifier) "internal" Linkage, und> "An inline definition does not provide an external definition> for the function."
Johann L. schrieb:>> Wie man sieht, wird foo() zwar aufgerufen, aber es wird keinerlei Code>> für die Implementation generiert.>> Siehe oben:>>> 3) Wenn weder extern noch static angegeben ist, hat die Funktion>> (bzw. der entsprechende Identifier) "internal" Linkage, und>> "An inline definition does not provide an external definition>> for the function."
Die Funktion hat in diesem Fall aber gar keine Linkage, da vom Compiler
überhaupt kein Code dafür erzeugt wurde, weder intern noch extern. Nur
wenn ich static angebe, wird die Funktion tatsächlich mit interner
Linkage erzeugt. Dass inline dazu führt, dass die Funktion keine externe
Definition mehr hat, ist schon klar, aber intern sollte sie doch
trotzdem noch da sein.
Und als Schlussfolgerung ergibt dann das für mich auch keinen Sinn:
Johann L. schrieb:> Der Aufruf>>> foo();>> call foo>> Hat deshalb external Linkage, und weil offenbar kein solches Symbol> definiert wurde, kann der Linker es nicht auflösen.
Weil die Funktion inline definiert ist, sucht der Compiler beim Aufruf
nicht intern nach ihr, sondern extern? Und wenn ich Optimierungen
einschalte, findet er sie dann doch auf einmal intern?
Rolf M. schrieb:> Das ist klar. Es ging aber darum, ob der Compiler eine> nicht-Inline-Version generiert. Das tut er offenbar auch dann nicht,> wenn er sie eigentlich braucht.
Der Punkt ist wohl, dass der Compiler beim Übersetzen dieser C-Datei
davon ausgeht, dass es in einer anderen Übersetzungseinheit eine
Non-Inline-Version gibt, die er aufrufen kann. Damit spart er sich das
Duplizieren der Non-Inline-Version in jeder Übersetzungseinheit.
Diese Non-Inline-Version muss man als Programmierer selber in genau
einer Übersetzungseinheit definieren, wie bei jeder anderen globalen
Funktion oder Variable auch. Bei C++ können Compiler und Linker das
dagegen selber hinbekommen, dass im fertigen Programm nur genau eine
Non-Inline-Version bleibt.
Nur wenn man static davor schreibt, sagt man dem Compiler, dass die
Duplizierung der Non-Inline-Funktion in jeder Übersetzungseinheit
gewünscht ist, sofern nötig.
Mir ist auch grad klar geworden, dass das hier wohl der wesentliche
Punkt ist:
Johann L. schrieb:> It is unspecified whether a call to the function uses the inline> definition or the external definition.
Der Compiler darf sich also dazu entscheiden, statt der schon in der
selben Übersetzungseinheit definierten Funktion alternativ nach einer
externen Definition zu suchen. Das ist nicht unbedingt intuitiv: Ich
definiere eine Funktion, rufe sie direkt danach auf, und der Compiler
sagt, sie sei nicht definiert, weil er ganz wo anders danach sucht...
Hans schrieb:> Der Punkt ist wohl, dass der Compiler beim Übersetzen dieser C-Datei> davon ausgeht, dass es in einer anderen Übersetzungseinheit eine> Non-Inline-Version gibt, die er aufrufen kann. Damit spart er sich das> Duplizieren der Non-Inline-Version in jeder Übersetzungseinheit.
Das könnte sein. So würde es am ehesten Sinn ergeben.
> Diese Non-Inline-Version muss man als Programmierer selber in genau> einer Übersetzungseinheit definieren, wie bei jeder anderen globalen> Funktion oder Variable auch.
Das heißt aber, daß man die Funktion in diesem Fall wirklich zweimal
hinschreiben müßte, einmal für den Fall, dass sie inline aufgelöst wird
und einmal als extern-Funktion für den anderen Fall, damit der Compiler
sich dann raussuchen kann, welche von den beiden er tatsächlich nimmt.
> Bei C++ können Compiler und Linker das dagegen selber hinbekommen, dass> im fertigen Programm nur genau eine Non-Inline-Version bleibt.
Ja. Ich dachte bislang, das sei in C auch so, aber dieser Automatismus
fehlt hier wohl.
> Nur wenn man static davor schreibt, sagt man dem Compiler, dass die> Duplizierung der Non-Inline-Funktion in jeder Übersetzungseinheit> gewünscht ist, sofern nötig.
Im Zitat aus der Norm oben steht aber nichts davon, dass static da
irgendwas ändert. Die Funktion hat ja ohne static schon internal
linkage. Also sollte das durch das static doch an sich gleich bleiben.
Dürfte der Compiler also theoretisch immer noch nach einer externen
Definition suchen?
Wenn ich es richtig verstehe, hat eine Funktion mit static grundsätzlich
internal Linkage und eine Funktion ohne static grundsätzlich external
Linkage.
Für internal Linkage (d.h. mit static) sind die Regeln einfach:
1
Any function with internal linkage can be an inline function.
Nur für external Linkage (d.h. ohne static) ist es komplizierter:
1
For a function with external linkage, the following restrictions apply:
Da gilt: Eine "inline definition" (d.h. mit inline aber ohne extern) ist
keine "external definition". Es darf aber eine externe Definition in
einer anderen Übersetzungseinheit geben.
1
If all of the file scope
2
declarations for a function in a translation unit include the inline
3
function specifier without extern, then the definition in that translation
4
unit is an inline definition. An inline definition does not provide an
5
external definition for the function, and does not forbid an external
6
definition in another translation unit.
Das heißt, es wird hier keine Funktion mit external Linkage definiert,
sondern nur deklariert. Erst mit "extern" wird es eine externe
Definition statt einer Deklaration.
Hans schrieb:> Das heißt, es wird hier keine Funktion mit external Linkage definiert,> sondern nur deklariert. Erst mit "extern" wird es eine externe> Definition statt einer Deklaration.
Hmm, ok. Die Definition hat dann wohl tatsächlich keine Linkage, wenn
weder static, noch extern angegeben ist. Das wird ja auch bestärkt durch
das:
> An inline definition provides an alternative to an external definition,> which a translator may use to implement any call to the function in the> same translation unit.
Die inline-definierte Funktion (ohne static oder extern) hat also gar
keine normalen Linkage-Regeln, sondern ist nur ein optionaler Ersatz,
für den sich der Compiler statt der "eigentlichen" extern-definierten
Funktion entscheiden kann. Damit ist also nicht - wie ich bisher dachte
- nur das Schlüsselwort inline selbst ein optionaler Hinweis an den
Compiler, sondern die ganze Funktion, die damit definiert ist.
Das passt dann auch dazu, dass sich das Verhalten mit static ändert,
denn dann bekommt die Funktion tatsächlich internal linkage und kann
somit immer vom Compiler gefunden werden.
Lustigerweise lässt der Compiler zwar zu, die extern-Funktion in einer
anderen Übersetzungseinheit zu definieren, aber nicht in der selben. Das
führt dazu, dass zwar obiger Code mit dem Fehler abbricht, dass die
Funktion nicht defniert ist, aber der folgende dafür mit dem Fehler,
dass sie zweimal definiert ist (was ja an sich auch stimmt):
1
#include<stdio.h>
2
3
voidfoo(void)
4
{
5
printf("Keine inline-Funktion\n");
6
}
7
8
inlinevoidfoo(void)
9
{
10
printf("inline-Funktion\n");
11
}
12
13
intmain()
14
{
15
foo();
16
return0;
17
}
Ich gebe dir also voll und ganz recht: Die Art, wie es in C++
spezifiziert ist, ist wesentlich sinnvoller und einleuchtender.