Grüße!
Ich hätte ein Frage zu dem Befehl "extern".
In einigen Header - Dateien hab ich jetzt den Befehl verwendet, meistens
in Verbindung mit einer if - Abfrage, und würde nun gerne wissen was
dieser Befehl genau bewirkt.
Beispiel:
1
#ifdef Dateienname
2
#define EXTERN
3
#else
4
#define EXTERN extern
5
#endif
6
7
//später kommt dann das:
8
9
EXTERNvoidprotocol_data_put(charvariable);
10
EXTERNvoidprotocol_data_get(charvariable);
11
EXTERNunsignedcharvariable;
Jetzt hab ich zwar eine Vermutung wie der Befehl wirkt / was er macht da
ich aber nirgends dazu was gefunden habe würde ich euch gerne um Rat
fragen.
Dank im voraus
Hmpf.
Da muss man weiter ausholen. Ich rate dir dringend zu einem
C-Buch. Da sind solche und ähnliche Dinge ausführlichst erklärt.
Aber was solls.
Zunächst mal musst du 2 Begriffe unterscheiden lernen:
* Definition
* Deklaration
Was ist was?
Kurz gefasst und etwas vereinfacht gesagt, ist eine Definition alles
was im endgültig Programm Speicherplatz verbraucht. Eine Deklaration
hingegen, teilt dem Compiler lediglich zur geschätzten Kenntnisnahme
mit, dass Etwas irgendwo im Programm existiert und wie dieses Etwas
aussieht.
1
intmain()
2
{
3
intj;
4
}
Das 'int j;' ist eine Definition, da der Compiler hier Speicherplatz
reservieren muss, um darin die Variable j unterzubringen. Aber auch
die komplette Funktion main() ist an dieser Stelle eine Definition,
da der Compiler ja dafür Code generieren muss und dieser Code im
fertigen Programm Speicher verbraucht.
Ein anderes Beispiel:
1
voidfoo(void);// <----- A
2
3
intmain()// <----- B
4
{
5
foo();// <----- B1
6
}
7
8
voidfoo()// <----- C
9
{
10
// Irgendwas
11
}
An den markierten Zeilen B und C beginnt jeweils eine Definition,
denn der Compiler muss ja Code für die Funktionen main() und foo()
generieren. Aber was ist das an Zeile A?
Das ist eine Deklaration. Diese Zeile (auch 'Prototyp' genannt)
teilt dem Compiler lediglich mit, dass es irgendwo im Programm
eine Funktion namens foo() gibt. Der Compiler nimmt dies zur
Kenntnis und trägt sich in seine internen Tabellen ein:
Es gibt eine Funktion foo. Diese Funktion liefert kein Ergebnis
und will auch keine Argumente haben.
Wozu braucht der Compiler diese Dekleration?
Er braucht sie um in Zeile B1, den Funktionsaufruf überprüfen zu
können. Der Compiler liest das Programm von oben nach unten durch.
Wenn er an die Zeile B1 stösst, dann hat er die Funktionsdefinition
von foo() noch nicht gesehen. Ohne die Deklaration in A, würde der
Compiler also nicht wissen, dass foo überhaupt existiert. Eine
Fehlermeldung wäre die Folge, denn das könnte ja auch ein Tippfehler
sein. Die Deklaration in A behebt dieses Problem: Dadurch weiss der
Compiler Bescheid, dass es sich um keinen Tippfehler handelt, dass
also foo tatsächlich existiert, dass es keinen Wert zurückliefert
(damit wäre automatisch eine Verwendung in der Form i = foo()
illegal) und das es keine Argumente haben möchte (was jegliche
Verwendung in der Form foo(4); als fehlerhaft qualifiziert).
Das ist alles. Mehr braucht der Compiler nicht um zu überprüfen,
ob der Funktionsaufruf, so wie ihn der Programmierer geschrieben
hat, auch gültig ist.
Soweit so gut. Bei Funktionen kann der Compiler immer zwischen
Definition und Deklaration unterscheiden. Etwas in der Form
1
voidbar()
2
{
3
...
4
}
ist immer eine Definition.
Etwas in der Form
1
voidbar();
ist immer eine Deklaration.
Aber wie ist das mit Variablen?
Nun, da ist das nicht so einfach zu unterscheiden.
1
intj;
könnte beides sein, je nachdem wo und wie es eingesetzt wird.
Um da eine Abhilfe zu schaffen, gibt es das Schlüsselwort 'extern'.
extern wandelt eine Definition zu einer Deklaration.
1
externinta;// Das ist eine Deklaration. Dem Compiler wird
2
// mitgeteilt, dass es irgendwo eine globale
3
// Variable namens 'a' gibt und dass es sich dabei
4
// um einen int handelt
5
6
intb;// Das ist eine Definition. Der Compiler wird
7
// angewiesen, hier und jetzt, an dieser Stelle
8
// eine Variable namens b im Code vorzusehen.
Warum ist nun diese Unterscheidung wichtig?
Weil es in C die sog. 'One definition rule' gibt. Sie besagt
schlicht und einfach, dass jedes Teil, egal ob Variable oder
Funktion nur ein einziges mal definiert werden darf. Es darf
aber beliebig viele Deklarationen darauf geben, solange nur
alle Deklarationen mit der Definition in den Datentypen
übereinstimmen.
Man benutzt das ganze zb so:
In einer *.c Datei, wird eine Variable definiert
1
// main.c
2
inta;
3
intb;
4
5
intmain()
6
{
7
...
8
}
und beliebig viele andere *.c Dateien können sich auf diese
Variable beziehen, indem sie Deklarationen darauf enthalten
1
// foo.c
2
externinta;
3
externintb;
4
5
voidfoo()
6
{
7
...
8
}
Auch umgekehrt wird das Verfahren eingesetzt. Damit aus main.c
heraus die Funktion foo in foo.c aufgerufen werden kann, muss es
eine Deklaration dieser Funktion in main.c geben. Es schadet nichts
bei Funktionsprototypen ebenfalls ein extern zu schreiben, auch
wenn es nichts bewirkt, der Compiler kann ja bei Funktionen immer
erkennen, ob das jetzt eine Definition oder eine Deklaration ist.
1
// main.c
2
inta;
3
intb;
4
5
externvoidfoo(void);// Das extern ist an dieser Stelle nicht
6
// wirklich notwendig. Schadet aber auch
7
// nicht.
8
9
intmain()
10
{
11
foo();
12
}
2 Dinge sind nocht (fürs erste) wichtig:
* Eine Definition ist immer auch eine Deklaration. Das ist auch
logisch, denn durch eine Definition wird dem Compiler ja auch
mitgeteilt wie Dinge aussehen.
1
voidfoo()// Diese Definition ist automatisch auch eine
2
{// Deklaration ....
3
}
4
5
intmain()
6
{
7
foo();// .... damit der Compiler hier die Funktionsparameter
8
// der Funktion foo auch in seinen internen Tabellen
9
// wiederfindet
10
}
* Besitzt eine Variable eine Initialisierung, dann handelt es sich
immer um eine Definition. Im Zweifelsfall wird in so einem Fall
das Schlüsselwort 'extern' ignoriert
1
externinta=5;// Das ist keine Deklaration, sondern eine
2
// Definition
Nachtrag zur ODR (One definition rule):
Wird sie verletzt, dann gibt es eine Fehlermeldung.
Das hier:
1
// main.c
2
inta;
3
4
intmain()
5
{
6
...
7
}
1
// foo.c
2
inta;
3
4
voidfoo()
5
{
6
...
7
}
ist also nicht zulässig, da es 2 Definitionen für a im gesamten
Programm gibt.
genausowenig wäre das hier zulässig (auch wenn es dein Compiler
bzw. Linker nicht prüfen kann):
1
// main.c
2
inta;
3
4
intmain()
5
{
6
...
7
}
1
// foo.c
2
externlonga;
3
4
voidfoo()
5
{
6
...
7
}
Es gibt zwar nur eine Definition für a, aber die Deklaration in
foo.c stimmt nicht mit der Definition überein.
Hab den Artikel grad per Google gefunden und ich muss sagen: Danke Karl
Heinz, sehr geil erklärt. Ich glaub jetzt hab ich das Prinzip auch
geschnallt. Nach K/R besagt extern, das Speicherplatz an anderer Stelle
belegt wird, bei Fkt. wird das also implizit gemacht, durch die
Unterscheidung, dass erklärt noch mal einiges.
Damit kann ich dann wohl endlich den Linkerfehlern begegenen.
Eine Frage hätte ich noch, bewahrt mich eigentlich ein
1
#ifndef HEADER_H
2
# define HEADER_H
3
...
4
#endif
in einer Headerdatei normalerweise wirklich vor doppelten
Definitionen(den Unterschied habe ich nun auch endlich kapiert...), bei
Mehrfacheinbindungen, oder sollte man lieber den Weg über extern gehen?
Also wenn man z.B. statische Arrays mit nem String hat, die in einer
File genutzt werden, dann werden die Strings, so im Header, ja auch
wieder neu zugewiesen, bzw die Arrays ohnehin neu definiert. Mit dem
Schalter würde das theoretisch nicht passieren, oder? Aber was ist mit
den Deklarationen, sind die dann im zweiten einbindenden *.c trotzdem
bekannt?
An der Stelle noch die Anmerkung, wer KEIL mit Realview benutzt, da
funktioniert das mit dem Schalter nicht. Dort wird ein definiertes Label
für jede neue *.c erstellt. Die Workarounds kann man sich hier
http://www.realview.com*dot*cn/support/kb.asp?ID=834 anschauen(dot
durch nen punkt ersetzen ersetzen, wurde komischerweise als Spam
erkannt, isses aber nich). Die erste Möglichkeit funzt bei mir nicht,
deswegen mach ich es jetzt ordentlich. Ich wollte es nur anmerken, falls
sich noch andere wundern.
Icke Muster wrote:
> Eine Frage hätte ich noch, bewahrt mich eigentlich ein>
1
>#ifndefHEADER_H
2
>#defineHEADER_H
3
>...
4
>#endif
5
>
> in einer Headerdatei normalerweise wirklich vor doppelten> Definitionen(den Unterschied habe ich nun auch endlich kapiert...),
Nein, das bewahrt dich nicht davor.
Diese Include Guards bewahren dich nur vor Problemen, die
bei der Übersetzung eines Source Code Files entstehen.
Wenn dein Programm aber aus mehreren *.c Files besteht, dann
hilft dir der Include Guard nichts, weil ja jedes *.c File
für sich alleine übersetzt wird.
Worum gehts bei den Include Guards?
Wenn Programme größer werden, dann kommt es schon mal vor, dass
ein Header File in ein *.c File mehrfach includiert wird.
Das passiert natürlich nicht so:
1
#include"foo.h"
2
#include"foo.h"
3
#include"foo.h"
4
5
intmain()
6
{
7
}
sondern etwas subtiler zb so:
1
// File: main.c
2
#include"test1.h"
3
#include"test2.h"
4
5
intmain()
6
{
7
}
1
// File: test1.h
2
#include"globals.h"
3
4
intInputBuffer[MAX_INPUT];
5
// noch andere Dinge
1
// File: test2.h
2
#include"globals.h"
3
4
intOutputBuffer[MAX_OUTPUT];
5
// noch andere Dinge
1
// File: globals.h
2
3
#define MAX_INPUT 10
4
#define MAX_OUTPUT 20
Hier wird, wenn main.c compiliert wird, die Datei globals.h zweimal
includiert. Einmal über den Umweg über File1.h und einmal über File2.h
Einmal den Text in globals.h einbinden reicht aber. Und genau dafür
sorgt der Include Guard. Wenn globals.h das erste mal includiert wird,
dann ist das Makro noch nicht definiert, er #ifndef schlägt nicht fehl
und der weitere Rest des Files wird abgearbeitet. Unter anderem enthält
diese Abarbeitung die Definition genau dieses Makronamens.
Wenn der Präprozessor dann das zweite mal den include durchführt
dann bindet er genauso den Quelltext ein, aber diesmal geht der
#ifndef schief, denn ein Makro mit diesem Namen gibt es ja bereits.
Es wurde erzeugt als dieses Header File das erste mal eingebunden
wurde. Dadurch das der #ifndef aber fehl schlägt, wird dann der
Rest dieser Datei nicht mehr eingebunden.
> Mehrfacheinbindungen, oder sollte man lieber den Weg über extern gehen?
Sollte ist der falsche Ausdruck. Du musst
Es gibt keinen anderen Weg, ausser einer compilerspezifischen
Erweiterung, so dass sich der Linker nicht mehr daran stört.
> Also wenn man z.B. statische Arrays mit nem String hat, die in einer> File genutzt werden, dann werden die Strings, so im Header, ja auch> wieder neu zugewiesen, bzw die Arrays ohnehin neu definiert. Mit dem> Schalter würde das theoretisch nicht passieren, oder? Aber was ist mit> den Deklarationen, sind die dann im zweiten einbindenden *.c trotzdem> bekannt?> An der Stelle noch die Anmerkung, wer KEIL mit Realview benutzt, da> funktioniert das mit dem Schalter nicht. Dort wird ein definiertes Label> für jede neue *.c erstellt.
Ich denke du hast eine etwas falsche Vorstellung wie das funktioniert.
Jedes einzelne *.c File wird für sich alleine compiliert.
Wenn du also in deinem Gesammtprogramm ein a.c und ein b.c hast,
dann wird a.c compiliert. Die Include Guards greifen und verhindern,
dass bei der Mehrfachinclusion bei der Compilierung von a.c
irgendetwas abwegiges passiert.
Aber dann wird b.c compiliert. Der Compiler geht aber wieder mit
jungfräulichem Wissen an diese Compilierei. Was immer er auch
durch das compilieren von a.c gelernt wurde, es wird alles verworfen
und wieder bei 0 angefangen. Dass muss auch so sein. Denn eine
der Designprinzipien von C war es, dass jede Übersetzungseinheit
(so nennt man ein *.c) für sich alleine compiliert werden kann
und zwar unabhängig davon, was vorher oder nachher compiliert wird.
Bei 1000 *.c Files möchte ich schliesslich nur die neu compiliert
haben, in denen es auch eine Änderung gab und nicht alle neu,
nur weil der Compiler aus dem compilieren von a.c Wissen erlangt
hat, das er für b.c benötigt.
Ich will jetzt nicht Verwirrung stiften, aber es gibt offenbar Compiler,
welche Modul-Übergreifend compilieren. Der Sinn davon ist eine bessere
Optimierung.
z.B.:
http://www.htsoft.com/products/OCG.php
Aber dies ändern nichts an den sehr guten Ausführungen von Karl heinz.
Severino R. wrote:
> Ich will jetzt nicht Verwirrung stiften, aber es gibt offenbar Compiler,> welche Modul-Übergreifend compilieren. Der Sinn davon ist eine bessere> Optimierung.> z.B.:> http://www.htsoft.com/products/OCG.php
Dagegen ist auch nichts zu sagen. Der Compiler kann machen
was er will, solange er die 'As if' Regel einhält. Das heist
er kann nach Herzenslust herumoptimieren, solange sich das
Programm insgesammt noch immer so verhält, wie es der C-Standard
fordert.
Ok, alles klar, dann war ich wohl mächtig auf dem Holzweg. Mir ist
eigentlich schon klar, dass alle einzeln übersetzt werden können, wenn
du es jetzt so sagst, weiß ich auch nich genau, was ich mir dabei
gedacht hab. Dann verhält sich auch RealView völlig normal. Also machen
in einer Headerdatei nur Variablen Sinn, die man global benutzen möchte
und vll noch defines, die zu Konfigurationszwecken dienen.
Methodenköpfe, der Übersicht wegen, aber mehr eigentlich nicht, oder? Es
macht ja eigentlich auch keinen Sinn z.B. eine int Variable im Header
nur zu deklarieren, wenn man sie am Anfang des C-Files eh initialisiert
und damit implizit deklariert und definiert. Oder seh ich das falsch?
Sorry für die vermeintlich blöden Fragen, aber ich hab scheinbar an der
Stelle noch keinen richtigen Zugang.
Icke Muster wrote:
> Also machen> in einer Headerdatei nur Variablen Sinn, die man global benutzen möchte> und vll noch defines, die zu Konfigurationszwecken dienen.> Methodenköpfe, der Übersicht wegen, aber mehr eigentlich nicht, oder?
Strukturdefinitionen, Typedefs, Enums.
Ich seh das so:
Jedes *.c File (manchmal auch mehrere davon) bildet ein 'Modul'.
Ein Modul ist eine 'Baugruppe' die ein Problem löst.
Für diese Baugruppe gibt es ein Header File, in dem alles
drinnen steht, was ein anderer Programmierer benötigt um mit
dieser Baugruppe arbeiten zu können.
Dinge, die nur für ein spezifisches *.c interessant sind, gehören
nicht ins Header File, sondern werden direkt im *.c definiert.
Beispiel:
Ein Modul für einen Ringbuffer.
So ein Ringbuffer benötigt ja eine Speicherfläche auf der er
operieren kann. Ins Header File oder ins Source File?
Von aussen soll und darf sich niemand an diesem Buffer vergreifen.
Ganz im Gegenteil, das hat niemanden zu ineterssieren, wie ich diesen
Buffer verwalte. Daher ganz klar ins Source File. Im Header File
stehen nur die Dinge, die jemand wissen muss, der mit diesem
Modul arbeitet. Das sind in erster Linie natürlich die Prototypen für
die Funktionen, die dieses Modul anbietet:
Da das Ringbuffer-Modul keinerlei eigene Datentypen vorhält, über die
der aufrufende Code etwas wissen müsste und die als Funktionsargument
irgendwo vorkommen, ist damit das Header File schon fertig.
Ein anderer Programmteil, weilcher den Ringbuffer benutzen möchte,
includiert einfach nur das Header File
1
#include"Ringbuffer.h"
2
3
intmain()
4
{
5
EnqueueInt(5);
6
EnqueueInt(7);
7
...
8
}
und kann damit die Funktionen benutzen. Und das (die Funktionen) ist
alles was er von meinem Ringbuffer Modul wissen muss. Im speziellen muss
er nicht wissen, dass ich die Zahlen die er mir übergibt in einem Array
namens 'Buffer' ablege. Muss er nicht wissen und weiß er auch nicht.
Denn im Header File ist dieses Array ja nicht enthalten.
> macht ja eigentlich auch keinen Sinn z.B. eine int Variable im Header> nur zu deklarieren, wenn man sie am Anfang des C-Files eh initialisiert> und damit implizit deklariert und definiert.
Du meinst, wenn diese Variable keine Globale ist?
In dem Fall hat sie in einem Header File nichts verloren.
Karl heinz Buchegger wrote:
> Ich seh das so:> Jedes *.c File (manchmal auch mehrere davon) bildet ein 'Modul'.> Ein Modul ist eine 'Baugruppe' die ein Problem löst.> Für diese Baugruppe gibt es ein Header File, in dem alles> drinnen steht, was ein anderer Programmierer benötigt um mit> dieser Baugruppe arbeiten zu können.>> Dinge, die nur für ein spezifisches *.c interessant sind, gehören> nicht ins Header File, sondern werden direkt im *.c definiert.
Das sehe ich ganz genau so, Karl Heinz. Aber solltest du das #define für
die Puffergröße nicht lieber in die Header-Datei tun? Sonst kann der
externe Programmierer ja gar nicht die Speichergröße verändern.
Simon K. wrote:
> Das sehe ich ganz genau so, Karl Heinz. Aber solltest du das #define für> die Puffergröße nicht lieber in die Header-Datei tun? Sonst kann der> externe Programmierer ja gar nicht die Speichergröße verändern.
Das kann man so machen. Ja.
Ich kann aber auch die andere Auffassung vertreten und sagen
dass diese Speichergröße fix ist. Das wäre zb. angebracht, wenn
dieses Ringbuffer Modul Teil einer Library ist. Denn dann bringt
es ja klarerweise nichts, wenn der Anwendungsprogrammierer zwar
den #define ändert und auch mit einer geänderten Buffergröße
rechnet aber die Library nicht neu gebaut hat.
Zb. sind die diversen Anfragen legendär, die sich damit
beschäftigen, warum der Compiler eine Veränderung von RAND_MAX
nicht korrekt umsetzt :-)
Langer Rede kurzer Sinn: Natürlich gibt es auch Grauzonen. Das
ist überhaupt etwas was man akzeptieren muss: Es gibt keine
100% Regeln in der Programmierung. Es kann, wird und soll
auch immer die Grauzone geben, die man so oder so sehen kann
und die man je nach konkreten Anforderungen dehnen kann.
Ist für mich einer der Gründe, warum Programmierung so schwer
ist. Neulinge habn gerne fixe Regeln: Zuerst machst du dieses,
dann machst du jenes und wenn du dich stur an dieses Kochrezept
hältst, dann hast du am Ende ein fehlerfreies Programm.
Nur leider spielts das in der Praxis nicht.
Was nicht heissen soll, dass es nicht Grundsätze gibt, die sich
bewährt haben und die sinnvoll sind. Man muss aber immer auch
einen gewissen Freiraum zulassen. Programmieren hat (auf diesen
Aspekt reduziert) sehr viel mit Kunst gemeinsam.
Karl heinz Buchegger wrote:
> Ich kann aber auch die andere Auffassung vertreten und sagen> dass diese Speichergröße fix ist. Das wäre zb. angebracht, wenn> dieses Ringbuffer Modul Teil einer Library ist. Denn dann bringt> es ja klarerweise nichts, wenn der Anwendungsprogrammierer zwar> den #define ändert und auch mit einer geänderten Buffergröße> rechnet aber die Library nicht neu gebaut hat.
Gut, das kann man so sehen. Ich dachte jetzt eher an einen Fall, wo das
Ringbuffer Modul beispielsweise für das UART in einem AVR benutzt wird.
Bei so einem Projekt wird ja eine veränderte Ringbuffer.h/c direkt neu
kompiliert. (Im Gegensatz zu einer festen Library)
> Zb. sind die diversen Anfragen legendär, die sich damit> beschäftigen, warum der Compiler eine Veränderung von RAND_MAX> nicht korrekt umsetzt :-)> Langer Rede kurzer Sinn: Natürlich gibt es auch Grauzonen. Das> ist überhaupt etwas was man akzeptieren muss: Es gibt keine> 100% Regeln in der Programmierung. Es kann, wird und soll> auch immer die Grauzone geben, die man so oder so sehen kann> und die man je nach konkreten Anforderungen dehnen kann.
Absolut.
> Ist für mich einer der Gründe, warum Programmierung so schwer> ist. Neulinge habn gerne fixe Regeln: Zuerst machst du dieses,> dann machst du jenes und wenn du dich stur an dieses Kochrezept> hältst, dann hast du am Ende ein fehlerfreies Programm.
Sehe ich (fast) jeden Tag in der Schule. Fürs Programmieren braucht man
halt eine bestimmte (erleichternde) Denkweise.
> Was nicht heissen soll, dass es nicht Grundsätze gibt, die sich> bewährt haben und die sinnvoll sind.
Glücklicherweise gibt es die. Das fängt ja schon damit an Variablen
geeignet zu prefixieren. Oder Techniken um Algorithmen zu
implementieren.
> Man muss aber immer auch> einen gewissen Freiraum zulassen. Programmieren hat (auf diesen> Aspekt reduziert) sehr viel mit Kunst gemeinsam.
Das sehe ich übrigens ganz genauso, leider ließ sich bis jetzt kaum
jemand davon überzeugen. Das ist dann direkt Kunst-Blasphemie sowas zu
behaupten...
@Karl: Danke, bin auch gerade über Google dazugestoßen - auf der suche
nach verständlichen Erklärungen erklärungen für extern.
Danke für die Hilfte!
lg Clemens
Ich bin soeben per Google auf diese Seite gestoßen und als nicht Neuling
im Programmieren habe ich endlich vollends begriffen wie sich
Definitionen und Deklarationen unterscheiden. Karl Heinz hat das
wirklich super erklärt und auch bestimmt einige zeit hier gelassen. Ich
bedanke mich dafür.
> #ifdef Dateienname> #define EXTERN> #else> #define EXTERN extern> #endif>> //später kommt dann das:>> EXTERN void protocol_data_put(char variable);> EXTERN void protocol_data_get(char variable);> EXTERN unsigned char variable;
Leider ist diese Coding-Style Sünde immer noch im Tutorial :-(
Bitte reduzieren Sie die Anzahl der Zitatzeilen.
Ich möchte mich auch herzlichst bedanken für die wirklich guten
Erklärungen.
Bin durch Zufall hier gelandet, weil ich das "Extern" nicht richtig
verstanden habe.
TOP Erklärungen in diesem Thread von Karl Heinz.
Vielen Dank
@kbuchegg: Die Erklärungen sind wirklich fantastisch!
Selbst in 2018 ist dies immer noch die beste Erklärung, die ich zu dem
Thema finden konnte.
Vielen Dank für die Mühe, solch ausführliche Texte zu schreiben!