8
votes

Pourquoi C a-t-il besoin de tableaux s'il a des pointeurs?

Si nous pouvons utiliser des pointeurs et MALLOC pour créer et utiliser des tableaux, pourquoi le type de matrice existe-t-il dans C? N'est-il pas inutile si nous pouvons utiliser des pointeurs à la place?


4 commentaires

Qu'entendez-vous par «type de tableau»? int x [6]; ?


Je ne savais pas qu'il y avait un type "tableau" en C.


Je prépare cette question plus parce qu'il existe une discussion intéressante dans les réponses.


Il s'agit d'une question parfaitement valable de la curiosité réfléchie. Up voté +1.


6 Réponses :


10
votes

Les tableaux sont plus rapides que la répartition de la mémoire dynamique.

Les tableaux sont "alloués" à "l'heure de la compilation", tandis que MALLOC attribue au moment de l'exécution. Alloué prend du temps. P>

En outre, c N'andonne pas que MALLOC () code> et des amis sont disponibles dans des implémentations autonomes. P>


Modifier em> p>

Exemple de tableau P>

int main(void) {
    size_t len = 52;
    int *deck = malloc(len * sizeof *deck);
    if (deck) {
        play(deck, len);
    }
    free(deck);
    return 0;
}


3 commentaires

L'allocation des deux arrive au temps d'exécution. La différence est que c vous permet de fournir la taille au moment de l'exécution pour un mais pas l'autre.


Pouvez-vous me donner plus de détails ce qui se passe dans le temps de compilation et le temps d'exécution lorsqu'un tableau est situé? Même un exemple serait utile. Merci.


@Arak, je pense que par répartition au moment de l'exécution, il signifie que le logiciel doit demander la mémoire du système d'exploitation (avec l'appel à Malloc ), par opposition à simplement ajuster le pointeur de pile (et Ainsi "alloué" à "compiler l'heure").



1
votes

Les tableaux ont leurs utilisations et doivent être utilisés quand vous le pouvez, car l'allocation statique aidera à rendre des programmes plus stables et que la nécessité est parfois nécessaire de s'assurer que les fuites de mémoire ne se produisent pas.

Ils existent parce que certaines exigences les nécessitent.

Dans une langue telle que BASIC, vous avez certaines commandes autorisées, et cela est connu, en raison de la construction de la langue. Ainsi, quel est l'avantage d'utiliser MALLOC pour créer les tableaux, puis les remplir dans des chaînes?

Si je dois définir les noms des opérations quand même, pourquoi ne pas les mettre dans un tableau?

C a été écrit comme une langue à usage général, ce qui signifie qu'il devrait être utile dans n'importe quelle situation, ils devaient donc s'assurer que les constructions étaient utiles pour écrire des systèmes d'exploitation ainsi que des systèmes embarqués.

Un tableau est un moyen sténique de spécifier le début d'un malloc par exemple.

Mais, imaginez essayer de faire des mathématiques à matrice en utilisant des manipulations de pointeur plutôt que Vec [x] * Vec [y] . Il serait très sujet à des erreurs difficiles à trouver.


0 commentaires

7
votes

Ce n'est pas une mauvaise question. En fait, le début de C n'avait aucun type de tableau.

Les matrices globales et statiques sont allouées à la compilation (très rapide). D'autres tableaux sont alloués sur la pile au moment de l'exécution (rapide). L'allocation de mémoire avec MALLOC (à utiliser pour un tableau ou autrement) est beaucoup plus lente. Une chose similaire est observée dans la classique: la mémoire allouée de manière dynamique est plus lente de traiter.

La vitesse n'est pas la seule question. Les types de tableau sont automatiquement traités lorsqu'ils ne sortent pas de la portée, ils ne peuvent pas être «fui» par erreur. Vous n'avez pas besoin de vous soucier de libérer accidentellement quelque chose deux fois, et ainsi de suite. Ils facilitent également la tâche des outils d'analyse statique de détecter des bogues.

Vous pouvez affirmer qu'il existe une fonction _ALLOCA () < / a> qui vous permet d'allouer de la mémoire de la pile. Oui, il n'y a pas de la raison technique pourquoi les tableaux sont nécessaires sur _alloca () . Cependant, je pense que les matrices sont plus pratiques à utiliser. En outre, il est plus facile pour le compilateur d'optimiser l'utilisation d'une matrice qu'un pointeur avec une valeur de retour _alloca () , car il est évident ce que le décalage d'une matrice de pile est alloué à partir du pointeur de pile est , alors que si _ALLOCA () est traité comme un appel de fonction Black-box, le compilateur ne peut pas indiquer cette valeur à l'avance.

Modifier, puisque Tsubasa a demandé plus de détails sur la manière dont cette allocation se produit:

sur les architectures x86, le registre ebp fait normalement référence au cadre de pile de la fonction actuelle et est utilisé pour référencer les variables allouées à la pile. Par exemple, vous pouvez avoir un int situé sur [EBP - 8] et un char étirement de [EBP - 24] à [EBP - 9] . Et peut-être plus de variables et de tableaux sur la pile. (Le compilateur décide comment utiliser le cadre de la pile à l'heure de la compilation. Les compilateurs de C99 permettent des matrices de taille variable d'être empilés, il s'agit simplement d'un peu de travail au moment de l'exécution.)

dans le code X86, les décalages de pointeur (tels que [EBP - 16] ) peuvent être représentés dans une seule instruction. Assez efficace.

Maintenant, un point important est que tous les variables et les tableaux d'affectation de la pile dans le contexte actuel sont récupérés via des compensations à partir d'un registre Single . Si vous appelez MALLOC, il y a (comme je l'ai dit), certains frais de traitement de la tête en trouvant une mémoire pour vous. Mais aussi, Malloc vous donne une nouvelle adresse mémoire. Disons qu'il est stocké dans le registre ebx . Vous ne pouvez pas utiliser de décalage à partir de EBP , car vous ne pouvez pas dire ce que ce décalage sera à la compilation. Donc, vous êtes fondamentalement "gaspiller" un registre supplémentaire dont vous n'auriez pas besoin si vous avez utilisé une matrice normale. Si vous Malloc Plus de tableaux, vous avez plus de valeurs de pointeur «imprévisibles» qui magnifient ce problème.


4 commentaires

"Vous inquiétez de libérer accidentellement quelque chose à deux reprises" - Supprimer un pointeur NULL est un non-OP tel que défini par la norme, c'est-à-dire que c'est sûr. Maintenant, le fait que vous appeliez Supprimer sur un pointeur NULL indique probablement un autre problème, mais l'appel lui-même est en sécurité.


@Ed Swangren: Bien qu'une pratique codante commune consiste à définir des variables de pointeur sur NULL après les libérer: a) Tout le monde ne le fait pas, et b) dans des structures de données pouvant avoir plusieurs pointeurs pointant vers la même chose, cela ne peut pas empêcher la problème


Les matrices multidimensionnelles semblent être difficiles à rendre difficiles à faire avec quelque chose comme _alloca , sans que certains frais généraux ne soient apparemment pas prêts à prendre. Aussi Tailleof ne serait pas aussi facile à mettre en œuvre pour donner un résultat temporel de compilation (le compilateur doit essentiellement en arrière le pointeur pour voir quel initialisateur a été utilisé pour ce pointeur, puis déciderait de céder la taille de la pointe du pointeur, ou du "tableau")


@LITB: Les tableaux multidimensionnels ne sont pas difficiles à faire, certains fardeaux supplémentaires du travail vont sur le programmeur (c'est-à-dire garder une trace des dimensions de la matrice elles-mêmes) mais il n'y a pas de pénalité de performance. Voir mon commentaire à votre réponse.



0
votes

Les tableaux sont une jolie amélioration de la syntaxe par rapport à la traite des pointeurs. Vous pouvez faire toutes sortes d'erreurs sans le savoir lors de la gestion des pointeurs. Et si vous déplacez trop d'espaces dans la mémoire, car vous utilisez la mauvaise taille d'octet?


5 commentaires

En C, les tableaux et les pointeurs utilisent toutes les deux la même méthode de déterminer combien d'octets à déplacer.


@Artelius, pas du tout. Les tableaux utilisent leur type pour connaître ce que le décalage à utiliser lors de l'indexation et leur propre adresse pour connaître l'adresse de base au démarrage, tandis que les indicateurs utilisent l'adresse stockée entre eux pour connaître l'adresse de base et ne savent pas les compensations (vous devez dites-leur manuellement). +1 :)


@LitB: De quoi parlez-vous? Les tableaux et les pointeurs Les deux utilisent leur signature de type pour savoir ce qui décalent de l'adresse de base à utiliser (bien que vous ayez raison que la source de cette adresse de base est différente).


Eh bien, oui, mais lorsque vous avez un tableau multi-DIM int [2] [2] , puis avancez par Tailleof (int) est faux lorsque vous faites A [1] , vous devez avancer par Tailleof (int) * 2 :)


IMO, la phrase "Utilisation de la mauvaise taille d'octet" implique que Omouse estime que le pointeur arithmétique est toujours fait en octets (une idée fausse de nombreux débutants C - Je pensais que c'était le cas moi-même à un moment donné). Les matrices multimids sont un problème, mais je ne pense pas qu'ils étaient le problème Omouse n'avait en tête. S'il avait quelque chose d'autre à l'esprit, il est le bienvenu pour le rendre clair.



1
votes

voir Cette question discutant de l'espace durcissement et C. parfois dynamique est juste une mauvaise idée, j'ai travaillé avec C bibliothèques entièrement dépourvu de malloc () et d'amis.

Vous ne voulez pas de satellite Déroferencer un pointeur NULL, plus que vous souhaitez que vous souhaitiez un logiciel de contrôle de la circulation aérienne, oubliant des blocs de tas à zéro.

c'est aussi important (comme d'autres l'ont souligné) pour comprendre ce qui fait partie de C et ce qui l'étend à diverses normes uniformes (c'est-à-dire POSIX).


0 commentaires

0
votes

Explication de Dennis Ritchie à propos de C Historique :

embryonnaire c fort> p>

NB existait si brièvement qu'aucune description complète de celle-ci n'a été écrite. Il fournissait les types Int et Char, les tableaux d'entre eux et les pointeurs qui leur sont déclarés dans un style caractérisé par p>

struct {
    int   inumber;
    char  name[14];
};


0 commentaires