2009-06-03 11 views
5

Avec les chaînes de style c, comment affectez-vous un caractère à une adresse de mémoire vers laquelle pointe un pointeur de caractère? Par exemple, dans l'exemple ci-dessous, je veux changer num à "123456", donc j'ai essayé de mettre p sur le chiffre où "0" est situé et j'essaye de l'écraser avec "4". Merci.Pourquoi ce code pour modifier une chaîne ne fonctionne-t-il pas?

#include <stdio.h> 
#include <stdlib.h> 

int main() 
{ 
    char* num = (char*)malloc(100); 
    char* p = num; 

    num = "123056"; 

    p = p+3; //set pointer to where '4' should be 
    p = '4'; 

    printf("%s\n", num); 

    return 0; 
} 
+0

double possible de [Pourquoi est-ce que je reçois une erreur de segmentation lors de l'écriture à une chaîne initialisée avec "char \ * s" mais pas "char s \ [\]"? ] (http://stackoverflow.com/questions/164194/why-do-i-get-a-segmentation-fault-when-writing-to-a-string-initialized-with-cha) – jww

Répondre

12

Ce code ne fonctionne pas, simplement parce que la ligne:

num = "123056"; 

change num à point éloigné de la mémoire allouée (et p reste pointant vers la mémoire afin que ils ne sont plus au même endroit) à ce qui est probablement la mémoire en lecture seule. Vous n'êtes pas autorisé à changer la mémoire appartenant aux littéraux de chaîne, c'est un comportement indéfini.

Vous avez besoin:

#include <stdio.h> 
#include <stdlib.h> 
int main (void) { 
    char *num = malloc (100); // do not cast malloc return value. 
    char *p = num; 

    strcpy (num, "123056"); // populate existing block with string. 

    p = p + 3;    // set pointer to where '0' is. 
    *p = '4';     // and change it to '4'. 

    printf ("%s\n", num); // output it. 

    return 0; 
} 
+0

En outre, besoin de libérer la mémoire. –

+2

C'est une bonne forme, @Igor, mais pas nécessaire étant donné que le programme sort tout de suite. Et l'idée d'un extrait est de transmettre des informations pertinentes, pas toutes les informations, sinon, nous devons vérifier que malloc() a également fonctionné. – paxdiablo

0

Il suffit d'utiliser *p = '4'; ...!

+0

Déréférencement n'a pas travail. La chaîne 'num' est-elle immuable ou quelque chose? Je compile avec cc sur Linux. –

+0

C'est faux; il doit être '4', pas 4. –

+0

Oui, le littéral de chaîne est probablement alloué dans un segent de mémoire marqué en lecture seule et toute tentative d'écriture dans celui-ci provoque une erreur d'exécution. – sharptooth

13

Tout d'abord, quand vous faites:

num = "123056"; 

Vous ne copiez pas la chaîne « 123056 » dans la zone de tas alloué par malloc(). En C, l'affectation d'un pointeur char * une valeur de chaîne littérale équivaut à la définir comme une constante - à savoir identique à:

char str[] = "123056"; 

Alors, ce que vous venez accompli là, vous avez abandonné est votre seule référence à la Zone de segment de mémoire de 100 octets allouée par malloc(), ce qui explique pourquoi le code suivant n'imprime pas la valeur correcte; 'p' pointe toujours vers la zone de tas allouée par malloc() (depuis num pointé vers lui au moment de l'affectation), mais num ne le fait plus.

Je suppose que vous avez réellement l'intention de faire était de copier la chaîne "123056" dans cette zone de tas. Voici comment faire:

strcpy(num, "123056"); 

Bien, cela est une meilleure pratique pour diverses raisons:

strncpy(num, "123056", 100 - 1); /* leave room for \0 (null) terminator */ 

Si vous venez faire:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

int  main(void) { 
     char *num = malloc(100); 
     char *p = num; 

     strncpy(num, "123056", 100 - 1); 

     p = p + 3; 
     *p = '4'; 

     printf("%s\n", num); 

     return 0; 
} 

Vous auriez obtenu la résultat correct:

123456 

Vous pouvez contracter cette opération:

p = p + 3; 
*p = '4'; 

... et éviter itérer le pointeur, par deferencing comme suit:

*(p + 3) = '4'; 

Quelques autres notes:

  • Bien que stylistique commune En pratique, la valeur de retour de malloc() à (char *) est inutile. La conversion et l'alignement du type void * sont garantis par le langage C. TOUJOURS vérifier la valeur de retour malloc()

  • Il sera NULL si l'allocation de tas a échoué (c'est-à-dire que vous n'avez plus de mémoire), et à ce stade, votre programme devrait quitter.

  • En fonction de la mise en œuvre, la zone de mémoire allouée par malloc() peut contenir des déchets obsolètes dans certaines situations.Il est toujours une bonne idée de zéro dehors après attribution:

    memset(num, 0, 100); 
    
  • Ne jamais oublier de free() votre tas! Dans ce cas, le programme va quitter et le système d'exploitation va nettoyer vos déchets, mais si vous ne prenez pas l'habitude, vous aurez des fuites de mémoire en un rien de temps.

Alors, voici la version "meilleures pratiques":

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

int  main(void) { 
     char *num, *p; 

     /* 
     * Don't take 1-byte chars for granted - good habit to get into. 
     */ 

     num = malloc(sizeof(char) * 100); 

     if(num == NULL) 
       exit(1); 

     memset(num, 0, sizeof(char) * 100); 

     p = num; 

     strncpy(num, "123056", 100 - 1); 

     *(p + 3) = '4'; 

     printf("%s\n", num); 

     free(num); 

     return 0; 
} 
+1

Merci pour les bons conseils de gestion de la mémoire. Ces conseils sont bons pour les personnes qui viennent des langages de collecte automatique des ordures! –

+0

Bien sûr! Heureux d'aider! –

+0

Excellent post! Beaucoup d'informations précieuses. Je vous remercie. –

0

Eh bien, vous savez p est de type pointeur. Il stocke l'adresse de char '0'. Si vous affectez p la valeur '4'. Cela prendra '4' comme adresse. Cependant, l'adresse '4' est illégale. Vous pouvez utiliser l'opérateur '*' pour obtenir la valeur de l'adresse stockée dans p stocké.

+0

Un nitpick, l'adresse '4' n'est pas illégale. Les implémentations peuvent très facilement utiliser la mémoire à 0x00000034 (elle est même alignée correctement pour un mot de 32 bits). – paxdiablo

+0

Eh bien, j'ai dit illégal pour cette application. C'est une adresse de mémoire inconnue. – Sefler

1

En plus du problème * p que d'autres ont signalé, vous avez également des problèmes d'utilisation de la mémoire. Vous avez un tampon de 100 octets, avec un contenu inconnu. Vous avez un autre tampon de 7 octets, contenant la chaîne "123056" et un terminateur null. Vous faites ceci:

  1. num est réglé pour pointer vers la mémoire tampon 100 octets
  2. p est réglé pour pointer vers num; c'est-à-dire, il pointe sur le tampon de 100 octets
  3. vous réinitialisez num pour pointer vers le tampon de 7 octets; p pointe toujours vers la mémoire tampon 100 octets
  4. Vous utilisez p pour modifier le tampon 100 octets
  5. vous utilisez num pour imprimer le tampon 7 octets

Vous n'êtes pas imprimer le même tampon que vous modifiez.

+0

Merci Bruce, votre explication a été utile, mais le strcpy de Pax a vraiment été clair. –