Forum: PC-Programmierung "Objektbasiertes C" Speicherzugriffsfehler


von Patti W. (pat_w)


Angehängte Dateien:

Lesenswert?

Guten Tag,

ich muss mich im Moment mit "Objektbasiertem C" auseinandersetzen und 
habe so meine Probleme.

Kurze Info vorab ich muss das im Anhang zu findende Klassendiagramm 
programmieren.

Ich habe einen kleinen Teil versucht umzusetzen und bekomme einen 
Speicherzugriffsfehler beim ausführen.

Hier die main.c
1
#include "LED.h"
2
#include "PWMLed.h"
3
#include <stdio.h>
4
#include <stdlib.h>
5
6
7
int main()
8
{
9
10
  class_LED led1 = NewClassLed(1);  // Led Status zum Anfang 1
11
12
  printf("LED ist %d\n", led1->getStatus(led1));
13
14
free(led1);
15
16
return 0;
17
}

Hier erstelle ich eine LED und möchte ihren Status einfach nur ausgeben.

1
#ifndef LED_H_
2
#define LED_H_
3
4
typedef struct Led *class_LED;
5
 struct Led{  
6
  int (*getStatus)(class_LED led);
7
  int status;
8
};
9
10
class_LED NewClassLed(int status);
11
12
int getstatus(class_LED led);
13
14
#endif /* LED_H_ */

Die Header-Datei..
1
#include "LED.h"
2
#include <stdio.h>
3
#include <stdlib.h>
4
5
6
class_LED NewClassLed(int status)
7
{
8
9
  class_LED led = (struct Led*)malloc(sizeof(struct Led));
10
  led->status=status;
11
12
13
 return led;
14
}
15
16
int getstatus(class_LED led){
17
  int status = led->status;
18
  return status;
19
}

Und die LED.c


Ich sehe den Fehler nicht und wäre sehr dankbar für jegliche Hilfe.

Grüße

pat_w

von Tom (Gast)


Lesenswert?

Der Funktionszeiger im struct wird nicht initialisiert, aber in main() 
benutzt.

von Patti W. (pat_w)


Lesenswert?

Tom schrieb:
> Der Funktionszeiger im struct wird nicht initialisiert, aber in main()
> benutzt.

Das geht wo, wie?

von Nase (Gast)


Lesenswert?

Patti W. schrieb:
> ich muss das im Anhang zu findende Klassendiagramm
> programmieren

"Ich muss" heißt doch sicher, dass du gerade eine Vorlesung besuchst 
oder anderweitig mehr zum Thema weißt oder...?

von Peter K. (Gast)


Lesenswert?

Du solltest bei deiner new_class_led Funktion nicht nur led->Status 
setzten, sondern eben auch led->getStatus = &getstatus;

Ansonsten zeigt der Funktionspointer getStatus nirgendwo hin.

von Patti W. (pat_w)


Lesenswert?

Nase schrieb:
> Patti W. schrieb:
>> ich muss das im Anhang zu findende Klassendiagramm
>> programmieren
>
> "Ich muss" heißt doch sicher, dass du gerade eine Vorlesung besuchst
> oder anderweitig mehr zum Thema weißt oder...?

Jain, sitze zu Hause und versuche Stoff auszubereiten, da der Prof sagen 
wir keine "Ich bin dein Freund Haltung" besitzt und auf solche Fragen 
ehr entblößt reagiert. Nett formuliert.

Deshalb wäre ich eurer Hilfe hier sehr dankbar.

von Patti W. (pat_w)


Lesenswert?

Peter K. schrieb:
> Du solltest bei deiner new_class_led Funktion nicht nur led->Status
> setzten, sondern eben auch led->getStatus = &getstatus;
>
> Ansonsten zeigt der Funktionspointer getStatus nirgendwo hin.

Top :) Solche Sachen vergisst man dann natürlich. Hat funktioniert.

von beric (Gast)


Lesenswert?

Aber wozu willst du getStatus als Pointer haben? Soll der überladen 
werden? (Also "virtual" in C++)? Wenn nicht, kannste das sein lassen.

von Peter K. (Gast)


Lesenswert?

Ich würde dir aber wegen der Übersichtlichkeit vorschlagen dass du eine 
new Funktion wie in C++ implementierst, die dir einfach nur Speicher 
reserviert und von der Klasse den Konstruktor aufruft, dadurch wird das 
Anlegen von Klassen vereinheitlicht.

Ich hab im Internet vor einiger zeit mal einen Code gefunden indem dass 
so implementiert war, das ging irgendwie mit Virtuellen Tabellen, also 
alles in etwa so wie es auch der C++ Compiler macht. Da war dann auch 
Polymorphie möglich usw.

von Nase (Gast)


Lesenswert?

Darauf fußt so ungefähr die komplette Glib und auch Gtk...

von Patti W. (pat_w)


Lesenswert?

beric schrieb:
> Aber wozu willst du getStatus als Pointer haben? Soll der überladen
> werden? (Also "virtual" in C++)? Wenn nicht, kannste das sein lassen.

genau das möchte ich auch noch ausprobieren,  werde mal berichten wenn 
ich es fertig habe.

von Patti W. (pat_w)


Lesenswert?

Peter K. schrieb:
> Ich würde dir aber wegen der Übersichtlichkeit vorschlagen dass du eine
> new Funktion wie in C++ implementierst, die dir einfach nur Speicher
> reserviert und von der Klasse den Konstruktor aufruft, dadurch wird das
> Anlegen von Klassen vereinheitlicht.
>
> Ich hab im Internet vor einiger zeit mal einen Code gefunden indem dass
> so implementiert war, das ging irgendwie mit Virtuellen Tabellen, also
> alles in etwa so wie es auch der C++ Compiler macht. Da war dann auch
> Polymorphie möglich usw.

Klingt gut. Ich werde nachher für ein RaspberryPi Projekt eine 
"Einparkhilfe" mit dieser OOP realisieren müssen und werde mir deinen 
Tipp da zu Gemüt führen. Danke

von Decius (Gast)


Lesenswert?

du definierst hier:

class_LED led1 = NewClassLed(1);  // Led Status zum Anfang 1

die variavble led1 statisch und resevierst deren Speicherplatz nicht mit 
malloc() oder new() daher ist die freigabe des Speichers mit

free(led1);

nicht nur unötig sondern schädlich. das dürfte die zeile sein, die den 
speicherzugriffsfehler verursacht. Speicherplatz statisch vereinbarter 
variablen wird automatisch freigegeben, wenn der gültigkeitsbereich der 
variable verlassen wird.

von Peter K. (Gast)


Lesenswert?

Decius schrieb:
> die variavble led1 statisch und resevierst deren Speicherplatz nicht mit
> malloc() oder new() daher ist die freigabe des Speichers mit

Das ist C und nicht C++ die Funktion NewClassLed verwendet malloc

von Clemens L. (c_l)


Lesenswert?

Decius schrieb:
> du definierst hier:
>
> class_LED led1 = NewClassLed(1);  // Led Status zum Anfang 1
>
> die variavble led1 statisch

Nein, class_LED ist ein Zeiger-Typ.

Wie man sieht, wird die Verständlichkeit durch das Verstecken des 
Zeigers in einem typedef nur verringert. "struct Led *" wäre nun auch 
nicht viel weniger Tipparbeit.

von Peter K. (Gast)


Lesenswert?

Clemens L. schrieb:
> Wie man sieht, wird die Verständlichkeit durch das Verstecken des
> Zeigers in einem typedef nur verringert. "struct Led *" wäre nun auch
> nicht viel weniger Tipparbeit.

Da stimme ich dir zu dass das Verstecken eines Zeigers in einem Typedef 
nicht günstig ist, aber bensonders beim Objektorientierten zu 
Programmieren wird das oft gemacht weil man damit den Eindruck 
vermitteln möchte es handelt sich um das Objekt und nicht um einen 
Zeiger auf das Objekt.
Dann definiert man noch eine delete Funktion und es sieht aus wie C++.

von Programmierer (Gast)


Lesenswert?

Warum man nicht einfach direkt C++ verwendet ist schleierhaft... Da 
könnte man direkt gebraucht von schönen Dingen wie shared_ptr oder 
unique_ptr machen, sodass man zwar dynamischen Speicher verwenden, aber 
das Freigeben ("delete") gar nicht mehr vergessen kann!

von Peter K. (Gast)


Lesenswert?

Programmierer schrieb:
> Warum man nicht einfach direkt C++ verwendet ist schleierhaft...

C++ ist einfach für viele ein Rotes Tuch, siehe Linus Torvald. 
Andererseits war es früher auch so dass die ersten C++ Compiler einfach 
nur Übersetzer auf C Code waren und dieser wurde dann kompiliert.

Zudem ist es oft von Vorteil C zu verwenden wenn man wert auf 
Binärkompatibilität legt. C++ hat nämlich keine definierte ABI

von xXx (Gast)


Lesenswert?

Programmierer schrieb:
> Warum man nicht einfach direkt C++ verwendet ist schleierhaft...

Es geht hier wohl um den Lerneffekt. shared_ptr & Co. sind wenig 
hilfreich um Grundlagenwissen zu vermitteln.

von Daniel A. (daniel-a)


Lesenswert?

Das malloc in NewClassLed könnte auch null livern. Ein constructor soll 
Initialisieren, nicht Speicher reservieren. Ich würde dass so 
schreiben*:
1
void Led_init(struct* Led led, int status){
2
  led->status=status;
3
}
4
5
int Led_getStatus(struct Led* led){
6
  return led->status;
7
}
8
9
int main(){
10
  struct Led led;
11
12
  Led_init( &led, 1 );  // Led Status zum Anfang 1
13
14
  printf("LED ist %d\n", Led_getStatus(&led1));
15
16
  return 0;
17
}

* zumindest wenn ich nicht zu faul wäre und statdessen auf den getter 
verzichten und die Init methode durch einen designated initializer 
ersetzen würde.

: Bearbeitet durch User
von bal (Gast)


Lesenswert?

Peter K. schrieb:
> Zudem ist es oft von Vorteil C zu verwenden wenn man wert auf
> Binärkompatibilität legt. C++ hat nämlich keine definierte ABI

Das ABI ist doch auch in C nicht im Standard geregelt, sondern in der 
Compilerdoku.
Msvc ist nicht binärkompatibel zu gcc.

Wo ist der Unterschied zu C++?

von Peter K. (Gast)


Lesenswert?

bal schrieb:
> Wo ist der Unterschied zu C++?

Naja beim C++ sind Programme die mit unterschiedlichen Compilerflags 
programmiert wurden oder die von zwei unterschiedlichen Versionen des 
selben Compilers kompiliert wurden nicht binärkompatibel.
Das ist auch der Grund warum viele C++ Programm ein Pluginsystem in C 
haben weil einfach dadurch ein Einheitliches Interface gibt.
Btw ich habe ein in C++ geschriebenes Programm mit C Pluginsystem, 
kompiliert mittels g++ und Plugins die ich mit dem Msvc kompiliert habe 
und das Funktioniert Problemlos.

Bei einem Pluginsystem in C++ funktionierte das nicht.

http://gcc.gnu.org/onlinedocs/gcc/Compatibility.html
Most platforms have a well-defined ABI that covers C code, but ABIs that 
cover C++ functionality are not yet common.

von Karl H. (kbuchegg)


Lesenswert?

Peter K. schrieb:
> bal schrieb:
>> Wo ist der Unterschied zu C++?
>
> Naja beim C++ sind Programme die mit unterschiedlichen Compilerflags
> programmiert wurden oder die von zwei unterschiedlichen Versionen des
> selben Compilers kompiliert wurden nicht binärkompatibel.

Das ist in C ganz genau gleich.
Sie können oder können nicht binärkompatibel sein. Welches davon der 
Fall ist bestimmt der Compilerhersteller.

> http://gcc.gnu.org/onlinedocs/gcc/Compatibility.html
> Most platforms have a well-defined ABI that covers C code, but ABIs that
> cover C++ functionality are not yet common.

Anders rum wird ein Schuh daraus.
Nicht C hat ein einheitliches ABI, sondern die zugrundeliegenden 
Betriebssysteme definieren durch ihre Programmierschnittstellen ein ABI. 
Da ein Betriebssystem aber nicht davon ausgehen kann, dass ein in C++ 
geschriebenes Programm auf ihm läuft, müssen diese ABI naturgemäss am 
kleinsten gemeinsamen Nenner ansetzen. Auf einer VAX war es problemlos 
möglich, von Fortran aus Lisp Code aufzurufen. Das hat aber nichts mit 
den definierten Eigenschaften von Fortran oder Lisp zu tun, sondern 
damit, das DEC für sich entschieden hat, in allen Sprachen einheitliche 
Schnittstellen auf Binärcode Ebene zu benutzen. Deshalb konnte man von 
Pascal auch PL/I Code aufrufen, der eine Fortran Subroutine zur 
Berechnung eines Problems benutzt, das mit Lisp optimiert wurde.

: Bearbeitet durch User
von Mikro 7. (mikro77)


Lesenswert?

Karl H. schrieb:
> ...Auf einer VAX war es problemlos
> möglich, von Fortran aus Lisp Code aufzurufen...

You made my day! :-)

von Peter K. (Gast)


Lesenswert?

Karl H. schrieb:
> Nicht C hat ein einheitliches ABI, sondern die zugrundeliegenden
> Betriebssysteme definieren durch ihre Programmierschnittstellen ein ABI.

Wieder mal was dazu gelernt ;)

Worauf ich aber eigentlich raus wollte es gibt durchaus noch Gründe 
warum man in C Objektorientiert programmiert und nicht einfach C++ 
nimmt. Wobei in diesem Fall ist es vermutlich nur eine Übung um zu 
zeigen obwohl C Prozedural ist kann ich Objektrorientiert arbeiten wenn 
man möchte.

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.