Frage

Ich habe immer gehört, dass man in C hat wirklich zu sehen, wie Sie Speicher verwalten. Und ich fange an, noch C zu lernen, aber bisher habe ich keine Erinnerung Verwaltung verbundene Tätigkeiten überhaupt zu tun hatte .. ich mit immer vorgestellt Variablen zu lösen und alle Arten von hässlichen Dinge zu tun. Aber das scheint nicht der Fall zu sein.

Kann jemand mir zeigen (mit Codebeispielen) ein Beispiel, wenn Sie einige „Speicherverwaltung“ würden tun?

War es hilfreich?

Lösung

Es gibt zwei Orte, an denen Variablen können im Speicher gesetzt werden. Wenn Sie eine Variable wie folgt erstellen:

int  a;
char c;
char d[16];

Die Variablen werden erstellt in der " Stapel ". Stack-Variablen werden automatisch freigegeben, wenn sie außerhalb des Gültigkeitsbereiches gehen (das heißt, wenn der Code sich nicht erreichen kann mehr). Sie könnten hören, wie sie „automatisch“ Variablen genannt, aber das hat sich aus der Mode gekommen.

Viele Anfänger Beispiele verwenden nur Variablen stapeln.

Der Stapel ist schön, weil es automatisch, aber es hat auch zwei Nachteile: (1) Der Compiler muss im Voraus wissen, wie groß die Variablen sind, und (b) der Stapelspeicher ist etwas begrenzt. Zum Beispiel: in Windows unter Standardeinstellung für den Microsoft-Linker wird der Stapel auf 1 MB, und nicht alles ist für die Variablen zur Verfügung

.

Wenn Sie nicht wissen, bei der Kompilierung, wie groß Ihr Array ist, oder wenn Sie eine große Array oder Struktur benötigen, müssen Sie „Plan B“.

Plan B ist die " heap " genannt. Sie können in der Regel Variablen so groß wie das Betriebssystem schaffen Sie lassen, aber man muss es selbst tun. Frühere Buchungen zeigten Sie ein, wie Sie es tun können, obwohl es andere Möglichkeiten gibt:

int size;
// ...
// Set size to some value, based on information available at run-time. Then:
// ...
char *p = (char *)malloc(size);

(Beachten Sie, dass Variablen in dem Heap nicht direkt manipuliert werden, sondern über Zeiger)

Wenn Sie einen Haufen Variable zu erstellen, das Problem ist, dass der Compiler nicht sagen kann, wenn Sie mit ihm fertig sind, so verlieren Sie die automatische Freigabe. Das ist, wo die „manuelle Freigabe“ Sie sich beziehen kommt in. Der Code ist jetzt verantwortlich zu entscheiden, wann die Variable nicht mehr benötigt wird, und lassen Sie ihn so kann der Speicher für andere Zwecke entnommen werden. Für den Fall, der oben mit:

free(p);

Was macht diese zweite Option „schmutziges Geschäft“ ist, dass es nicht immer einfach zu wissen, ist, wenn der Variable nicht mehr benötigt wird. Vergessend eine Variable freigeben, wenn Sie nicht brauchen wird es Ihr Programm verursachen mehr Speicher zu verbrauchen, die es braucht, um. Diese Situation ist ein „Leck“ genannt. Der „durchgesickert“ Speicher kann nicht für alles verwendet werden, bis das Programm beendet und das Betriebssystem wieder alle seiner Ressourcen. Auch fiese Probleme sind möglich, wenn Sie ein Heap-Variable versehentlich freigeben vor Sie sind damit tatsächlich getan.

In C und C ++, sind Sie verantwortlich Ihre Heap Variablen aufzuräumen wie oben gezeigt. Allerdings gibt es Sprachen und Umgebungen wie Java und .NET-Sprachen wie C #, die einen anderen Ansatz verwenden, wo der Haufen auf eigenem gereinigt wird. Diese zweite Methode, „Garbage Collection“ genannt, ist viel einfacher, auf dem Entwickler aber Sie eine Strafe in Aufwand und Leistung bezahlen. Es ist ein Gleichgewicht.

(Ich habe über viele Details beschönigt ein einfacheres zu geben, aber hoffentlich mehr nivelliert Antwort)

Andere Tipps

Hier ist ein Beispiel. Angenommen, Sie eine strdup () Funktion, die einen String dupliziert:

char *strdup(char *src)
{
    char * dest;
    dest = malloc(strlen(src) + 1);
    if (dest == NULL)
        abort();
    strcpy(dest, src);
    return dest;
}

Und Sie nennen es wie folgt aus:

main()
{
    char *s;
    s = strdup("hello");
    printf("%s\n", s);
    s = strdup("world");
    printf("%s\n", s);
}

Sie können sehen, dass das Programm funktioniert, aber Sie haben Speicher zugewiesen (über malloc), ohne sie frei. Sie haben die Zeiger auf den ersten Speicherblock verloren, wenn Sie strdup zweites Mal aufgerufen.

Das ist keine große Sache für diese kleine Menge an Speicher, aber betrachten wir den Fall:

for (i = 0; i < 1000000000; ++i)  /* billion times */
    s = strdup("hello world");    /* 11 bytes */

Sie haben nun 11 GB Speicher verbraucht (möglicherweise mehr, auf dem Speichermanager abhängig), und wenn Sie nicht Ihr Prozess abgestürzt wahrscheinlich ziemlich langsam ausgeführt wird.

Um dies zu beheben, müssen Sie für alles () frei nennen, die mit malloc erhalten wird (), nachdem Sie fertig sind mit ihm:

s = strdup("hello");
free(s);  /* now not leaking memory! */
s = strdup("world");
...

Hope dieses Beispiel hilft!

Sie müssen „Speicherverwaltung“ tun, wenn Sie Speicher auf dem Heap, anstatt den Stack verwenden möchten. Wenn Sie nicht wissen, wie groß ein Array erst zur Laufzeit zu machen, dann müssen Sie den Heap verwenden. Zum Beispiel könnten Sie etwas in einem String speichern möchten, aber nicht wissen, wie groß sein Inhalt sein wird, bis das Programm ausgeführt wird. In diesem Fall würde man so etwas schreiben:

 char *string = malloc(stringlength); // stringlength is the number of bytes to allocate

 // Do something with the string...

 free(string); // Free the allocated memory

Ich denke, die knappste Art und Weise, die Frage zu beantworten, die Rolle des Zeigers in C betrachten Der Zeiger ist ein leichter, aber leistungsfähiger Mechanismus, den Ihnen eine enorme Freiheit auf Kosten der immensen Kapazität gibt selbst in dem Fuß zu schießen.

C die Verantwortung dafür, Ihre Zeiger auf Speicher zeigen Sie besitzen ist Ihnen und Sie allein. Dies erfordert einen organisierten und disziplinierten Ansatz, wenn Sie Zeiger verlassen, was es schwer macht, wirksam C zu schreiben.

Die posted Antworten bisher konzentrieren sich auf automatische (Stack) und Heap-Variablenbelegungen. Stapelzuweisung verwenden, funktioniert aber für automatisch verwaltet und bequeme Speicher, aber in einigen Fällen (große Puffer, rekursive Algorithmen) kann es zu dem schrecklichen Problem der Stack-Überlauf führen. genau, wie viel Speicher zu wissen, Sie auf dem Stapel zuweisen kann, ist auf dem System sehr abhängig. In einigen Embedded-Szenarien könnte ein paar Dutzend Bytes Ihr Limit sein, in einigen Desktop-Szenarien, die Sie sicher Megabyte verwenden können.

Heapzuordnung weniger inhärent auf die Sprache. Es ist im Grunde eine Reihe von Bibliotheksaufrufen, die Sie Eigentum an einem Speicherblock gegebener Größe gewährt, bis Sie bereit zurückzukehren ( ‚frei‘) es sind. Es klingt einfach, ist aber mit unsäglichen Programmierer Trauer verbunden. Die Probleme sind einfach (zweimal die gleichen Speicher zu befreien, oder gar nicht [Speicherlecks], nicht genügend Speicher [Pufferüberlauf] Zuweisung usw.), aber schwer zu vermeiden und zu debuggen. Ein hightly disziplinierter Ansatz ist absolut zwingend in practive aber natürlich die Sprache eigentlich ist es nicht Mandat.

Ich möchte eine andere Art von Speicherzuweisung erwähnen, die von anderen Stellen ignoriert worden ist. Es ist möglich, statisch Variablen zuweisen, die sie außerhalb jeder Funktion zu deklarieren. Ich denke, in der Regel diese Art der Zuteilung einen schlechten Ruf bekommt, weil es von globalen Variablen verwendet wird. Allerdings gibt es nichts, dass der einzige Weg, sagt Speicher verwenden zugewiesen diese Art und Weise ist als undiszipliniert globale Variable in einem Gewirr von Spaghetti-Code. Die statische Zuweisung Methode kann einfach verwendet werden, einige der Gefahren des Haufens und automatische Zuordnungsverfahren zu vermeiden. Einige C-Programmierer sind überrascht, dass große und anspruchsvolle C eingebettet und Spielprogramme zu lernen, wurden ohne Verwendung von Heapzuordnung überhaupt aufgebaut ist.

Es gibt einige großen Antworten hier, wie und freie Speicher zu reservieren, und meiner Meinung nach der schwierigeren Seite des C verwendet, ist sichergestellt, dass der einzige Speicher, den Sie verwenden, ist Speicher, den Sie zugewiesen haben - wenn dies nicht richtig gemacht was Sie am Ende mit ist der Vetter von dieser Seite - ein Pufferüberlauf -. und Sie können Speicher werden überschreiben, die von einer anderen Anwendung verwendet werden sind, mit sehr unvorhersehbaren Ergebnissen

Ein Beispiel:

int main() {
    char* myString = (char*)malloc(5*sizeof(char));
    myString = "abcd";
}

An diesem Punkt haben Sie 5 Byte für myString zugeordnet und füllte es mit "abcd \ 0" (Strings in einem Null-Ende - \ 0). Wenn Ihre String Zuordnung ist

myString = "abcde";

Sie würden „ABCDE“ in dem 5 Bytes werden zuweisen Sie zu Ihrem Programm zugewiesen haben waren, und das Hinter Null-Zeichen würde am Ende dieses gesetzt werden - ein Teil des Speichers, der für die Nutzung nicht zugewiesen wurde und könnte frei sein, könnte aber ebenso von einer anderen Anwendung verwendet werden, wobei -. Dies ist der kritische Teil der Speicherverwaltung, wo ein Fehler wird nicht vorhersehbar (und manchmal nicht wiederholbar) Folgen hat

Eine Sache zu erinnern ist, zu immer initialisieren Ihre Zeiger auf NULL, da ein nicht initialisierte Zeiger eine Pseudo-Zufall gültige Speicheradresse enthalten, die Zeiger Fehler lautlos voran gehen machen. Durch die Durchsetzung eines Zeigers mit NULL initialisiert werden, können Sie immer fangen, wenn Sie diesen Zeiger verwenden, ohne es zu initialisieren. Der Grund dafür ist, dass Betriebssysteme „Draht“ die virtuelle Adresse 0x00000000 zu allgemeiner Schutz Ausnahme abzufangen Null-Zeiger-Nutzung.

Auch könnten Sie dynamische Speicherzuweisung verwenden, wenn Sie eine riesige Auswahl definieren müssen, sagen int [10000]. Sie können einfach nicht in Stapel gelegt, weil dann, hm ... Sie werden einen Stapelüberlauf bekommen.

Ein weiteres gutes Beispiel eine Implementierung einer Datenstruktur sein würde, sagen Liste oder binären Baum verknüpft. Ich habe keinen Beispielcode hier einfügen, aber Sie können es einfach googeln.

(Ich schreibe, weil ich die Antworten fühlen, sind bisher nicht ganz auf die Marke.)

Der Grund, warum Sie die Speicherverwaltung haben erwähnenswert ist, wenn Sie ein Problem / Lösung, die komplexen Strukturen zu schaffen, erfordert. (Wenn Ihr Programme abstürzen, wenn Sie zu viel Platz auf dem Stapel auf einmal zuweisen, ist das ein Fehler.) In der Regel die erste Datenstruktur Sie lernen brauchen, ist eine Art von Liste . Hier ist ein einzelner verknüpft ein, aus der Spitze von meinem Kopf:

typedef struct listelem { struct listelem *next; void *data;} listelem;

listelem * create(void * data)
{
   listelem *p = calloc(1, sizeof(listelem));
   if(p) p->data = data;
   return p;
}

listelem * delete(listelem * p)
{
   listelem next = p->next;
   free(p);
   return next;
}

void deleteall(listelem * p)
{
  while(p) p = delete(p);
}

void foreach(listelem * p, void (*fun)(void *data) )
{
  for( ; p != NULL; p = p->next) fun(p->data);
}

listelem * merge(listelem *p, listelem *q)
{
  while(p != NULL && p->next != NULL) p = p->next;
  if(p) {
    p->next = q;
    return p;
  } else
    return q;
}

Natürlich würde man ein paar andere Funktionen wie, aber im Grunde, das ist, was Sie Speicherverwaltung für benötigen. Ich möchte darauf hinweisen, dass es eine Reihe Tricks, die möglich sind mit „manuell“ Speicherverwaltung, z. B.

  • Mit der Tatsache, dass malloc garantiert wird (von dem Sprachstandard) einen Zeiger zurück durch 4 teilbar,
  • die Zuteilung zusätzlicher Raum für einige finstere Zweck Ihrer eigenen,
  • Erstellen von Speicherpool s ..

einen guten Debugger Get ... Viel Glück!

Euro Micelli

Ein negative hinzuzufügen ist, dass Zeiger auf den Stapel sind nicht mehr gültig, wenn die Funktion zurück, so dass Sie nicht einen Zeiger auf einen Stapel Variable aus einer Funktion zurückgeben können. Dies ist ein häufiger Fehler, und ein wichtiger Grund, warum Sie nicht nur mit Stack-Variablen auskommen. Wenn Ihre Funktion einen Zeiger zurückgeben muss, dann muss man mit Speicherverwaltung malloc und zu behandeln.

  

Ted Percival :
  ... Sie brauchen nicht malloc () 's Rückgabewert zu werfen.

Sie sind richtig, natürlich. Ich glaube, dass ist schon immer so, auch wenn ich a href keine Kopie von <= „http://en.wikipedia.org/wiki/The_C_Programming_Language_%28book%29“ rel = „nofollow noreferrer“ title = "“ The C Programming Language“von B. Kernighan und D. Ritchie '> K & R überprüfen.

Ich mag nicht viel von den impliziten Konvertierungen in C, so neige ich dazu, Abgüsse zu verwenden, um „Magie“ besser sichtbar zu machen. Manchmal hilft es, Lesbarkeit, manchmal nicht, und manchmal verursacht es ein stiller Fehler vom Compiler abgefangen werden. Trotzdem ich eine starke Meinung dazu nicht eine oder andere Weise.

haben,
  

Dies ist besonders wahrscheinlich, wenn Ihr Compiler versteht C ++ -. Stil Kommentare

Ja ... Sie haben mich erwischt es. Ich verbringe viel mehr Zeit in C ++ als C Vielen Dank für das zu bemerken.

In C, Sie haben tatsächlich zwei verschiedene Möglichkeiten. Eins, können Sie das System lassen Sie den Speicher verwalten. Alternativ können Sie das selbst tun. Im Allgemeinen würden Sie so lange wie möglich an den ehemaligen haften mögen. Allerdings Auto-verwalteten Speicher in C sind äußerst begrenzt und Sie müssen manuell den Speicher in vielen Fällen verwalten, wie zum Beispiel:

a. Sie wollen, dass der Variable, um die Funktionen zu überleben, und Sie wollen nicht, globalen Variable haben. Ex:

struct pair{
   int val;
   struct pair *next;
}

struct pair* new_pair(int val){
   struct pair* np = malloc(sizeof(struct pair));
   np->val = val;
   np->next = NULL;
   return np;
}

b. Sie wollen dynamisch zugewiesenen Speicher haben. Gängigste Beispiel ist Array ohne feste Länge:

int *my_special_array;
my_special_array = malloc(sizeof(int) * number_of_element);
for(i=0; i

c. You want to do something REALLY dirty. For example, I would want a struct to represent many kind of data and I don't like union (union looks soooo messy):

struct data{ int data_type; long data_in_mem; }; struct animal{/*something*/}; struct person{/*some other thing*/}; struct animal* read_animal(); struct person* read_person(); /*In main*/ struct data sample; sampe.data_type = input_type; switch(input_type){ case DATA_PERSON: sample.data_in_mem = read_person(); break; case DATA_ANIMAL: sample.data_in_mem = read_animal(); default: printf("Oh hoh! I warn you, that again and I will seg fault your OS"); }

Siehe, ein langer Wert ist genug, um alles zu halten. Denken Sie daran, es zu befreien, oder Sie werden es bereuen. Dies gehört zu meinen Lieblingstricks Spaß in C zu haben. D

Allerdings, in der Regel würden Sie weg von Ihren Lieblings-Tricks (T___T) zu bleiben. Sie werden Ihre OS brechen, früher oder später, wenn man sie zu oft verwenden. Solange Sie verwenden * alloc und frei nicht, es ist sicher zu sagen, dass du noch Jungfrau ist, und dass der Code immer noch schön aussieht.

Klar. . Wenn Sie ein Objekt erstellen, die außerhalb des Bereichs existiert Sie es in verwenden hier ein konstruiertes Beispiel ist (beachten Sie meine Syntax weg sein wird, meine C rostig ist, aber dieses Beispiel noch das Konzept erläutern):

class MyClass
{
   SomeOtherClass *myObject;

   public MyClass()
   {
      //The object is created when the class is constructed
      myObject = (SomeOtherClass*)malloc(sizeof(myObject));
   }

   public ~MyClass()
   {
      //The class is destructed
      //If you don't free the object here, you leak memory
      free(myObject);
   }

   public void SomeMemberFunction()
   {
      //Some use of the object
      myObject->SomeOperation();
   }


};

In diesem Beispiel verwende ich ein Objekt vom Typ SomeOtherClass während der Lebensdauer von MyClass. Das SomeOtherClass Objekt wird in mehreren Funktionen verwendet, also habe ich zugewiesen dynamisch den Speicher. Das SomeOtherClass Objekt erstellt wird, wenn MyClass erstellt wird, verwendet mehrmals über die gesamte Lebensdauer des Objekts, und dann befreit, wenn MyClass befreit

Natürlich, wenn dies war echter Code, gäbe es keinen Grund, sein (abgesehen von möglicherweise Stapelspeicherverbrauch) myObject auf diese Weise zu schaffen, aber diese Art der Objekterstellung / Zerstörung wird nützlich, wenn Sie viele Objekte haben, und wollen fein Kontrolle, wenn sie erzeugt und zerstört werden (so dass Ihre Anwendung 1 GB RAM für die gesamte Lebensdauer, zum Beispiel nicht aufzusaugen), und in einer Fensterumgebung, dann ist dies so ziemlich zwingend, als Objekte, die Sie erstellen (Schaltflächen ,) sagen, existieren müssen auch außerhalb eines bestimmten Funktion (oder sogar Klasse) Anwendungsbereich.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top