2010-12-12 6 views
1

Bonjour, je suis autodidacte C et je suis un peu confus au sujet du code suivant car je ne sais pas si je comprends bien le code. Je serais très reconnaissant si quelqu'un pouvait lire mon explication et me corriger si je me trompe.Utiliser des pointeurs avec des fonctions et des structures

Le code provient d'un fichier d'en-tête. La fonction du programme devrait être inintéressante à ce stade, puisque mon problème de compréhension concerne les pointeurs et les valeurs que les fonctions rendent en retour. Donc tout d'abord je déclare 3 tableaux de char et un nombre entier dans ma structure d'employé.

struct employee 
{ 
    char firstname[11]; 
    char lastname[11]; 
    char number[11]; 
    int salary; 
} 


5 fonctions sont déclarées dans le fichier d'en-tête. La première fonction prend 4 valeurs (3 pointeurs et un int) et renvoie un pointeur vers une structure. La deuxième fonction obtient un pointeur sur le "struct struct" et renvoie un pointeur à un élément du tableau "char firstname" dans l'employé struct. Les fonctions 3 et 4 font la même chose pour les deux autres tableaux.
La fonction 5 obtient un pointeur sur l'opérateur struct mais renvoie un int et non un pointeur. Donc, il utilise simplement la variable déclarée dans la structure.

struct employee* createEmployee(char*, char*, char*, int); //1 
char* firstname (struct Employee*);      //2 
char* lastname (struct Employee*);       //3 
char* number (struct Employee*);       //4 
int salary (struct Employee*);        //5 
+1

Sans corps de fonction, il est difficile de dire ce qu'ils font, mais les signatures montrent ce que vous avez expliqué. Et quelle est la question? – khachik

+0

Ma question est si mon explication est correcte. Je ne suis pas sûr si je comprends comment fonctionnent les pointeurs dans ce cas. – Ordo

Répondre

2

Votre compréhension est à peu près correcte. Pour être un peu plus précis et/ou moins abusif de la langue anglaise, les fonctions 2-4 renvoient un pointeur à un élément du tableau correspondant. L'idée est que le contenu de chaque tableau représente une sorte de texte, chaque élément correspondant à un caractère de texte, jusqu'à la première apparition d'une valeur nulle (utilisée pour marquer la fin de la "chaîne"). Gardez à l'esprit qu'il n'y a pas de disposition pour Unicode ici, ou même pour spécifier un encodage: nous supposons ASCII, et pour tous les octets non compris dans la plage 0..127, tous les paris sont désactivés. Un meilleur nom pour le type char serait byte; mais nous ne savions pas vraiment mieux à l'époque.(Vous devez également savoir que char est un type distinct du signed char et unsigned char, et que char peuvent ou non être signé.)

De cette façon, les noms et numéro peut être une longueur jusqu'à dix (un onzième octet est réservé pour avoir de la place pour le "terminateur nul" avec la valeur zéro), et le pointeur retourné peut être utilisé pour inspecter - et modifier; mais cela pourrait ne pas être une bonne idée - les données dans les tableaux qui font partie de la structure Employee.

Le int retourné comme salary du Employee est une copie de l'une dans le struct. Donc, bien que nous puissions voir la valeur entière, cela ne pas laissez-nous modifier la valeur dans la structure. Bien sûr, tant que nous avons la définition struct et une instance Employee, il n'y a pas de protection; nous pouvons accéder au membre directement au lieu de passer par la fonction.

En C, nous obtenons "encapsulation" et "dissimulation de données" par pas fournissant ces définitions. Au lieu de cela, nous allons simplement mettre struct Employee; et les déclarations de fonctions dans l'en-tête, et la définition de struct dans le fichier d'implémentation. Maintenant, le code appelant ne sait rien sur ce que Employeeest; seulement sur ce que peut faire.

+0

Merci. Votre explication le rend beaucoup plus clair pour moi. – Ordo

1

Ce que vous ne pouvez pas être sûr en lisant simplement la signature de fonction si les valeurs/pointeurs retournés sont référence aux mêmes valeurs/pointeurs de la sturct, ou la fonction (s) crée une copie et revenir pointeur vers la nouvelle valeur. C'est pourquoi c'est une bonne idée d'écrire aussi la documentation en tant que commentaires avec la signature de la fonction dans le fichier d'en-tête.

+2

Bien que ce soit vrai, il serait étrange et non-idiomatique d'écrire du code C de cette façon. Si vous ne souhaitez pas que l'appelant modifie vos données via un pointeur renvoyé, vous devez le dire explicitement, en utilisant les fonctionnalités de langue, c'est-à-dire en utilisant le mot clé 'const' de manière appropriée. –

+0

D'accord, pour ma défense, C est ma langue secondaire: P –

1

Vos explications de sens, sauf # 2-4: depuis leur retour char*, ils ne peuvent pas retourner un élément d'un tableau de caractères (le type correct pour celui-ci serait tout simplement char). Chacune des trois fonctions renvoie un pointeur sur char. On suppose qu'ils retournent le pointeur sur le premier élément du tableau correspondant, qui est essentiellement comment les tableaux sont passés en C.

+0

Avez-vous manqué les mots "un pointeur" juste avant l'enhardi "d'un élément" dans la question? –

+0

@Karl Je n'ai pas (il n'était pas là). Le PO a édité la question en réponse à ma réponse. – NPE

+0

SO est plein de ninjas, je vous dis. ;) –

2

Presque à droite, sauf quand vous lisez les fonctions, l'entrée "char *" doit être lue En tant que la chaîne est transmise bien que techniquement c'est un pointeur. pour les entiers, la valeur réelle est transmise, vous êtes donc là.

donc,

struct employee* createEmployee(char*, char*, char*, int); 

signifierait que la procédure crée un employé avec les quatre valeurs entrées (les trois premiers sont des chaînes (char *) qui sont probablement le prénom, le nom et le numéro/id dans ce l'ordre et le dernier est le salaire.)

+0

Les pointeurs vers char sont des pointeurs vers char. Tout le reste n'est que convention. Il y a ** aucun ** type de chaîne réelle dans C. –

+0

D'accord, Il n'y a pas de type de chaîne dans C. mais c'est l'intention du programme. transmettre le prénom, le nom de famille en tant que chaînes. la façon dont vous le feriez en passant le pointeur. –

1

Il y a un problème dans 1. Qu'est-ce que vous faites, je suppose est:

struct employee* createEmployee(const char* f, const char* l, const char* n, 
const int sal) 
{ 
    struct employee* e; 
    strcpy(e->firstname, f); 
    strcpy(e->lastname, l); 
    /* ... */ 
    return e; 
} 

Le problème ici est les tableaux à venir dans Il est parfaitement possible pour un tableau de caractères de toute taille à passer. dans; quoi que ce soit sur la longueur de 11 vous obligerait à commencer à écraser les données à &(e->firstname[0])+11;. Au-dessus de quoi? Exactement. Vous n'avez aucune idée (et je ne l'ai pas non plus, cela serait déterminé à l'exécution). Cela pourrait causer de sérieux problèmes.

Une façon de contourner est d'utiliser les fonctions de stdlib.h et string.h dire strlen() pour tester la longueur des données transmises pour vous assurer qu'il correspond à la taille de votre terrain.

Une meilleure méthode pourrait consister à écrire:

int createEmployee(struct employee* e, const char* f, const char* l, const char* n, 
const int sal) 
{ 
    int error = 0; 
    if (strlen(f) < 11) 
    {  
     strncpy(e->firstname, f); 
    } 
    else 
    { 
     error++; 
    } 
    /* ... */ 

    return error; 
} 

Voyez ce que je l'ai fait? Oui, cela fonctionnera - tout ce qui est passé en tant que pointeur peut être modifié. Ce n'est pas passer par référence, tout à fait. Edit: comme le dit aix, les pointeurs sont "comment les tableaux sont transmis en C".

Une autre méthode potentielle est strncpy() qui tronquera la chaîne source en fonction du dernier argument, donc strncpy(e->firstname, f, 11); serait sûr.

Vous pouvez également essayer l'allocation de mémoire dynamique pour les tailles de champ en fonction des besoins. Je suppose que vous apprendrez cela plus tard/comme un autre défi.

En outre, une autre suggestion, alors que nous y sommes, est de définir le pointeur vers une structure en utilisant typedef. Cela rend les choses un peu plus lisibles bien que la façon dont vous l'avez fait est nettement plus claire pour quelqu'un qui apprend.

+0

o_O Il y a plusieurs problèmes ici: Il n'y a aucune indication que le PO a réellement écrit, ou même ** regardé ** n'importe quel code d'implémentation (je pense qu'il lit des exemples); 'strcpy' est autant une fonction de la bibliothèque standard que 'strlen' est; le signalement d'erreurs n'est pas un problème parce que nous retournons un pointeur, donc nous pouvons simplement retourner NULL s'il y a une erreur (et qui se soucie du nombre d'erreurs quand même?); "out paramètres" sont laids en général; et votre premier exemple est cassé puisque l'Employé n'est jamais réellement alloué nulle part (et quand vous passez dans un pointeur, l'appelant doit en allouer un). –

+0

@Karl Je ne voulais pas trop jeter d'un coup, mais si vous ajoutez 1, puis 2, puis 4 au nombre d'erreurs, vous utilisez effectivement des bits dans une chaîne binaire pour représenter quelle erreur s'est produite. –

+0

Oui, les paramètres out sont laids, d'accord. Je pense toujours que c'est mieux que de créer la structure dans la fonction parce que la prochaine étape logique est d'allouer dans la fonction pour les tableaux dynamiques ... ce qui pourrait entraîner ignorer free est plus tard. Oui, je n'ai pas créé d'instance de la structure employee dans la fonction, mais non, la fonction elle-même n'est pas cassée, comme vous le dites, l'appelant doit l'allouer. –

Questions connexes