Malloc и scanf - PullRequest
       43

Malloc и scanf

4 голосов
/ 21 июля 2010

Я достаточно компетентен в некоторых языках сценариев, но наконец-то заставляю себя изучать сырой C. Я просто играю с некоторыми базовыми вещами (ввод / вывод прямо сейчас).Как я могу выделить память кучи, сохранить строку в выделенной памяти, а затем выплюнуть ее обратно?Это то, что у меня есть сейчас, как я могу заставить его работать правильно?

#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;
}

В настоящее время я получаю странный вывод, такой как '8' \ '.

Ответы [ 5 ]

9 голосов
/ 21 июля 2010

Вам нужно указать scanf формат преобразования, чтобы он знал, что вы хотите прочитать строку - сейчас вы просто отображаете мусор, который оказался в выделенной памяти.Вместо того, чтобы пытаться описать все проблемы, вот некоторый код, который, по крайней мере, должен быть близок к работе:

char *toParseStr = malloc(10);
printf("Enter a string: ");
scanf("%9s", toParseStr);
printf("\n%s\n", toParsestr);
/* Edit, added: */ 
free(toParseStr);
return 0;

Редактировать: В этом случае free строка не имеет реального значенияно, как уже отмечали другие, это является хорошей привычкой к совершенствованию.

8 голосов
/ 21 июля 2010
  char *toParseStr = (char*)malloc(10);
  printf("Enter string here: ");
  scanf("%s",toParseStr);
  printf("%s",toParseStr);
  free(toParseStr);

Во-первых, строка в scanf указывает ввод, который она собирается получить.Чтобы отобразить строку перед принятием ввода с клавиатуры, используйте printf, как показано.

Во-вторых, вам не нужно разыменовывать toParseStr, так как он указывает на массив символов размером 10, как вы выделили с помощьюmalloc. Если вы использовали функцию, которая указала бы ее на другое место в памяти, , то &toParseStr требуется.

Например, предположим, что вы хотите написать функциювыделить память.Тогда вам понадобится &toParseStr, поскольку вы изменяете содержимое переменной-указателя (это адрес в памяти - вы можете убедиться сами, распечатав его содержимое).

void AllocateString(char ** ptr_string, const int n)
{
    *ptr_string = (char*)malloc(sizeof(char) * n);
}

КакВы можете видеть, что он принимает char ** ptr_string, который читает как указатель, который хранит место в памяти указателя, который будет хранить адрес памяти (после операции malloc) первого байта выделенного блокаn байт (сейчас у него есть некоторый адрес мусорной памяти, поскольку он не инициализирован).

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;
}

В-третьих, рекомендуется освободить выделенную память.Даже если это вся ваша программа, и эта память будет освобождена при выходе из программы, это все же хорошая практика.

4 голосов
/ 21 июля 2010

Использование scanf() (или fscanf() для данных, которые вы не контролируете) со стандартным спецификатором "% s" - это почти определенный способ избежать проблем с переполнением буфера.

Классический пример - это то, что я ввожу в вашу программу строку «Эта строка содержит более 10 символов», наступает хаос, кошки и собаки начинают спать вместе, и обнаженная особенность вполне может появиться и поглотить Землю (большинство людей просто утверждают,"неопределенное поведение", но я думаю, что мое описание лучше).

Я активно препятствую использованию функций, которые не могут обеспечить защиту.Я призываю вас (особенно новичка в C) использовать fgets() для чтения ввода, так как вы можете намного легче контролировать переполнение буфера, и он больше подходит для простого ввода строки, чем scanf().

После того, как у вас есть строка, вы можете позвонить по ней sscanf(), чтобы удовлетворить ваши пожелания, что, кстати, вам не нужно делать в этом конкретном случае, поскольку вы все равно получаете только необработанную строку.

Я бы использовал:

#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;
}
3 голосов
/ 21 июля 2010

Вам не нужно & до toParseStr в scanf, так как это уже указатель

также звоните free(toParseStr) потом

0 голосов
/ 21 июля 2010

Во-первых, ошибки, из-за которых ваша программа не работала: scanf(3) принимает строку формата, как и printf(3), а не строку для печати для пользователя.Во-вторых, вы передавали адрес указателя toParseStr, а не указателя toParseStr.

. Я также удалил ненужное приведение из вашего вызова к malloc(3).

УлучшениеВаша программа все еще нуждается в том, чтобы использовать опцию scanf(3) a, чтобы выделить память для вас, чтобы какой-нибудь джокер, помещающий десять символов в вашу строку, не начинал топать на несвязанную память.(Да, C позволит кому-то переписать почти всю адресную область с помощью этой программы, как написано. Гигантский недостаток безопасности.:)

#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;
}
...