C: pointeur vers tableau de pointeurs vers des structures (allocation/désallocation de questions)
J'ai été faire dans C pour quelque chose, mais je vais avoir du mal à se souvenir de beaucoup de la façon dont cette gestion de la mémoire des œuvres. J'aimerais avoir un pointeur vers un tableau de pointeurs vers des structures.
Dire que j'ai:
struct Test {
int data;
};
Puis le tableau:
struct Test **array1;
Est-ce correct? Ma question est de travailler avec ce truc. Ainsi, chaque pointeur dans le tableau de points à quelque chose qui est attribué séparément. Mais je pense que j'ai besoin pour ce faire d'abord:
array1 = malloc(MAX * sizeof(struct Test *));
Je vais avoir de la difficulté à comprendre la ci-dessus. Ai-je besoin de le faire, et pourquoi dois-je faire? En particulier, ce qui signifie pour allouer de la mémoire pour les pointeurs si je vais être de l'allocation de mémoire pour chaque chose que le pointeur pointe vers?
Dire maintenant que j'ai pointeur vers un tableau de pointeurs vers des structures. Je veux maintenant elle pointe vers le même tableau que j'ai créé précédemment.
struct Test **array2;
Faire j'ai besoin d'allouer de la salle pour les pointeurs comme je l'ai fait ci-dessus, ou puis-je simplement faire:
array2 = array1
- jetez un oeil à stackoverflow.com/questions/11421884/...
- Voulez-vous un véritable tableau de pointeurs vers les structures? Comme dans une déclaration de tableau où vous allouer à chaque élément une struct?
- Eh bien, je veux un pointeur vers un tableau où je peux accomplir ce que vous avez dit.
- Je sais. Mais voulez-vous un vrai tableau de tenir? Plutôt que de simplement un pointeur vers un bloc de mémoire, je veux dire.
- Je pense qu'ils vont se comporter de la même pour mes besoins, donc non, je ne pense pas que j'ai besoin d'un "vrai" tableau.
- C'est juste plus simple avec un tableau, je peux poster un exemple si vous le souhaitez.
- Doit chaque emplacement dans la matrice de point à une allocation distincte? C'est finalement la question que vous devez poser. Si la réponse est non (I. e. "Je veux un tableau alloué dynamiquement de 50 structures" vs "je veux un tableau de 50 allouée dynamiquement les structures"). Pour être honnête, je ne vois guère de raison de la preuve pour suggérer que vous besoin de quelque chose au-delà d'une simple dimension linéaire de l'allocation, que je vais développer dans une réponse si nécessaire.
- J'ai mis les deux cas vers le bas (espérons-le droit, la tête me fait mal maintenant)
- ouais, il ressemble à droite. Le seul cas, il manque le tableau dynamique de la structure dynamique des allocations, qui a généralement besoin d'
realloc()
de gestion. mais je pense que vous avez couvert les choses importantes. - Fixe, espérons-le!
Vous devez vous connecter pour publier un commentaire.
Alloué Tableau
Avec attribuées sur le tableau, il est assez simple à suivre.
Déclarer votre tableau de pointeurs. Chaque élément de ce tableau de points pour un
struct Test
:Puis répartir et attribuer les pointeurs vers les structures toutefois vous le souhaitez. À l'aide d'une boucle serait simple:
Déclarer un pointeur vers ce tableau:
Cela vous permet d'utiliser
(*p)[n]->data
; pour faire référence à la nième membre.Ne vous inquiétez pas si ce truc est source de confusion. C'est probablement l'aspect le plus difficile de C.
Dynamique Linéaire De La Matrice De
Si vous voulez juste pour allouer un bloc de structures (un tableau de structures, pas des pointeurs vers des structures), et un pointeur vers le bloc, vous pouvez le faire plus facilement:
Vous pouvez ensuite pointer vers ce pointeur:
Vous n'avez pas un tableau de pointeurs vers des structures plus, mais elle simplifie l'ensemble de la chose considérablement.
Tableau dynamique de Alloués Dynamiquement les Structures
La plus flexible, mais ne sont généralement pas nécessaires. C'est très similaire à la première exemple, mais qui nécessite une allocation supplémentaire. J'ai écrit un programme complet pour démontrer ce qui devrait compiler amende.
De sortie:
Ou l'ensemble:
Dynamique Pointeur de Tableau de Simple-Dynamique Alloués aux Structures
Ce dernier exemple est assez spécifique. C'est un tableau dynamique de pointeurs comme nous l'avons vu dans les exemples précédents, mais à la différence de ceux-ci, les éléments sont tous affectés dans un unique allocation. Cela a ses utilise, le plus notable pour le tri des données dans des configurations différentes, tout en laissant l'allocation initiale sans être dérangé.
Nous commençons par l'attribution d'un seul bloc d'éléments comme nous le faisons dans les plus élémentaires de bloc unique de répartition:
Maintenant, nous allouons un séparé bloc de pointeurs:
Nous avons ensuite remplir chaque emplacement dans notre pointeur de la liste avec l'adresse de l'un de notre tableau d'origine. Depuis l'arithmétique des pointeurs nous permet de nous déplacer d'un élément de l'adresse, c'est simple:
À ce point les suivants désignent le même élément de champ
Et après l'examen ci-dessus, j'espère que c'est clair pourquoi.
Quand nous en avons fini avec le pointeur de tableau et le bloc d'origine tableau ils sont libérés que:
Remarque: nous ne sommes PAS libres de chaque élément dans le
ptrs[]
tableau individuellement. Ce n'est pas la façon dont ils ont été attribués. Elles ont été réparties comme un seul bloc (souligné pararr
), et c'est la façon dont ils devraient être libérés.Alors pourquoi quelqu'un voudrait-il faire cela? Plusieurs raisons.
Tout d'abord, il réduit radicalement le nombre de l'allocation de la mémoire des appels. Plutôt que de
N+1
(un pour le tableau de pointeurs, N pour des structures individuelles) vous avez maintenant seulement deux: l'un pour la matrice bloc, et un pour le tableau de pointeurs. Les allocations de mémoire sont l'un des plus chers des opérations un programme peut demander et, si possible, il est souhaitable de les minimiser (remarque: le fichier IO est un autre, pour info).Une autre raison: Plusieurs représentations d'une même base de tableau de données. Supposons que vous vouliez de trier les données en ordre croissant et décroissant, et à la fois triés représentations disponibles en même temps. Vous pourriez reproduire le tableau de données, mais cela demanderait beaucoup de copie et de manger significative de l'utilisation de la mémoire. Au lieu de cela, juste allouer un supplément de pointeur de tableau et le remplir avec des adresses de la base de la matrice, puis de sorte que le pointeur de tableau. Cela a surtout des avantages importants lorsque les données triées est grande (peut-être kilo-octets, voire plus élevé, élément par élément) Les éléments d'origine restent dans leur emplacement d'origine dans la base de tableau, mais maintenant, vous avez un très efficace mécanisme dans lequel vous pouvez les trier, sans avoir à réellement déplacer eux. Vous triez le tableau de pointeurs vers des éléments; les éléments n'obtenez pas déplacé du tout.
Je réalise que j'ai énormément de choses à prendre, mais l'utilisation du pointeur est essentielle pour comprendre les nombreuses et puissantes choses que vous pouvez faire avec le langage C, donc le nez dans les livres et toujours rafraîchir votre mémoire. Il va revenir.
Il peut être préférable de déclarer un tableau réel, comme d'autres l'ont suggéré, mais votre question semble être plus à propos de la gestion de la mémoire donc je vais en discuter.
C'est un pointeur vers l'adresse d'un
struct Test
. (Pas un pointeur vers la structure elle-même; c'est un pointeur sur un emplacement mémoire qui contient la adresse de la structure.) La déclaration alloue de la mémoire pour le pointeur, mais pas pour les articles, il points de. Depuis un tableau peut être consulté via les pointeurs, vous pouvez travailler avec*array1
comme un pointeur vers un tableau dont les éléments sont de typestruct Test
. Mais il n'est pas encore un tableau réel pour qu'il désigne.Ce alloue de la mémoire pour contenir
MAX
des pointeurs vers des objets de typestruct Test
. Encore une fois, il ne pas allouer de la mémoire pour les structures elles-mêmes; seulement pour une liste de pointeurs. Mais maintenant, vous pouvez traiterarray
comme un pointeur vers un alloués tableau de pointeurs.Afin d'utiliser
array1
, vous avez besoin pour créer la des structures. Vous pouvez le faire simplement en déclarant chaque struct avecVous pouvez également affecter les structures sur le tas:
Une fois que vous avez alloué de la mémoire, vous pouvez créer une nouvelle variable qui pointe vers la même liste de structures:
Vous n'avez pas besoin d'allouer de la mémoire supplémentaire, parce que
array2
points à la même mémoire que vous avez alloué àarray1
.Parfois, vous voulez à avoir un pointeur vers une liste de pointeurs, mais à moins que vous êtes en train de faire quelque chose de fantaisie, vous pouvez être en mesure d'utiliser
Il déclare le pointeur
array1
, alloué assez de mémoire pourMAX
des structures, et des points dearray1
pour que la mémoire. Maintenant, vous pouvez accéder à des structures comme ceci:Alors, quelle est la différence? Un certain nombre de choses. Clairement, la première méthode nécessite d'allouer de la mémoire pour les pointeurs, et ensuite d'allouer de l'espace supplémentaire pour les structures elles-mêmes; la seconde permet de sortir avec un
malloc()
appel. Quel est le supplément de travail, vous acheter?Depuis la première méthode vous donne un véritable tableau de pointeurs vers
Test
les structures, chaque pointeur peut pointer vers n'importe quelTest
struct, n'importe où dans la mémoire; ils ne doivent être contiguës. En outre, vous pouvez allouer et libérer de la mémoire pour chaqueTest
struct que nécessaire, et vous pouvez réaffecter les pointeurs. Ainsi, par exemple, vous pouvez permuter les deux structures par simple échange de leurs pointeurs:D'autre part, la deuxième méthode alloue un seul bloc contigu de mémoire pour l'ensemble de la
Test
les structures et les partitions enMAX
éléments. Et chaque élément du tableau se trouve à une position fixe; la seule façon d'échanger deux structures est de les copier.Les pointeurs sont l'un des plus utiles constructions en C, mais ils peuvent aussi être parmi les plus difficiles à comprendre. Si vous prévoyez de continuer à l'aide de C, il va probablement être un investissement rentable de passer du temps à jouer avec les pointeurs, les tableaux, et un débogueur jusqu'à ce que vous êtes à l'aise avec eux.
Bonne chance!
Je vous suggère de construire ce d'une couche à l'aide de typdefs pour créer des couches de types. Ce faisant, les différents types nécessaires seront beaucoup plus claires.
Par exemple:
Cela va créer deux nouveaux types, un pour la structure et un pour un pointeur vers une struct.
Prochain donc si vous voulez un tableau de struct ensuite, vous utilisez:
Si vous voulez un tableau de pointeurs vers des struct ensuite, vous utilisez:
Alors si vous voulez vous allouer les structures dans le tableau que vous feriez quelque chose comme:
C ne permet pas d'affecter un tableau à un autre. Vous devez plutôt utiliser une boucle à attribuer à chaque élément d'un tableau à un élément de l'autre.
EDIT: une Autre Approche Intéressante
Une autre approche serait plus que l'approche orientée objet dans lequel vous encapsuler un peu les choses. Par exemple, en utilisant les mêmes couches de types, nous devons créer deux types:
Ensuite, nous avons une fonction d'aide qui nous permet de créer l'objet, nommé de façon assez appropriée
CreateTestData (int nArrayCount)
.Maintenant, nous pouvons utiliser notre objet comme dans le code source segment ci-dessous. Il doit vérifier que le pointeur retourné à partir de CreateTestData() est valide, cependant, c'est vraiment juste pour montrer ce qui pourrait être fait.
Dans une véritable dynamique de l'environnement, vous pouvez également avoir un
ReallocTestData(PTestData p)
fonction qui permettrait de réaffecter uneTestData
objet pour modifier la taille du tableau contenu dans l'objet.Avec cette approche, lorsque vous avez terminé avec un particulier TestData objet, vous pouvez juste libre de l'objet que dans
free (go)
et de l'objet et de son tableau sont tous deux libérés en même temps.Edit: Extension De Plus De
Avec ce type encapsulé nous pouvons maintenant faire un peu d'autres choses intéressantes. Par exemple, nous pouvons avoir une fonction de copie,
PTestType CreateCopyTestData (PTestType pSrc)
qui permettrait de créer une nouvelle instance, puis copier l'argument d'un nouvel objet. Dans l'exemple suivant, nous réutilisons la fonctionPTestType CreateTestData (int nCount)
qui permettra de créer une instance de notre type, à l'aide de la taille de l'objet à copier. Après avoir fait de la création d'un nouvel objet, nous faire une copie des données à partir de l'objet source. La dernière étape consiste à fixer le pointeur dans la source de points de l'objet de sa zone de données de sorte que le pointeur dans le nouvel objet maintenant les points de la zone de données de lui-même plutôt que de la zone de données de l'ancien objet.Les structures ne sont pas très différents des autres objets. Commençons par les personnages:
*p est un personnage, donc
sizeof *p
est sizeof (char) == 1; nous avons alloué CNT caractères. Prochaine:*p est un pointeur de caractère, de sorte
sizeof *pp
est sizeof (char*). Nous avons alloué des CNT pointeurs. Prochaine:*p est un struct quelque chose, de sorte
sizeof *p
est sizeof (struct quelque chose). Nous avons alloué des CNT struct somethings. Prochaine:*pp est un pointeur vers une struct, donc
sizeof *pp
est sizeof (struct quelque chose*). Nous avons alloué des CNT pointeurs.sizeof
existe.