mikrocontroller.net

Forum: Compiler & IDEs Funktionszeiger im struct in C


Autor: NoIdea (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe ein Verständnisproblem mit Funktionszeigern evtl. hackt es auch 
wo anders (bin in C nicht wirklich Sattelfest). Da ich modular 
programmieren und bereits programmierte Teile wiederverwenden möchte, 
hab ich mir folgendes überlegt: Es wäre doch toll, wenn ich einen 
Datentyp für eine Bsp. UART erstelle, der die zugehörigen Variablen und 
Funktionen mitbringt (ähnlich wie Klassen in der objektorientierten 
Programmierung).

Beispiel:
typedef void (*VoidFnct)( char [] );

struct
{
   char buffer[10] = "         ";
   VoidFnct send = uart_send;
}uart;

void uart_send(char [] to_send)
{
    // TODO senderoutine
}

int main ()
{
    uart.send("hallo");
}

Wie ihr seht möchte ich, dass über die Struktur auf die Funktionen 
zugegriffen wird. Wichtig ist auch, dass nicht erst der Funktionspointer 
initialisiert werden muss, nachdem eine Instanz der Struktur angelegt 
wurde. Mein Problem ist, wie mach ich das dem Compiler klar?

Ich hoffe ihr versteht was ich meine, sonst muss ich versuchen mein 
Problem anders zu schildern.

Grüzi, Willi

Autor: sous (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Willi,
Du schreibst: "... der die zugehörigen Variablen und
Funktionen mitbringt (ähnlich wie Klassen in der objektorientierten
Programmierung)."

warum programmierst Du es denn nicht einfach objektorientiert in C++ 
statt in C?

Autor: NoIdea (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Darüber habe ich auch nachgedacht. Ich habe da noch ein Posting im Kopf, 
in dem es hiess dass ein grosser Overhead erzeugt wird und für kleine 
8-Bitter nicht geeignet wäre. Wenn es stimmen sollte, fiele c++ dann aus 
dem Rennen.

Willi

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du die Instanz wie oben anlegst, wird die Funktionsadresse vom 
Compiler zur Compilezeit in die Struktur eingetragen. Wo genau ist das 
Problem?

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
struct
{
   char buffer[10] = "         ";
   VoidFnct send = uart_send;
}uart;

Das Prinzip ist zwar recht weit verbreitet und macht auch oft Sinn. Aber 
über denselben hast du dich ja schon gefragt, als du überlegt hast, mit 
C++ zu arbeiten.
Wenn sich diese Funktionen später wirklich mal ändern, ist der Ansatz in 
Ordnung. Ansonsten würd ich aber dazu tendieren, in der Struktur nur 
Daten aufzubewahren und dann halt einen Satz von Funktionen zu 
erstellen, ohne diese auch per Zeiger in der Struktur zu verewigen. Das 
braucht nur unnötig Speicher in dem Fall.

Autor: NoIdea (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Stefan:

Hast recht so funktioniert es auch. Fängt man jedoch an alles in 
verschiedene Files auszulagern, so fangen die Probleme an.

Beispiel:

uart.c
#include "uart.h"

struct
{
   char buffer[10] = "         ";
   VoidFnct send = uart_send;
}uart;

void uart_send(char [] to_send)
{
    // TODO senderoutine
}

uart.h
#ifndef UART_H
#define UART_H

#include "common.h"

typedef void (*VoidFnct)( char [] );

#endif

main.c
#include "common.h"

int main ()
{
  uart.send("hello");
  while(1);
}

common.h
#ifndef COMMON_H
#define COMMON_H

#include <avr/io.h>

#endif

Versuch zu kompilieren (WinAVR mit AVR Studio) mit Fehlermeldung:
../main.c:5: error: 'uart' undeclared (first use in this function)

Wie bringe ich jetzt dem Compiler bei, dass uart existiert?

Wie erwähnt möchte ich eine Pointerinitialisierung, wenn eine Instanz 
der Struktur angelegt wurde, vermeiden.

Willi

Autor: holzmagnet (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hi
#include "uart.h"

extern struct
{
   char buffer[10] = "         ";
   VoidFnct send = uart_send;
}uart;

void uart_send(char [] to_send)
{
    // TODO senderoutine
}

das könnte evtl. weiterhelfen. Mit extern machst du die Sache 
öffentlich. Kanns aber leider net testen. Probiers ma aus.

cya

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
holzmagnet wrote:
> hi
>
> #include "uart.h"
> 
> extern struct
> {
>    char buffer[10] = "         ";
>    VoidFnct send = uart_send;
> }uart;
> 
> void uart_send(char [] to_send)
> {
>     // TODO senderoutine
> }
> 
>
> das könnte evtl. weiterhelfen. Mit extern machst du die Sache
> öffentlich. Kanns aber leider net testen. Probiers ma aus.

Das dürfte eigentlich nicht funktionieren. In dem Moment
in dem da eine Initialisierung ist, ist das Ganze eine Definition
und keine Deklaration mehr (das extern wird in dem Fall quasi
ignoriert). Das einzige was einen dann noch rettet, ist das
Ignorieren der 'One Definition Rule' im gcc-Linker.

Im Übrigen schliesse ich mich sous bzw. Sven an.
Es macht nicht wirklich Sinn, objektorientierte Konzepte aus
C++ 1:1 nach C übernehmen zu wollen. In C hast du nun mal das
Gegenstück zu einem Konstruktor nicht. Eine Initialisierung ist
da nur ein müder Ersatz dafür.

> Ich habe da noch ein Posting im Kopf,
> in dem es hiess dass ein grosser Overhead erzeugt wird

Nicht wirklich. Kommt immer darauf an, was man aus C++ benutzt.
Letztendlich implementiert ein C++ Compiler das von dir gewünschte
Verhalten (virtuelle Funktionen) auch nicht anders, nur macht er
es ein bischen cleverer, indem nicht jedes Objekt eine Tabelle
von Funktionszeigern beeinhaltet, sondern dafür eine eigene
Struktur abgestellt wird und im Objekt ein Pointer auf eben diese
Funktionspointertabelle abgelegt wird.

Die vernünftigste Lösung ist immer noch ein Mittelweg und die
Forderung nach Initialisierung der Funktionspointer über Bord
zu schmeissen. Da macht man sich dann eine Init Funktion, in der
die Funktionspointer rein kommen und die für jedes Objekt auf-
gerufen werden muss.

*Wenn man denn unbedingt 'objektorientiert' in C arbeiten will*
*und die object.function Notation haben möchte.*

Ob das so Sinn macht steht auf einem anderen Blatt. Speichertechnisch
ist das ein ziemlicher Horror, solange die Funktionspointer nicht
ihre wirkliche Stärke zur Implementierung von virtuellen Funktionen
ausspielen können.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Im Übrigen schliesse ich mich sous bzw. Sven an.

Dito. Aber als Schnellschuss bzw. der Neugier wegen, würde ich mal 
folgenden Aufbau testen:

uart.c
======

#include "uart.h"

suart uart = { "         ", uart_send };

void uart_send(char [] to_send)
{
    // TODO senderoutine
}



uart.h
======

#ifndef UART_H
#define UART_H

#include "common.h"

typedef void (*VoidFnct)( char [] );

typedef struct
{
   char buffer[10];
   VoidFnct send;
} suart;

extern suart uart; // in uart.c

#endif



main.c
======

#include "common.h"
#include "uart.h"

int main (void)
{
  uart.send("hello");
  while(1);
}



common.h
========

#ifndef COMMON_H
#define COMMON_H

#include <avr/io.h>

#endif

Autor: NoIdea (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schreibt:
"Speichertechnisch ist das ein ziemlicher Horror, solange die 
Funktionspointer nicht ihre wirkliche Stärke zur Implementierung von 
virtuellen Funktionen
ausspielen können."

Aha, ok das ist ein starkes Gegenargument. Werd wohl der Empfehlung 
folgen und  die Funktionen aus dem struct rauslassen, denn das umbiegen 
der Fkt.-pointer ist in den allermeisten Fällen nicht wirklich von 
Nöten. War nur ne Idee (keine gute aber immerhin ;) ).

Vielen Dank!!!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
NoIdea wrote:

> der Fkt.-pointer ist in den allermeisten Fällen nicht wirklich von
> Nöten. War nur ne Idee (keine gute aber immerhin ;) ).

Ne. ist doch ok.

Wenn die Memberfunktionen keine virtuellen Funktionen sind,
dann verändert ein C++ Compiler intern (*) einen Aufruf

   object.function( argument );

zu

  function( object, argument );

macht also auch einen stink normalen Funktionsaufruf daraus, bei
dem als erstes Argument (oder letztes, je nach Compiler) das
Objekt selbst übergeben wird (als Referenz).

Diese . Notation ist also im Grunde für einen C++ Compiler nur
'syntactic sugar' um eine andere Syntax für einen Member-
Funktionsaufruf zu haben, der ein bischen schöner aussieht und
Aufrufe von Memberfunktionen von Aufrufen von standalone
Funktionen zu unterscheiden.

(*) Das ist ihm vom C++ Standard natürlich nicht so vorgeschrieben,
aber seit Stroustroups Zeiten wird das so gemacht. Daher auch
unter anderem das viel gepriesene und noch viel öfter verfluchte
Name-Mangling von Member-Funktionsnamen.

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

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

Formatierung (mehr Informationen...)

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




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

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