L'utilisation de variables de pointeur n'est-elle pas une surcharge de mémoire?


29

Dans des langages comme C et C ++, tout en utilisant des pointeurs vers des variables, nous avons besoin d'un emplacement mémoire supplémentaire pour stocker cette adresse. N'est-ce pas une surcharge de mémoire? Comment est-ce compensé? Les pointeurs sont-ils utilisés dans des applications à faible mémoire critique?


11
Les avantages de l'allocation dynamique de mémoire l'emportent largement sur le coût du pointeur.
James McLeod

32
Comment pensez-vous que d'autres langages (Java, C #, ...) stockent les références aux objets? (Indice: ils utilisent des pointeurs).
Erik Eidt

6
Un pointeur peut se trouver dans des registres ou être passé en argument. Dans les deux cas, il n'y a pas de surcharge de mémoire évidente . Et le pointeur peut être calculé (par exemple à travers l'arithmétique du pointeur, les fonctions renvoyant des pointeurs, etc.)
Basile Starynkevitch

9
Que diriez-vous de passer un (grand) argument struct par adresse? Si vous comptez cela comme une variable de pointeur, cela est inévitable pour de nombreux algorithmes et utilise beaucoup moins d'espace que de passer la structure par valeur!
PJTraill

4
Cela a la sensation de certains devoirs. La question est conçue pour explorer si la réponse comprend les pointeurs et comment ils sont utilisés.
Michael Shaw

Réponses:


34

En fait, la surcharge ne réside pas vraiment dans les 4 ou 8 octets supplémentaires nécessaires pour stocker le pointeur. La plupart du temps, les pointeurs sont utilisés pour l'allocation dynamique de la mémoire , ce qui signifie que nous invoquons une fonction pour allouer un bloc de mémoire, et cette fonction nous renvoie un pointeur qui pointe vers ce bloc de mémoire. Ce nouveau bloc représente en soi un surcoût considérable.

Maintenant, vous n'avez pas à vous engager dans l'allocation de mémoire pour utiliser un pointeur: vous pouvez avoir un tableau de intdéclaré statiquement ou sur la pile, et vous pouvez utiliser un pointeur au lieu d'un index pour visiter le ints, et c'est tous très agréables et simples et efficaces. Aucune allocation de mémoire nécessaire, et le pointeur occupera généralement exactement autant d'espace en mémoire qu'un index entier.

De plus, comme Joshua Taylor nous le rappelle dans un commentaire, les pointeurs sont utilisés pour transmettre quelque chose par référence. Par exemple, struct foo f; init_foo(&f);allouerait f sur la pile, puis appelerait init_foo()avec un pointeur vers cela struct. C'est très courant. (Faites juste attention à ne pas passer ces pointeurs "vers le haut".) En C ++, vous pouvez voir cela se faire avec une "référence" ( foo&) au lieu d'un pointeur, mais les références ne sont rien d'autre que des pointeurs que vous ne pouvez pas modifier, et elles occupent le même quantité de mémoire.

Mais la principale raison pour laquelle les pointeurs sont utilisés est l'allocation dynamique de la mémoire, et cela est fait afin de résoudre des problèmes qui ne pourraient pas être résolus autrement. Voici un exemple simpliste: imaginez que vous souhaitez lire l'intégralité du contenu d'un fichier. Où allez-vous les stocker? Si vous essayez avec un tampon de taille fixe, vous ne pourrez lire que les fichiers qui ne sont pas plus longs que ce tampon. Mais en utilisant l'allocation de mémoire, vous pouvez allouer autant de mémoire que nécessaire pour lire le fichier, puis procéder à sa lecture.

De plus, C ++ est un langage orienté objet, et certains aspects de la POO comme l' abstraction ne sont réalisables qu'en utilisant des pointeurs. Même les langages comme Java et C # font un usage intensif des pointeurs, ils ne vous permettent tout simplement pas de manipuler directement les pointeurs, afin de vous empêcher de faire des choses dangereuses avec eux, mais encore, ces langages ne commencent à avoir un sens qu'une fois que vous avez réalisé que dans les coulisses tout se fait à l'aide de pointeurs.

Ainsi, les pointeurs ne sont pas seulement utilisés dans des applications à temps critique et à faible mémoire, ils sont utilisés partout .


5
"la principale raison pour laquelle les pointeurs sont utilisés est l'allocation dynamique de la mémoire" Cela peut être le cas en C ++, mais pas nécessairement en C. En C, les pointeurs sont votre seul moyen de transmettre quelque chose par référence. Si vous ne voulez pas copier une structure entière, vous allez avoir besoin d'un pointeur dessus. Cela reste logique même si vous ne faites aucune allocation dynamique. Par exemple, struct foo f; init_foo(&f);allouer fsur la pile, puis appeler init_fooavec un pointeur sur cette structure. C'est très courant. (Faites juste attention à ne pas passer ces pointeurs "vers le haut".)
Joshua Taylor

@JoshuaTaylor c'est très correct, je l'avais oublié. Puis-je l'amender dans ma réponse? (Ceci est considéré comme une bonne pratique sur les programmeurs SE car les commentaires sont éphémères, alors que les réponses ne le sont pas.)
Mike Nakis

Ajoutez certainement votre réponse. :)
Joshua Taylor

2
Cela représente une surcharge considérable en soi, car ce bloc aura un en-tête (caché) qui sera généralement aussi grand que quelques pointeurs. => En fait, les implémentations modernes mallocont une surcharge d'en-tête TRÈS FAIBLE car elles regroupent les blocs alloués dans des "compartiments". D'un autre côté, cela se traduit par une surallocation en général: vous demandez 35 octets et obtenez 64 (à votre insu) gaspillant ainsi 29 ...
Matthieu M.

1
@MikeNakis: Ça a l'air mieux, merci de rester avec moi :)
Matthieu M.

35

N'est-ce pas une surcharge de mémoire?

Bien sûr, une adresse supplémentaire (généralement 4/8 octets selon le processeur).

Comment est-ce compensé?

Ce n'est pas. Si vous avez besoin de l'indirection nécessaire pour les pointeurs, vous devez payer pour cela.

Les pointeurs sont-ils utilisés dans des applications à faible mémoire critique?

Je n'ai pas fait beaucoup de travail là-bas, mais je suppose que oui. L'accès au pointeur est un aspect élémentaire de la programmation d'assemblage. Il faut des quantités insignifiantes de mémoire et les opérations de pointeur sont rapides - même dans le contexte de ce type d'applications.


1
@DavidGrinberg C'est seulement en supposant qu'il n'y a pas d' optimisation de la valeur de retour .
Darkhogg

3
J'ai utilisé des pointeurs lors de l'écriture d'applications TSR pour DOS qui devaient tenir en 15k dans les années 80. Alors oui, ils sont utilisés dans des applications à faible mémoire.
Gort the Robot

1
@StevenBurnap Vous appelez 15k de mémoire faible pour une application TSR? J'ai écrit une fois un outil TSR qui ne consommait que 16 octets de mémoire.
kasperd

22
@kasperd: Et à l'époque, nous n'avions que des zéros
Jack


11

Je n'ai pas tout à fait le même tour que Telastyn.

Les globaux du système dans un processeur intégré peuvent être adressés avec des adresses spécifiques codées en dur.

Les globaux dans un programme seront traités comme un décalage à partir d'un pointeur spécial qui pointe vers l'endroit en mémoire où les globaux et les statiques sont stockés.

Les variables locales apparaissent lorsqu'une fonction est entrée et sont traitées comme un décalage par rapport à un autre pointeur spécial, souvent appelé "pointeur de trame". Cela inclut les arguments de la fonction. Si vous faites attention aux push et pops avec le pointeur de pile, vous pouvez supprimer le pointeur de trame et accéder aux variables locales directement à partir du pointeur de pile.

Vous payez donc pour l'indirection des pointeurs, que vous parcouriez un tableau ou que vous saisissiez simplement une variable locale ou globale banale. Il est simplement basé sur un pointeur différent, en fonction de sa variable. Le code bien compilé gardera ce pointeur dans un registre CPU, plutôt que de le recharger chaque fois qu'il est utilisé.


6

Oui bien sûr. Mais c'est un équilibre.

Les applications à faible mémoire sont généralement construites en tenant compte du compromis entre la surcharge de quelques variables de pointeur par rapport à la surcharge de ce qui serait un programme massif (qui doit être stocké en mémoire, rappelez-vous!) Si les pointeurs ne pouvaient pas être utilisés .

Cette considération s'applique à tous les programmes, car personne ne veut construire un désordre horrible et impossible à entretenir avec du code dupliqué à gauche et au centre, c'est vingt fois plus grand qu'il ne devrait l'être.


5

Dans des langages comme C et C ++, tout en utilisant des pointeurs vers des variables, nous avons besoin d'un emplacement mémoire supplémentaire pour stocker cette adresse. N'est-ce pas une surcharge de mémoire?

Vous supposez que le pointeur doit être stocké. Ce n'est pas toujours le cas. Chaque variable est stockée à une adresse mémoire. Disons que vous avez longdéclaré un long n = 5L;. Cela alloue du stockage nà une certaine adresse. Nous pouvons utiliser cette adresse pour faire des choses fantaisistes comme *((char *) &n) = (char) 0xFF;pour manipuler des parties de n. L'adresse de nn'est stockée nulle part en tant que surcharge supplémentaire.

Comment est-ce compensé?

Même si les pointeurs sont explicitement stockés (par exemple dans des structures de données telles que des listes), la structure de données résultante est souvent plus élégante (plus simple, plus facile à comprendre, plus facile à manipuler, etc.) qu'une structure de données équivalente sans pointeurs.

Les pointeurs sont-ils utilisés dans des applications à faible mémoire critique?

Oui. Les appareils qui utilisent des microcontrôleurs contiennent souvent très peu de mémoire, mais le micrologiciel peut utiliser des pointeurs pour gérer les vecteurs d'interruption ou la gestion des tampons, etc.


Certaines variables ne sont pas stockées en mémoire, mais uniquement dans les registres
Basile Starynkevitch

@BasileStarynkevitch: Vous pouvez suggérer que les variables restent dans les registres, mais le compilateur n'est pas obligé de le faire. À moins que vous ne programmiez en assembleur, vous n'avez vraiment pas ce niveau de contrôle sur le stockage immédiat. Et même dans l'assembleur, tout sous-programme que vous invoquez renversera probablement les registres sur la pile afin qu'il puisse les utiliser pour ses variables. Donc, pour tout programme non trivial, il est presque garanti que vos variables passeront au moins un certain temps en mémoire.
TMN

Le compilateur peut être autorisé à stocker certaines variables locales dans des registres uniquement, et la plupart des compilateurs d' optimisation le font (essayez gcc -fverbose-asm -S -O2de compiler du code C)
Basile Starynkevitch

@BasileStarynkevitch Je ne suis pas sûr du point que vous essayez de faire avec l'observation que les variables peuvent être stockées uniquement dans des registres. Pourriez-vous s'il vous plaît développer?
Lawrence

5

Avoir un pointeur consomme certainement des frais généraux, mais vous pouvez également voir l'avantage: le pointeur est comme l'index. En C, vous pouvez utiliser des structures de données complexes comme des chaînes et des structures en raison de pointeurs uniquement.

En fait, supposons que vous vouliez passer une variable par référence, il est facile de maintenir un pointeur plutôt que de répliquer toute la structure et de synchroniser les changements entre eux (même pour les copier, vous aurez besoin d'un pointeur). Comment feriez-vous face aux allocations et désallocations de mémoire non contiguës sans pointeur?

Même vos variables normales ont une entrée dans la table des symboles qui stocke l'adresse vers laquelle votre variable pointe. Donc, je ne pense pas que cela crée beaucoup de surcharge en termes de mémoire (seulement 4 ou 8 octets). Même les langages comme java utilisent des pointeurs en interne (référence), ils ne vous permettent tout simplement pas de les manipuler car cela rendrait la JVM moins sécurisée.

Vous ne devez utiliser des pointeurs que lorsque vous n'avez pas d'autre choix comme les types de données manquants, les structures (en c) car l'utilisation de pointeurs peut entraîner des erreurs si elle n'est pas gérée correctement et est relativement plus difficile à déboguer.


3

N'est-ce pas une surcharge de mémoire?

Oui Non peut-être?

C'est une question délicate, car imaginez la plage d'adressage de la mémoire sur la machine et un logiciel qui doit constamment garder une trace de l'endroit où les choses sont en mémoire d'une manière qui ne peut pas être liée à la pile.

Par exemple, imaginez un lecteur de musique où le fichier de musique est chargé sur un bouton poussé par l'utilisateur et déchargé de la mémoire volatile lorsque l'utilisateur essaie de charger un autre fichier de musique.

Comment pouvons-nous savoir où sont stockées les données audio? Nous avons besoin d'une adresse mémoire pour cela. Le programme doit non seulement garder une trace du morceau de données audio en mémoire, mais aussi où il se trouve en mémoire. Nous devons donc conserver une adresse mémoire (c'est-à-dire un pointeur). Et la taille du stockage requis pour l'adresse mémoire va correspondre à la plage d'adressage de la machine (ex: pointeur 64 bits pour une plage d'adressage 64 bits).

C'est donc un peu "oui", cela nécessite un stockage pour garder une trace d'une adresse mémoire, mais ce n'est pas comme si nous pouvions l'éviter pour une mémoire allouée dynamiquement de ce type.

Comment est-ce compensé?

En parlant uniquement de la taille d'un pointeur lui-même, vous pouvez éviter le coût dans certains cas en utilisant la pile, par exemple. Dans ce cas, les compilateurs peuvent générer des instructions qui codent en dur l'adresse de mémoire relative, évitant ainsi le coût d'un pointeur. Pourtant, cela vous rend vulnérable aux débordements de pile si vous le faites pour de grandes allocations de taille variable, et a également tendance à être impossible (sinon carrément impossible) à faire pour une série complexe de branches pilotées par l'entrée utilisateur (comme dans l'exemple audio au dessus de).

Une autre façon consiste à utiliser des structures de données plus contiguës. Par exemple, une séquence basée sur un tableau peut être utilisée à la place d'une liste à double liaison qui nécessite deux pointeurs par nœud. Nous pouvons également utiliser un hybride de ces deux comme une liste déroulée qui ne stocke que des pointeurs entre chaque groupe contigu de N éléments.

Les pointeurs sont-ils utilisés dans des applications à faible mémoire critique?

Oui, très souvent, car de nombreuses applications critiques pour les performances sont écrites en C ou C ++ qui sont dominées par l'utilisation du pointeur (elles peuvent être derrière un pointeur intelligent ou un conteneur comme std::vectorou std::string, mais la mécanique sous-jacente se résume à un pointeur utilisé pour garder l'adresse d'un bloc de mémoire dynamique).

Revenons maintenant à cette question:

Comment est-ce compensé? (Deuxième partie)

Les pointeurs sont généralement très bon marché, sauf si vous en stockez comme un million (ce qui est toujours un maigre * 8 mégaoctets sur une machine 64 bits).

* Notez que Ben a souligné qu'un "maigre" 8 Mo est toujours la taille du cache L3. Ici, j'ai utilisé "maigre" davantage dans le sens de l'utilisation totale de la DRAM et de la taille relative typique des morceaux de mémoire vers laquelle une utilisation saine des pointeurs indiquera.

Les pointeurs qui coûtent cher ne sont pas les pointeurs eux-mêmes, mais:

  1. Allocation dynamique de mémoire. L'allocation dynamique de mémoire a tendance à être coûteuse car elle doit passer par une structure de données sous-jacente (ex: alloué copain ou dalle). Même si ceux-ci sont souvent optimisés à mort, ils sont polyvalents et conçus pour gérer des blocs de taille variable qui nécessitent qu'ils effectuent au moins un peu de travail ressemblant à une "recherche" (bien que léger et peut-être même à temps constant) pour trouver un ensemble gratuit de pages contiguës en mémoire.

  2. Accès mémoire. Cela a tendance à être le plus gros frais généraux à s'inquiéter. Chaque fois que nous accédons à la mémoire allouée dynamiquement pour la première fois, il y a une erreur de page obligatoire ainsi que des échecs de cache en déplaçant la mémoire dans la hiérarchie de la mémoire et dans un registre.

Accès à la mémoire

L'accès à la mémoire est l'un des aspects les plus critiques des performances au-delà des algorithmes. De nombreux domaines critiques pour les performances, tels que les moteurs de jeux AAA, consacrent une grande partie de leur énergie à des optimisations orientées données qui se résument à des schémas d'accès à la mémoire et des dispositions plus efficaces.

L'une des plus grandes difficultés de performance des langages de niveau supérieur qui souhaitent allouer chaque type défini par l'utilisateur séparément via un garbage collector, par exemple, est qu'ils peuvent fragmenter un peu la mémoire. Cela peut être particulièrement vrai si tous les objets ne sont pas alloués en même temps.

Dans ces cas, si vous stockez une liste d'un million d'instances d'un type d'objet défini par l'utilisateur, l'accès à ces instances séquentiellement dans une boucle peut être assez lent car il est analogue à une liste d'un million de pointeurs qui pointent vers des régions de mémoire disparates. Dans ces cas, l'architecture souhaite récupérer la mémoire sous des niveaux supérieurs, plus lents et plus grands de la hiérarchie dans de gros morceaux alignés dans l'espoir que les données environnantes dans ces morceaux seront accessibles avant l'expulsion. Lorsque chaque objet dans une telle liste est alloué séparément, nous finissons souvent par le payer avec des échecs de cache lorsque chaque itération suivante peut être chargée à partir d'une zone complètement différente en mémoire sans qu'aucun objet adjacent ne soit accessible avant l'expulsion.

Beaucoup de compilateurs pour de telles langues font un très bon travail ces jours-ci dans la sélection des instructions et l'allocation des registres, mais le manque de contrôle plus direct sur la gestion de la mémoire ici peut être mortel (bien que souvent moins sujet aux erreurs) et toujours créer des langues comme C et C ++ assez populaires.

Optimisation indirecte de l'accès au pointeur

Dans les scénarios les plus critiques pour les performances, les applications utilisent souvent des pools de mémoire qui regroupent la mémoire à partir de blocs contigus pour améliorer la localité de référence. Dans de tels cas, même une structure liée comme un arbre ou une liste liée peut être rendue compatible avec le cache à condition que la disposition de la mémoire de ses nœuds soit de nature contiguë. Cela rend effectivement le déréférencement de pointeur moins cher, quoique indirectement en améliorant la localité de référence impliquée lors du déréférencement.

Chasser les pointeurs autour

Supposons que nous ayons une liste à liaison unique comme:

Foo->Bar->Baz->null

Le problème est que si nous allouons tous ces nœuds séparément par rapport à un allocateur à usage général (et éventuellement pas tous en même temps), la mémoire réelle peut être dispersée un peu comme ceci (diagramme simplifié):

entrez la description de l'image ici

Lorsque nous commençons à rechercher des pointeurs et à accéder au Foonœud, nous commençons par un échec obligatoire (et éventuellement un défaut de page) en déplaçant un morceau de sa région de mémoire des régions de mémoire plus lentes vers des régions de mémoire plus rapides, comme ceci:

entrez la description de l'image ici

Cela nous amène à mettre en cache (éventuellement aussi à la page) une région de mémoire uniquement pour accéder à une partie de celle-ci et à expulser le reste pendant que nous poursuivons des pointeurs autour de cette liste. En prenant le contrôle de l'allocateur de mémoire, cependant, nous pouvons allouer une telle liste de manière contiguë comme ceci:

entrez la description de l'image ici

... et ainsi améliorer considérablement la vitesse à laquelle nous pouvons déréférencer ces pointeurs et traiter leurs pointes. Ainsi, bien que très indirect, nous pouvons accélérer l'accès au pointeur de cette façon. Bien sûr, si nous les stockions simplement de manière contiguë dans un tableau, nous n'aurions pas ce problème en premier lieu, mais l'allocateur de mémoire ici nous donnant un contrôle explicite sur la disposition de la mémoire peut sauver le jour où une structure liée est requise.

* Remarque: il s'agit d'un diagramme et d'une discussion très simplifiés sur la hiérarchie de la mémoire et la localité de référence, mais j'espère que cela convient au niveau de la question.


1
"maigre 8 mégaoctets" est la taille typique de tout le cache L3 sur un processeur moderne
Ben Voigt

1
@BenVoigt Je vais essayer de lever l'ambiguïté un peu. Bien sûr, ce serait horrible si chaque pointeur pointait vers un morceau de mémoire de 32 bits, par exemple

@BenVoigt Ajout d'une note de bas de page pour essayer de clarifier cette partie!

Cette clarification semble bonne.
Ben Voigt

1

N'est-ce pas une surcharge de mémoire?

Il s'agit en effet d'une surcharge de mémoire, mais très petite (au point d'être insignifiante).

Comment est-ce compensé?

Il n'est pas compensé. Vous devez réaliser que l'accès aux données via un pointeur (déréférencer un pointeur) est extrêmement rapide (si je me souviens bien, il utilise une seule instruction d'assemblage par déréférence). Il est suffisamment rapide pour être dans bien des cas l'alternative la plus rapide dont vous disposez.

Les pointeurs sont-ils utilisés dans des applications à faible mémoire critique?

Oui.


0

Vous n'avez besoin que de l'utilisation supplémentaire de la mémoire (4 à 8 octets par pointeur, généralement) pendant que vous en avez besoin. Il existe de nombreuses techniques qui rendent cela plus abordable.

La technique la plus fondamentale qui rend les pointeurs puissants est que vous n'avez pas besoin de conserver chaque pointeur. Parfois, vous pouvez utiliser un algorithme pour construire un pointeur à partir d'un pointeur vers autre chose. L'exemple le plus trivial de ceci est l'arithmétique des tableaux. Si vous allouez un tableau de 50 entiers, vous n'avez pas besoin de conserver 50 pointeurs, un pour chaque entier. Vous gardez généralement un pointeur (le premier) et utilisez l'arithmétique du pointeur pour générer les autres à la volée. Parfois, vous pouvez conserver temporairement l'un de ces pointeurs vers un élément spécifique du tableau, mais uniquement pendant que vous en avez besoin. Une fois que vous avez terminé, vous pouvez le supprimer, tant que vous avez conservé suffisamment d'informations pour le régénérer plus tard, si vous en avez besoin. Cela peut sembler trivial, mais c'est exactement le type d'outils de conservation que vous '

Dans les situations de mémoire extrêmement serrée, cela peut être utilisé pour minimiser les coûts. Si vous travaillez dans un espace mémoire très restreint, vous avez généralement une bonne idée du nombre d'objets que vous devez manipuler. Au lieu d'allouer un groupe d'entiers un par un et de garder des pointeurs complets vers eux, vous pouvez profiter de votre connaissance de développeur que vous n'aurez jamais plus de 256 entiers dans cet algorithme particulier. Dans ce cas, vous pouvez conserver un pointeur sur le premier entier et suivre un index à l'aide d'un caractère (1 octet) plutôt que d'utiliser un pointeur complet (4/8 octets). Vous pouvez également utiliser des astuces algorithmiques pour générer certains de ces indices à la volée.

Ce genre de conscience de mémoire était très populaire dans le passé. Par exemple, les jeux NES s'appuieraient largement sur leur capacité à créer des données et à générer des pointeurs de manière algorithmique plutôt que d'avoir à les stocker tous en gros.

Les situations de mémoire extrêmes peuvent également conduire à faire des choses comme l'allocation de tous les espaces sur lesquels vous opérez au moment de la compilation. Ensuite, le pointeur que vous devez stocker dans cette mémoire est stocké dans le programme plutôt que dans les données. Dans de nombreuses situations de mémoire limitée, vous avez un programme et une mémoire de données séparés (souvent ROM vs RAM), vous pouvez donc être en mesure d'ajuster la façon dont vous utilisez votre algorithme pour pousser les pointeurs dans cette mémoire de programme.

Fondamentalement, vous ne pouvez pas vous débarrasser de tous les frais généraux. Cependant, vous pouvez le contrôler. En utilisant des techniques algorithmiques, vous pouvez minimiser le nombre de pointeurs que vous pouvez stocker. Si vous utilisez des pointeurs vers la mémoire dynamique, vous n'obtiendrez jamais en dessous du coût de la conservation d'un pointeur vers cet emplacement de mémoire dynamique, car c'est la quantité minimale d'informations nécessaires pour accéder à quoi que ce soit dans ce bloc de mémoire. Cependant, dans les scénarios de contraintes de mémoire ultra-resserrées, cela a tendance à être le cas spécial (les contraintes de mémoire dynamique et de mémoire ultra-resserrées ont tendance à ne pas apparaître dans les mêmes situations).


0

Dans de nombreuses situations, les pointeurs économisent de la mémoire. Une alternative courante à l'utilisation de pointeurs est de faire une copie d'une structure de données. Une copie complète d'une structure de données sera plus grande qu'un pointeur.

Un exemple d'application à temps critique est une pile réseau. Une bonne pile réseau sera conçue pour être "zéro copie" - et pour ce faire, il faut utiliser intelligemment des pointeurs.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.