Heim > Web-Frontend > CSS-Tutorial > Erweiterungsproblem durch die Verwendung von Generika in C

Erweiterungsproblem durch die Verwendung von Generika in C

高洛峰
Freigeben: 2016-11-22 17:33:41
Original
1389 Leute haben es durchsucht

Vor ein paar Tagen las ein Blogger einen Artikel, in dem er die Generika von C dafür kritisierte, dass sie aufgeblähten Code in der generierten ausführbaren Datei verursachten.

Der Blogger beschäftigt sich seit vielen Jahren mit der Entwicklung von C-Software. Da die vorherige Entwicklungsumgebung ein Server mit ausreichenden Ressourcen war, musste man sich keine Gedanken über den Speicherplatz machen. Kürzlich habe ich geplant, C für die Entwicklung auf der eingebetteten Plattform des Smart-Home-Hosts zu verwenden. Der FLASH-Speicherplatz ist begrenzt, dies ist ein Faktor, der berücksichtigt und beachtet werden muss.

Definieren Sie zwei Listen wie folgt mit unterschiedlichen Elementtypen:

list<int> l1;
list<string> l2;
Nach dem Login kopieren

Was sollten Sie tun, wenn Sie die Sprache C verwenden? Es wird ein Codesatz für list und ein weiterer Codesatz für list geschrieben. Jeder Satz hat die gleichen Mitgliedsfunktionen, nur unterschiedliche Variablentypen.

Das Folgende ist die C-Sprachimplementierung von list:

//! code-1struct list_int_item {
    int value;
    struct list_int_item *next;
};struct list_int {
    struct list_int_item *head;
    size_t size;
};

void list_int_insert(struct list_int *p, int value);int  list_int_sort(struct list_int *p);bool list_int_empty(struct list_int *p);
...
Nach dem Login kopieren

Das Folgende ist die C-Sprachimplementierung von list:

//! code-2
struct list_string_item {
    string value;
    struct list_string_item *next;
};

struct list_string {
    struct list_string_item *head;
    size_t size;
};

void list_string_insert(struct list_int *p, string value);
int  list_string_sort(struct list_int *p);
bool list_string_empty(struct list_int *p);
...
Nach dem Login kopieren

Beide Der Unterschied liegt in der Art. In der C-Sprache verwenden wir häufig Makros, um die Typen wie folgt zu ersetzen:

//! code-3
#define LIST_DECLARE(TYPE) \
    struct list_##TYPE##_item { \
        TYPE## value; \
        struct list_##TYPE##_item *next; \
    }; \
    \
    struct list_##TYPE { \
        struct list_##TYPE##_item *head; \
        size_t size; \
    }; \
    \
    void list_##TYPE##_insert(struct list_##TYPE *p, ##TYPE## value); \
    int  list_##TYPE##_sort(struct list_##TYPE *p); \
    bool list_##TYPE##_empty(struct list_##TYPE *p); \
    ...
Nach dem Login kopieren

Dann ist list in der Header-Datei wie folgt definiert:

//! code-4

LIST_DECLARE(double)
Nach dem Login kopieren

Daher ist es unvermeidlich, dass Generika redundanten Code erzeugen, zumindest ist die Verwendung von C zur Erstellung solcher Generika unvermeidlich.

Da es unvermeidbar ist, wollen wir sehen, wie wir die oben genannten Probleme so weit wie möglich vermeiden können. In „Effective C“ gibt es ein Kapitel, in dem ausdrücklich erwähnt wird: Verwenden Sie keine unnötigen Parameter in Vorlagen. Weil der Compiler für jeden Parameter einen entsprechenden Codesatz generiert.

Wenn der Code nur einen Datentyp enthält, generiert der Compiler dann nur einen Satz verwandter Codes, selbst wenn mehrere Variablen mit diesem Typ definiert sind? (So ​​sollte es sein).

Schreiben Sie ein Beispiel zum Vergleich: (ohne unnötigen Code)

test1.cpp, das nur map enthält, aber m1, m2, m3 definiert sind.

//! code-5

    map<int, string> m1;
    map<int, string> m2;
    map<int, string> m3;

    m1.insert(std::make_pair(1, "hello"));
    m2.insert(std::make_pair(1, "hi"));
    m3.insert(std::make_pair(1, "lichunjun"));
Nach dem Login kopieren

test2.cpp, verglichen mit test1.cpp, gibt es drei Typen:

//! code-6    map<int, string> m1;
    map<int, double> m2;
    map<int, int> m3;

    m1.insert(std::make_pair(1, "hello"));
    m2.insert(std::make_pair(1, 1.2));
    m3.insert(std::make_pair(1, 44));
Nach dem Login kopieren

Als Ergebnis wird die Größe der kompilierten ausführbaren Datei verglichen:

[hevake_lcj@Hevake tmp]$ ll test1 test2
-rwxrwxr-x. 1 18784 Mar 19 22:01 test1
-rwxrwxr-x. 1 35184 Mar 19 22:03 test2
Nach dem Login kopieren

Test2 ist doppelt so groß wie Test1. Der Grund dafür muss natürlich nicht genannt werden.


Noch eine Frage: Werden Zeiger als Typ betrachtet?

Die obige Liste und list Daher müssen zwei Codesätze generiert werden, um dies zu erreichen.

Und Hinweise, egal was sie sind, sie sind gleich. Wir können void* verwenden, um alle Zeigertypen darzustellen.

Also haben wir den obigen Code geändert und erneut getestet:

//! code-7    map<int, string*> m1;
    map<int, string*> m2;
    map<int, string*> m3;

    m1.insert(std::make_pair(1, new string("hello")));
    m2.insert(std::make_pair(1, new string("hi")));
    m3.insert(std::make_pair(1, new string("lichunjun")));
Nach dem Login kopieren

und

//! code-8    map<int, string*> m1;
    map<int, double*> m2;
    map<int, int*> m3;

    m1.insert(std::make_pair(1, new string("hello")));
    m2.insert(std::make_pair(1, new double(1.2)));
    m3.insert(std::make_pair(1, new int(44)));
Nach dem Login kopieren

Das Ergebnis sieht so aus:

-rwxrwxr-x. 1 18736 Mar 19 23:05 test1
-rwxrwxr-x. 1 35136 Mar 19 23:05 test2
Nach dem Login kopieren

Die erwarteten Ergebnisse von Test1 und Test2 sind ähnlich, aber es gibt keine Optimierung in Bezug auf die Ergebnisse~


Denke: Gibt es beliebige C Welche Parameter können dies optimieren?

Wenn nicht, können wir aus Platzgründen nur alle Zeiger als void*-Typ definieren und sie dann bei Verwendung umwandeln.

//! code-9    map<int, void*> m1;
    map<int, void*> m2;
    map<int, void*> m3;

    m1.insert(std::make_pair(1, new string("hello")));
    m2.insert(std::make_pair(1, new double(1.2)));
    m3.insert(std::make_pair(1, new int(44)));

    cout << *static_cast<string*>(m1[1]) << endl;
    cout << *static_cast<double*>(m2[1]) << endl;
    cout << *static_cast<int*>(m3[1]) << endl;
Nach dem Login kopieren

Der obige Code basiert auf Code-8, definiert alle Spezifikationen als void* und verwendet static_cast, um bei Verwendung die Konvertierung in den entsprechenden Zeigertyp zu erzwingen.

Im Vergleich zu Code-7 beträgt die auf diese Weise erhaltene Codegröße nur 16 Byte mehr.

Aber dieser Ansatz ist sehr unerwünscht. Nachdem void*-Zeiger verwendet werden müssen, überprüft der Compiler den Typ nicht mehr und es ist leicht, den Typ zu verwechseln.


Verwandte Etiketten:
c++
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage