0
votes

Codage semblable à un python en C pour les pointeurs

Je passe de python à c, alors ma question peut paraître naïf. Je lis Tutoriel sur des liaisons Python-C et il est mentionné que:

in c, tous les paramètres sont une valeur de passage. Si vous souhaitez autoriser une fonction de modifier une variable dans l'appelant, vous devez passer un pointeur sur cette variable. P> BlockQuote>

question strong>: Pourquoi ne pouvons-nous pas simplement ré-attribuer les valeurs à l'intérieur de la fonction et être libre des pointeurs? p>

Le code suivant utilise des pointeurs: P > XXX PRE>

MAINTENANT Ceci peut être remplacé par le code suivant qui n'utilise pas les pointeurs: P>

int i = 24;

int increment(int j){
    j++;
    return j;
}
void main() {
    i=increment(i);
    printf("i = %d", i);
}


8 commentaires

Je ne sais pas si je comprends la question correctement. Préférez-vous que les deux extraits de code sont équivalents ou demandez-vous pourquoi ils ne sont pas?


Vous montrez un style de codage légitime qui fronçait des effets secondaires dans les programmes. Il peut aider à créer (sans doute) un code plus parallélizable avec moins d'erreurs. Certaines langues de programmation sont construites de cette façon. Si j'ai une structure dans FOO et passez & foo ou * FOO , je dois vous inquiéter de la durée de vie de FOO Et quelle fonction la fonction va faire à elle. Si je passe foo , c'est copié, je n'ai pas ces préoccupations. Les composants STL ont tendance à copier beaucoup, que cela se produise sous les couvertures fréquemment.


Ou attendre. Oui, cette construction est équivalente. Mais c'est un exemple étrange. Je veux dire, oui tu peux faire ça. Mais il y a des tonnes d'autres utilisations pour les pointeurs.


@Tdelaney +1 Bien merci pour avoir bravé des projecteurs à ce sujet. Lire vos commentaires dans la réponse ci-dessous et la réponse elle-même me permet de me demander qu'il existe un compromis entre la performance et la commodité. Les pointeurs peuvent donc être bons pour la performance et la ré-affectation de la commodité. À droite?


@klutt Eh bien, je pensais que cela pourrait être l'exemple reproductible le plus simple pour illustrer un point et demander une question.


@gfdsal - Cette question pourrait conduire à des combats de poing dans le département du CS. Supposons qu'une structure partagée avec des verrous se soit terminée dans des blocages ou une inversion prioritaire dans un système parallèle. Copies que le code Decouple peut fonctionner plus rapidement, même si vous brûlez quelques cycles y arriver. Lorsqu'un pointeur est transmis à une fonction, qui possède le tampon lorsque la fonction retourne? Les systèmes de dépassement de la mémoire tampon tels que les files d'attente de message ont des règles compliquées à ce sujet. Lorsqu'un message atteint son point de fin, a-t-il besoin de revenir au créateur d'origine pour la libération? La copie peut rendre cela moins lourd.


L'une des choses sur Python est qu'il dispose d'un système de gestion de la mémoire standard unique. Si je passe un pointeur en C, le destinataire ne sait pas si c'est quelque chose qui peut être libéré ou si sa partie d'un tampon plus grand. Donc, même si les données doivent être consommées par la fonction, la libération est toujours la responsabilité de l'appelant. Si je passe un pointeur sur envoyer en C Par exemple, la bibliothèque va copier ces données car elle ne prend jamais la propriété du tampon lui-même.


Parfois, Python vous donne les deux choix. Regardez list.sort.sort () vs. code> trié (liste) .


3 Réponses :


6
votes

Vous ne pouvez retourner qu'une seule chose d'une fonction. Si vous devez mettre à jour plusieurs paramètres ou que vous devez utiliser la valeur de retour pour autre chose que la variable mise à jour (telle qu'un code d'erreur), vous devez transmettre un pointeur sur la variable.


7 commentaires

Vous pouvez cependant retourner une structure, cependant. Un programmeur fonctionnel froncerait les sourcils sur la modification des données dans des pointeurs et préférerait cette façon de faire des choses.


@Tdelaney j'ai débattu de la question de mentionner cela dans ma réponse. Les structures de passage autour par la valeur ne sont pas très courantes dans la programmation C, Afaik.


Je l'ai fait dans des systèmes où nous voulions transmettre les codes de retour et les valeurs de retour. Go a immortalisé la pratique. En outre, les gens peuvent le faire plus que ce qu'ils pensent lors du retour des objets C ++ STL. Je suis d'accord, ce n'est pas la façon normale de faire des choses, mais errno () est horrible!


@Barmar, oui, comme tdelaney a dit, dans Python, je peux retourner un tuple ou un tableau contenant plusieurs valeurs de sortie, je suppose que cela a du sens avec votre réponse "couplée à la réponse ci-dessous que nous allons copier de la grande graisse de plusieurs fois", n'est-ce pas?


@gfdsal Il est plus courant dans des langues comme Python et JS, car les objets sont passés par référence, il n'y a pas de copie.


Très bien. a du sens maintenant. Et Bravo pour 5 Uvottes dans 10 minutes!


L'une des grandes choses sur Python est la facilité avec laquelle vous pouvez retourner plusieurs choses à partir d'une fonction. En C, il est beaucoup plus difficile, vous devez définir une structure pour tenir toutes les choses.



1
votes

Ainsi, imaginez que vous devez passer de grandes tableaux ou d'autres structures de données nécessitant une modification. Si vous appliquez la façon dont vous utilisez pour incrémenter un entier, vous créez une copie de ce grand tableau pour chaque appel à cette fonction. Évidemment, il n'est pas favorable à la mémoire de créer une copie, mais nous transmettons des pointeurs à des fonctions et effectuons les mises à jour sur un seul tableau ou quoi que ce soit.

plus, comme l'autre réponse mentionnée, si vous devez mettre à jour de nombreux paramètres, il est impossible de revenir de la manière dont vous avez déclaré.


1 commentaires

D'accord. Je suppose que cela est logique d'éviter de devoir copier le grand tableau et réaffecter.



2
votes

Obtenir ceci à l'écart des premiers - les pointeurs sont la programmation fondamentale à C. Vous ne pouvez pas être "GRATUIT" des pointeurs lors de l'écriture de C. Vous pourriez aussi bien essayer de ne jamais utiliser si instructions, tableaux ou n'importe lequel des opérateurs arithmétiques. Vous ne pouvez pas utiliser une partie substantielle de la bibliothèque standard sans utiliser de pointeurs.

"Pass par valeur" signifie, entre autres choses, que le paramètre formel j dans incrément et le paramètre actuel i dans Main sont des objets distincts en mémoire et la modification de l'un n'a absolument aucun effet sur l'autre. La valeur de i est copiée sur j lorsque la fonction est appelée, mais toutes les modifications apportées à i ne sont pas reflétées dans j et vice-versa.

Nous travaillons autour de cela dans C à l'aide des pointeurs. Au lieu de passer la valeur de i à incrément , nous passons son adresse adresse (par valeur), puis la désarférence qui adresse avec le * opérateur.

C'est l'un des cas où nous ont utiliser des pointeurs. L'autre cas est lorsque nous suivons la mémoire allouée de manière dynamique. Les pointeurs sont également utiles (si non requis strictement) pour la construction de conteneurs (listes, arbres, files d'attente, piles, etc.).

Passer une valeur en tant que paramètre et renvoyer sa valeur mise à jour fonctionne, mais uniquement pour un seul paramètre. Passer plusieurs paramètres et renvoyer leurs valeurs mises à jour dans un type struct peut fonctionner, mais ce n'est pas un bon style si vous le faites juste pour éviter d'utiliser des pointeurs. Il n'est également pas possible si la fonction doit mettre à jour les paramètres et renvoyer une sorte de statut (tel que la fonction de bibliothèque scanf , par exemple).

De même, l'utilisation de variables d'étendue de fichiers ne fait pas échoue et crée des maux de tête de maintenance. Il y a des moments où ce n'est pas la mauvaise réponse, mais en général, ce n'est pas une bonne idée.


3 commentaires

Donc, c'est plus d'un style / pratique de programmation adopté, comme des bibliothèques en cas de réponse, la bibliothèque standard est construite autour de l'intention d'utiliser des pointeurs.


@gfdsal: Ce n'est pas seulement une question de style - nous sommes requis utiliser des pointeurs dans certains cas. Les pointeurs sont une classe distincte de types de données. La mémoire allouée de manière dynamique est suivie par des pointeurs. L'indemnisation de tableau est définie en termes de sémantique du pointeur. L'abstraction est accomplie par des pointeurs. Fonctions d'ordre supérieur (c'est-à-dire des fonctions qui prennent d'autres fonctions comme des arguments) exigent que vous utilisiez des pointeurs. Au lieu de trouver des moyens créatifs d'éviter d'utiliser des pointeurs, votre temps est mieux dépensé d'apprendre comment ils fonctionnent et comment les utiliser correctement.


Noté. Merci pour le conseil. J'apprends en fait comment les utiliser correctement.