7
votes

Malloc et scanf

Je suis assez compétent dans quelques langues de script, mais je me forgie enfin à apprendre cru C. Je joue juste avec des trucs de base (I / O en ce moment). Comment allouer la mémoire de la mémoire de tas puis-je enregistrer une chaîne dans la mémoire allouée, puis la cracher de côté? C'est ce que j'ai en ce moment, comment puis-je le faire fonctionner correctement?

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

int main(int argc, char *argv[])
{
  char *toParseStr = (char*)malloc(10);
  scanf("Enter a string",&toParseStr);
  printf("%s",toParseStr);
  return 0;
}


2 commentaires

Vous n'avez pas besoin de lancer le type de retour de MALLOC (3) dans ISO C avec inclus.


Il convient de souligner que vous devriez probablement utiliser la pile ici.


5 Réponses :


3
votes

Vous n'avez pas besoin d'un et avant toparsestr dans scanf car il s'agit déjà d'un pointeur

Appelez également gratuit (toparsestr) après


4 commentaires

Selon le système de BBALL, il peut être nécessaire de mettre un "\ n" dans ce printf pour des choses à apparaître correctement. De plus, 10 caractères sont une chaîne vraiment courte.


Bien que vrai, ce n'est pas vraiment la source d'un problème (le & est inutile mais inoffensif dans ce cas).


@Jerry, il est inoffensif car le spécificateur de format ne spécifie pas des arguments, mais une fois qu'il l'a correctement réparé, il faut avoir un% comme dans votre réponse, il va causer un segfault


@Michael: Oui, mais il ne pointe qu'un problème relativement mineur où il y a beaucoup d'autres qui sont beaucoup plus graves. En particulier, changer ce point particulier (tout en laissant le reste du code inchangé) ne fournira aucune amélioration (visible) de son comportement.



9
votes

Vous devez donner scanf un format de conversion afin qu'il sait que vous souhaitez lire une chaîne - à l'heure actuelle, vous venez d'afficher les ordures qui se trouvaient dans la mémoire que vous avez allouée. Plutôt que d'essayer de décrire tous les problèmes, voici quelques codes qui devraient au moins être proches de travailler: xxx

modifier: dans ce cas, gratuit 't faire toute différence réelle, mais comme d'autres l'ont souligné, il est une bonne habitude de cultiver néanmoins.


2 commentaires

Contrairement à la conviction de certaines personnes, rinçage stdout est pas nécessaire pour que l'invite apparaisse avant que l'entrée soit lue, à moins que la mise en œuvre ne soit bien et véritablement cassée. Pour ceux qui s'en soucient vraiment, voir § 7.19.3. stdout ne peut être complètement tamponné que si elle peut être déterminée pas pour faire référence à un périphérique interactif.


vous vous trompez. stdout peut toujours être tamponné de ligne ce qui signifie que rien ne s'affiche jusqu'à ce qu'une nouvelle ligne soit imprimée. POSIX recommande que les implémentations affleurant stdout et d'autres flux tamponnés de la ligne de ce type à la lecture, mais c'est une performance significative à numériser la liste des fichiers ouverts pour les flux tamponnés de ligne (en particulier avec des filets et des filets) et une implémentation Peut choisir de ne pas le faire pour de très bonnes raisons. Pour autant que je sache, l'ISO C rend peu / aucune exigence sur la sémantique tampon. Donc, vous Devriez Flush!



9
votes
int main(int argc, char *argv[])
{
  char *toParseStr;
  const int n = 10;
  printf("Garbage: %p\n",toParseStr);
  AllocateString(&toParseStr,n);
  printf("Address of the first element of a contiguous array of %d bytes: %p\n",n,toParseStr);

  printf("Enter string here: ");
  scanf("%s",toParseStr);
  printf("%s\n",toParseStr);
  free(toParseStr);

  return 0;
}

2 commentaires

+1 pour libérer même dans un petit programme. Me rappelle que "petites gouttes font un océan". ;-)


Vous devez appeler fflush (stdout); entre imprimer l'invite et appeler scanf . La plupart des implémentations feront cela pour que vous puissiez être poli, mais cela n'est pas mandaté.



0
votes

Premièrement, les erreurs qui gardaient votre programme de travail: scanf (3) code> prend une chaîne de format, comme printf (3) code>, pas une chaîne à Imprimer pour l'utilisateur. Deuxièmement, vous passez l'adresse du pointeur toparsestr code>, plutôt que le pointeur toparsestr code>.

J'ai également supprimé la distribution inutile de votre appel à Malloc ( 3) code>. P>

Une amélioration nécessaire que votre programme a encore besoin d'utiliser scanf (3) code> 'S A code> option pour allouer la mémoire pour Vous - de sorte que certains Joker mettent dix caractères dans votre chaîne ne commencent pas à piétiner la mémoire non liée. (Oui, C laissera quelqu'un qui écrase presque tout l'espace d'adressage avec ce programme, comme écrit. Frame de sécurité géante. :) p>

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

int main(int argc, char *argv[])
{
  char *toParseStr = malloc(10);
  printf("Enter a short string: ");
  scanf("%s",toParseStr);
  printf("%s\n",toParseStr);
  return 0;
}


2 commentaires

scanf n'a pas d'option . Ceci est une extension GNU qui n'est pas seulement non standard mais conflits avec ISO C (% A est l'un des spécificateurs de lire un numéro de point flottant!). Il devrait être évité absolument.


Merci; Je n'étais pas au courant de cette vulgarisation avec Iso C.



5
votes

Utilisation de scanf () code> (ou FSCANF () code> sur les données que vous ne contrôlez pas) avec un spécificateur "% s" standard est un moyen quasi certain d'obtenir vous-même dans des ennuis avec des débordements de tampon.

L'exemple classique est que j'entre la chaîne "Cette chaîne est de la manière de plus de 10 caractères" dans votre programme, le chaos s'ensuivra, les chats et les chiens vont commencer à dormir ensemble et une singularité nue peut Bien apparaître et consommez la Terre (la plupart des gens n'adaptent que «comportement indéfini», mais je pense que ma description est meilleure). p>

Je décourage activement l'utilisation de fonctions qui ne peuvent pas fournir une protection. Je vous exhorterais (surtout comme un nouveau venu à c) d'utiliser fgets () code> pour lire votre entrée puisque vous pouvez contrôler beaucoup plus facilement les débordements de mémoire tampon et il est plus adapté à une entrée de ligne simple que scanf () code>. p>

Une fois que vous avez une ligne, vous pouvez ensuite appeler sscanf () code> sur le contenu de votre coeur qui, au fait, vous. N'ayez pas besoin de faire dans ce cas particulier puisque vous obtenez seulement une chaîne brute de toute façon. P>

J'utiliserais: P>

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

#define BUFFSZ 10

int main(int argc, char *argv[]) {
  char *toParseStr = malloc(BUFFSZ+2);
  if (toParseStr == NULL) {
      printf ("Could not allocate memory!\n");
      return 1;
  }
  printf ("Enter a string: ");
  if (fgets (toParseStr, BUFFSZ+2, stdin) == NULL) {
      printf ("\nGot end of file!\n");
      return 1;
  }
  printf("Your string was: %s",toParseStr);
  if (toParseStr[strlen (toParseStr) - 1] != '\n') {
      printf ("\nIn addition, your string was too long!\n");
  }
  free (toParseStr);
  return 0;
}


5 commentaires

+1, bien que j'ajouterais que lorsque fgets a des avantages, scanf et fscanf do disposent de empêcher également les débordements de la mémoire tampon.


C'est un bon point, @jerry, bien que j'ai rarement vu que les gens utilisent le spécificateur de largeur avec "% s" :-) puisque la majeure partie de mon code d'E / S console a tendance à avoir une entrée basée sur la ligne,% s est inappropriée pour obtenir l'espace blanc. Cependant, comme votre réponse est réellement juste dans ce cas, +1 pour vous.


Une autre possibilité intéressante est scanf ("% 9 [^ \ n]", votre_string); - Entrée de chaîne orientée ligne de scanf , pour tout ce qui vaut.


@Jerry Coffin: scanf et FSCANF est généralement difficile à utiliser pour d'autres raisons. OMI c'est mieux pour quiconque n'est pas un expert C pour les éviter complètement. Quoi qu'il en soit, +1 pour être la seule réponse qui met en garde sur le débordement potentiel de la mémoire tampon.


@Jerry: +1 pour le Nice % [ suggestion. Donc, peu de gens savent qu'il existe. Il est en fait utile pour la mise en œuvre d'une version entièrement portable de GNU getline / getdelim sur la nature ISO C. et si vous utilisez % n après cela, vous Peut même obtenir le nombre d'octets de lecture, au cas où la lecture des données contient des octets nuls incorporés.