Une chose qui m'a toujours intuitivement frappé comme une caractéristique positive de C (enfin, en fait de ses implémentations comme gcc, clang, ...) est le fait qu'il ne stocke aucune information cachée à côté de vos propres variables lors de l'exécution. J'entends par là que si vous vouliez par exemple une variable "x" de type "uint16_t", vous pourriez être sûr que "x" n'occupera que 2 octets d'espace (et ne portera aucune information cachée comme son type, etc. .). De même, si vous vouliez un tableau de 100 entiers, vous pouvez être sûr qu'il est aussi grand que 100 entiers.
Cependant, plus je suis en train de trouver des cas concrets d'utilisation de cette fonction plus je me demande si elle a fait des avantages pratiques du tout. La seule chose que j'ai pu trouver jusqu'à présent est qu'il a évidemment besoin de moins de RAM. Pour les environnements limités, comme les puces AVR, etc., c'est certainement un énorme avantage, mais pour les cas d'utilisation de bureau / serveur de tous les jours, cela semble plutôt hors de propos. Une autre possibilité à laquelle je pense est qu'elle pourrait être utile / cruciale pour accéder au matériel, ou peut-être mapper des régions de mémoire (par exemple pour la sortie VGA et autres) ...?
Ma question: existe-t-il des domaines concrets qui ne peuvent pas ou ne peuvent être mis en œuvre que très lourdement sans cette fonctionnalité?
PS S'il vous plaît, dites-moi si vous avez un meilleur nom! ;)
la source
virtual
fonction membre. Ainsi, RTTI n'augmente jamais la taille des objets, il ne fait qu'agrandir le binaire d'une constante.T *
toujours la même taille etT
peut contenir un champ caché qui pointe vers la table virtuelle. Et aucun compilateur C ++ n'a jamais inséré de vtables dans des objets qui n'en ont pas besoin.Réponses:
Il y a plusieurs avantages, le plus évident étant au moment de la compilation pour s'assurer que des choses comme les paramètres de fonction correspondent aux valeurs transmises.
Mais je pense que vous demandez ce qui se passe à l'exécution.
Gardez à l'esprit que le compilateur créera un runtime qui intègre la connaissance des types de données dans les opérations qu'il effectue. Chaque bloc de données en mémoire peut ne pas être auto-descriptif, mais le code sait intrinsèquement ce que sont ces données (si vous avez fait votre travail correctement).
Au moment de l'exécution, les choses sont un peu différentes de ce que vous pensez.
Par exemple, ne supposez pas que seuls deux octets sont utilisés lorsque vous déclarez uint16_t. Selon le processeur et l'alignement des mots, il peut occuper 16, 32 ou 64 bits sur la pile. Vous constaterez peut-être que votre gamme de courts métrages consomme beaucoup plus de mémoire que prévu.
Cela peut être problématique dans certaines situations où vous devez référencer des données à des décalages spécifiques. Cela se produit lors de la communication entre deux systèmes ayant des architectures de processeur différentes, soit via une liaison sans fil, soit via des fichiers.
C vous permet de spécifier des structures avec une granularité au niveau du bit:
Cette structure est longue de trois octets, avec un court défini pour commencer à un décalage impair. Il devra également être emballé afin d'être exactement comme vous l'avez défini. Sinon, le compilateur alignera les membres par mot.
Le compilateur générera du code en arrière-plan pour extraire ces données et les copier dans un registre afin que vous puissiez faire des choses utiles avec.
Vous pouvez maintenant voir que chaque fois que mon programme accède à un membre de la structure myMessage, il saura exactement comment l'extraire et l'utiliser.
Cela peut devenir problématique et difficile à gérer lors de la communication entre différents systèmes avec différentes versions de logiciels. Vous devez soigneusement concevoir le système et le code pour vous assurer que les deux côtés ont exactement la même définition des types de données. Cela peut être assez difficile dans certains environnements. C'est là que vous avez besoin d'un meilleur protocole qui contient des données auto-descriptives telles que les tampons de protocole de Google .
Enfin, vous faites un bon point pour demander à quel point cela est important dans l'environnement de bureau / serveur. Cela dépend vraiment de la quantité de mémoire que vous prévoyez d'utiliser. Si vous effectuez quelque chose comme le traitement d'image, vous pouvez finir par utiliser une grande quantité de mémoire, ce qui peut affecter les performances de votre application. C'est certainement toujours une préoccupation dans l'environnement embarqué où la mémoire est limitée et il n'y a pas de mémoire virtuelle.
la source
short
. Mais c'est une exigence unique pour le début du tableau, le reste est automatiquement aligné correctement en raison d'être consécutif.uint8_t padding: 6;
, tout comme les deux premiers bits. Ou, plus clairement, juste le commentaire//6 bits of padding inserted by the compiler
. La structure, comme vous l'avez écrite, a une taille d'au moins neuf octets, pas trois.Vous avez trouvé l'une des seules raisons pour lesquelles cela est utile: la cartographie des structures de données externes. Ceux -ci incluent des tampons vidéo mappés en mémoire, registres matériels, etc. Ils incluent également des données transmises à l' extérieur intact le programme, comme les certificats SSL, les paquets IP, des images JPEG, et à peu près toute autre structure de données qui a une vie persistante en dehors du programme.
la source
C est un langage de bas niveau, presque un assembleur portable, donc ses structures de données et ses constructions de langage sont proches du métal (les structures de données n'ont pas de coûts cachés - à l'exception des contraintes de remplissage, d'alignement et de taille imposées par le matériel et ABI ). Donc C n'a en effet pas de typage dynamique en natif. Mais si vous en avez besoin, vous pouvez adopter une convention selon laquelle toutes vos valeurs sont des agrégats commençant par des informations de type (par exemple certaines
enum
...); utilisezunion
-s et (pour les choses de type tableau) un membre de tableau flexible enstruct
contenant également la taille du tableau.(lors de la programmation en C, il est de votre responsabilité de définir, documenter et suivre les conventions utiles - notamment les conditions préalables et postérieures et les invariants; également l'allocation dynamique de mémoire C nécessite des conventions explicites sur qui devrait
free
unemalloc
zone de mémoire empilée)Ainsi, pour représenter des valeurs qui sont des nombres entiers ou des chaînes en boîte, ou une sorte de schéma -comme symbole , ou des vecteurs de valeurs, vous utiliserez conceptuellement une union étiquetée (mis en œuvre en tant qu'union de pointeurs) -Toujours à partir par le genre de type -, par exemple:
Pour obtenir le type dynamique d'une valeur
Voici une "distribution dynamique" aux vecteurs:
et un "accesseur sûr" à l'intérieur des vecteurs:
Vous définissez généralement la plupart des fonctions courtes ci-dessus comme
static inline
dans certains fichiers d'en-tête.BTW, si vous pouvez utiliser le garbage collector de Boehm, vous pouvez alors coder assez facilement dans un style de niveau supérieur (mais dangereux), et plusieurs interpréteurs Scheme sont effectués de cette façon. Un constructeur de vecteur variadique pourrait être
et si vous avez trois variables
vous pouvez construire un vecteur à partir d'eux en utilisant
make_vector(3,v1,v2,v3)
Si vous ne voulez pas utiliser le garbage collector de Boehm (ou concevoir le vôtre), vous devez faire très attention à définir les destructeurs et à documenter qui, comment et quand la mémoire doit être
free
-d; voir cet exemple. Vous pouvez donc utilisermalloc
(mais ensuite tester contre son échec) au lieu deGC_MALLOC
ci - dessus, mais vous devez soigneusement définir et utiliser une fonction de destructeurvoid destroy_value(value_t)
La force de C est d'être suffisamment bas pour rendre possible le code comme ci-dessus et définir vos propres conventions (particulières à votre logiciel).
la source