2
votes

Est-il possible de définir un pointeur sans variable temp / aux? (Ou serait-ce un mauvais codage C?)

J'essaie de comprendre les pointeurs C. En toile de fond, je suis habitué à coder à la fois en C # et en Python3 .

Je comprends que les pointeurs peuvent être utilisés pour enregistrer les adresses d'une variable (écrire quelque chose comme type * ptr = & var; ) et que l'incrémentation des pointeurs équivaut à incrémenter l'index d'un tableau d'objets de ce type d'objet type . Mais ce que je ne comprends pas, c'est si vous pouvez ou non utiliser des pointeurs et des objets déférencés de type (par exemple int ) sans référencer un déjà - variable définie.

Je ne pouvais pas penser à un moyen de faire cela, et la plupart des exemples de pointeurs C / C ++ semblent tous les utiliser pour référencer une variable. Il se peut donc que ce que je demande soit une pratique de codage impossible et / ou mauvaise. Si tel est le cas, il serait utile de comprendre pourquoi.

Par exemple, pour clarifier ma confusion, si il n'y a aucun moyen d'utiliser des pointeurs sans utiliser des variables codées en dur prédéfinies, pourquoi utiliseriez-vous des pointeurs au lieu de l'objet de base directement, ou des tableaux d'objets?

Il y a un petit morceau de code ci-dessous pour décrire formellement ma question.

Merci beaucoup pour tout conseil!

// Learning about pointers and C-coding techniques.

#include <stdio.h>

/* Is there a way to define the int-pointer age WITHOUT the int variable auxAge? */

int main()  // no command-line params being passed
{
    int auxAge = 12345;
    int* age = &auxAge;
    // *age is an int, and age is an int* (i.e. age is a pointer-to-an-int, just an address to somewhere in memory where data defining some int is expected)
    // do stuff with my *age int e.g. "(*age)++;" or "*age = 37;"
    return 0;
}


7 commentaires

int * age; * age = 12345; , ou quelque chose comme ça, je suis rouillé en C


... a un comportement indéfini, @Cid.


@JohnBollinger comme je l'ai dit plus haut, je suis rouillé. Pourquoi indéfini?


int * age = malloc (sizeof (int)); * age = 12345; et n'oubliez pas de free (age); après


aah oui, malloc


Parce que age ne contient pas de valeur de pointeur valide lorsque vous essayez de le déréférencer dans l'affectation, @Cid. Où pensez-vous que la valeur que vous stockez va réellement aller ? En pratique, cela peut sembler fonctionner ou non, mais une erreur de segmentation ou une autre erreur liée à la mémoire est raisonnablement probable.


@JohnBollinger ouais, d'où le malloc () cela me rappelle que le temps où je luttais avec des fautes de segment


5 Réponses :


1
votes

Les paramètres d'une fonction en C sont toujours passés par valeur, donc la modification d'une valeur de paramètre dans une fonction n'est pas reflétée dans l'appelant. Vous pouvez cependant utiliser des pointeurs pour émuler le passage par référence. Par exemple:

int *getarray(int size)
{
    int *array = malloc(size * sizeof *array);
    if (!array) {
        perror("malloc failed"); 
        exit(1);
    }
    return array;
}

Vous pouvez également utiliser des pointeurs pour pointer vers la mémoire allouée dynamiquement:

void clear(int *x)
{
    *x = 0;
}

int main()
{
    int a = 4;
    printf("a=%d\n", a);   //  prints 4
    clear(&a);
    printf("a=%d\n", a);   //  prints 0
    return 0;
}

Ce ne sont que quelques exemples. p>


0 commentaires

7
votes

Oui, vous pouvez utiliser l'allocation de mémoire dynamique (également appelée "tas"):

#include <stdlib.h>

int * const integer = malloc(sizeof *integer);
if (integer != NULL)
{
  *integer = 4711;
  printf("forty seven eleven is %d\n", *integer);
  free(integer);
  // At this point we can no longer use the pointer, the memory is not ours any more.
}

Cela demande à la bibliothèque C d'allouer de la mémoire du système d'exploitation et de renvoyer un pointeur vers elle . Allouer sizeof * integer octets fait que l'allocation correspond exactement à un entier, et nous pouvons ensuite utiliser * integer pour déréférencer le pointeur, cela fonctionnera à peu près exactement comme référencer un entier directement .


2 commentaires

De plus, en tant que méta-commentaire complètement égoïste, il se trouve que c'est ma 6 000: e réponse publiée ici. :) Ouais moi.


Ahh c'est intéressant à savoir! Je suppose que c'est presque la fin du cours C que je suis en train de suivre (les fonctions lib standard sont bien plus tardives, ce qui me semble étrange). Alors mon code d'origine, où je déclare et attribue un int normal, puis attribue un point séparé à son adresse, ne fonctionnerait-il toujours que sur la pile? Toutes les variables sont-elles enregistrées dans la pile par défaut, sauf si vous appelez spécifiquement malloc (byteCount) ?



1
votes

Il existe de nombreuses bonnes raisons d'utiliser des pointeurs en C, et l'une d'elles est que vous ne pouvez passer que par valeur en C - vous ne pouvez pas passer par référence. Par conséquent, passer le pointeur vers une variable existante vous évite la surcharge de la copie dans la pile. A titre d'exemple, supposons cette très grande structure:

bool has_zero(struct very_large_structure *structure) {
    for (int i = 0; i < sizeof(*structure); i++) {
        if (0 == structure->kilobyte[i]) {
            return true;
        }
    }

    return false;
}

Et maintenant supposons une fonction qui doit utiliser cette structure:

bool has_zero(struct very_large_structure structure) {
    for (int i = 0; i < sizeof(structure); i++) {
        if (0 == structure.kilobyte[i]) {
            return true;
        }
    }

    return false;
}

Donc pour que cette fonction soit appelée, vous devez copier toute la structure à empiler, et cela peut être particulièrement sur les plates-formes embarquées où C est largement utilisé, une exigence inacceptable.

Si vous passez la structure via un pointeur, vous copiez uniquement dans la pile le pointeur lui-même, généralement un nombre de 32 bits:

struct very_large_structure {
    uint8_t kilobyte[1024];
}

Ce n'est en aucun cas la seule et la plus importante utilisation des pointeurs, mais c'est clairement montre pourquoi les pointeurs sont importants en C.


0 commentaires

0
votes

Raison la plus courante: parce que vous souhaitez modifier le contenu sans le faire passer.

Analogie:
Si vous voulez que votre salon soit peint, vous ne voulez pas placer votre maison sur une remorque de camion, la déplacer chez le peintre, le laisser faire le travail et la ramener. Cela coûterait cher et prendrait beaucoup de temps. Et si votre maison est trop large pour être transportée dans les rues, le camion pourrait s'écraser. Vous préférez dire au peintre à quelle adresse vous habitez, le faire y aller et faire le travail.

En termes C, si vous avez une grosse struct ou similaire, vous voudrez qu'une fonction accède à cette structure sans en faire une copie, en passant une copie à la fonction , puis recopiez le contenu modifié dans la variable d'origine.

void do_stuff (really_big* thing)
{
  thing->something = ...;
}

Cela fait une copie de rb appelée chose . Il est placé sur la pile, gaspillant beaucoup de mémoire et augmentant inutilement l'espace de pile utilisé, augmentant la possibilité de débordement de pile. Et copier le contenu de rb vers chose prend beaucoup de temps d'exécution. Ensuite, quand il est retourné, vous faites encore une autre copie, de chose à rb.

En passant un pointeur sur la structure, aucun des la copie a lieu, mais le résultat final est le même:

// BAD CODE, DONT DO THIS
typedef struct { ... } really_big;
really_big rb;
rb = do_stuff(rb);

...    

rb do_stuff (really_big thing) // pass by value, return by value
{
  thing->something = ...;
  ...
  return thing;  
}


0 commentaires

1
votes

Mais ce que je ne comprends pas, c'est si vous pouvez ou non utiliser des pointeurs et des objets déférencés du type (par exemple int) sans faire référence à une variable déjà définie.

Oui, il y a deux cas où cela est possible.

Le premier cas se produit avec l'allocation dynamique de mémoire. Vous utilisez les fonctions malloc , calloc ou realloc pour allouer de la mémoire à partir d'un pool de mémoire dynamique (le "tas"):

char *port = (char *) OxDEADBEEF;

Le deuxième cas se produit lorsque vous avez une adresse fixe et bien définie pour un canal ou un port d'E / S ou quelque chose:

int *ptr = malloc( sizeof *ptr ); // allocate enough memory for a single `int` object
*ptr = some_value; 

bien que cela soit plus courant dans les systèmes embarqués que dans la programmation d'applications générales.

EDIT[

Concernant le deuxième cas, chapitre et verset :

6.3.2.3 Pointeurs

...
5 Un entier peut être converti en n'importe quel type de pointeur. Sauf indication contraire, le le résultat est défini par l'implémentation, peut ne pas être correctement aligné, peut ne pas pointer vers un entité du type référencé, et peut être une représentation d'interruption. 67)

67) Les fonctions de mappage pour convertir un pointeur en entier ou un entier en pointeur sont destinées à être cohérent avec la structure d'adressage de l'environnement d'exécution.

2 commentaires

Le deuxième exemple est-il réellement autorisé par le standard C, ou les compilateurs pour les systèmes embarqués ont-ils tendance à ajouter une extension au langage pour le prendre en charge?


@ChristianGibbons: C'est autorisé, si c'est significatif dépend de l'implémentation. Cela dépend de la façon dont la valeur entière est convertie en valeur de pointeur, si cette valeur de pointeur résultante est valide, etc.