Я пытаюсь прочитать некоторые данные из стандартного ввода. Это будут числа (любое количество цифр), разделенные пробелом. Проблема в том, что я не знаю длину заранее. Я хочу иметь возможность читать со стандартного ввода и использовать его для управления чем-либо, это повторяется до тех пор, пока не будет нажата ^d.
#include <stdio.h>
#include <stdlib.h>
int
main(){
char input[] = scanf("%s", &input);
for(int i=0; i<sizeof(&input);i++){
//Do something
}
}
Это не работает, но как я могу изменить его, чтобы он работал?
Синтаксис вашего кода неверен. Я предлагаю вам открыть хорошую книгу по C и изучить основы.
@Unholysheep, может быть, но мне трудно понять весь ответ.
@ Бармар, да, спасибо, это была опечатка.
@machine_1 спасибо, "гений", ты ОЧЕНЬ полезен ?
попробуйте сначала прочитать строку, а затем прочитать целые числа в строке
char *x ,*s ;
int d ;
while (fgets(input, sizeof(input), stdin)) {
x = input;
for (x = input; ; x = s) {
d = strtol(x, &s, 10);
if (x == s)
break;
}
}
его пример. Вам нужно добавить проверку результатов malloc и realloc (я не делал этого для простоты)
#include <stdio.h>
#include <stdlib.h>
#define CHUNK 32
char *readline(void)
{
size_t csize = CHUNK;
size_t cpos = 0;
char *str = malloc(CHUNK);
int ch;
while((ch = fgetc(stdin)) != '\n' && ch != '\r')
{
str[cpos++] = ch;
if (cpos == csize)
{
csize += CHUNK;
str = realloc(str, csize);
}
}
str[cpos] = 0;
return str;
}
Тогда у вас есть строка, которую вы можете sscanf. После использования освободить память
И преобразовать его в массив целых чисел (поскольку вы не знаете размер результирующего массива):
int *tointegers(char *str, char *delimiters, size_t *size)
{
int *array = malloc(CHUNK * sizeof(*array));
char *ptr;
*size = 0;
ptr = strtok (str, delimiters);
while (ptr != NULL)
{
array[(*size)++] = atoi(ptr);
if (*size == CHUNK)
{
array = malloc((*size + CHUNK) * sizeof(*array));
}
ptr = strtok (NULL, delimiters);
}
return array;
}
Те же замечания о проверке результатов malloc и realloc и освобождении памяти
Основная проблема здесь в том, что вы не знаете количество элементов заранее, в этих случаях вам нужно зарезервировать место для хранения элементов с использованием динамической памяти, вы можете использовать очередь или вы можете использовать realloc
, также избегайте использования scanf
в этом образом, всегда ограничивайте длину строки:
char str[100];
scanf("%99s", str); /* buffer overflow protection */
и всегда проверяйте результат:
if (scanf("%99s", str) != 1) {
/* something went wrong */
}
Пример использования fgets
(в качестве альтернативы scanf
) и strtol
с сохранением данных в очереди:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
struct node {
struct node *next;
int data;
};
void *enqueue(struct node **root, int data)
{
struct node *node;
node = malloc(sizeof *node);
if (node == NULL) {
return NULL;
}
if (*root == NULL) {
node->next = node;
} else {
node->next = (*root)->next;
(*root)->next = node;
}
node->data = data;
*root = node;
return node;
}
int dequeue(struct node **root)
{
struct node *node;
int data = 0;
node = *root;
if (node != NULL) {
node = node->next;
data = node->data;
if (*root == node) {
*root = NULL;
} else {
(*root)->next = node->next;
}
free(node);
}
return data;
}
int main(void)
{
struct node *node = NULL;
char str[512];
char *ptr;
int data;
ptr = fgets(str, sizeof str, stdin);
if (ptr != NULL) {
while (*ptr) {
data = (int)strtol(ptr, &ptr, 10);
if (!isspace(*ptr)) { // If we don't have a blank space
break; // exit the loop
}
enqueue(&node, data);
ptr++;
}
}
while (node != NULL) {
data = dequeue(&node);
printf("%d\n", data);
}
return 0;
}
Вход
123 456 -789
Выход
123
456
-789
Что делать, если строка длиннее 99 символов? Если это 1000, 100000, 10000000?
Использование списка для хранения целых чисел, которые являются излишними
@P_J, вы можете использовать fgets
в цикле, пока не найдете новую строку.
Почему перебор?, для этого и составляются списки.
Чтобы прочитать (и сохранить) неизвестное количество символов из stdin
, у вас есть два основных варианта:
getline()
для считывания всех символов в буфер (getline
перераспределит память по мере необходимости для хранения всей строки ввода, включая завершающий символ *nul), илиmalloc
и realloc
.Как вы уже поняли, динамическое выделение памяти поначалу может быть немного пугающим, но на самом деле это не так. В любом случае вы просто выделяете некоторый блок памяти исходного размера, назначаете начальный адрес указателю, сохраняете все, что вам нужно, в блоке, отслеживая используемую память. Когда затем используемая память равна доступной памяти (т. е. когда вы заполняете выделенный вами блок памяти), вы просто перераспределяете больше памяти с помощью временного указателя, подтверждаете, что ваш вызов realloc
прошел успешно, а затем назначаете начало перераспределенного блока памяти. память на исходный указатель и продолжайте повторять процесс каждый раз, когда вы заполняете свой блок памяти.
Есть много способов приблизиться к чтению, используя функцию символьного ввода, такую как getchar()
, или используя некоторый буфер фиксированного размера и fgets
для чтения фиксированного количества символов за раз. Это действительно зависит от вас. Избегайте scanf
для одного символа, в этом нет необходимости. Базовое чтение буферизуется файловой системой, поэтому производительность не снижается независимо от того, что вы выберете. (Linux предоставляет 8192-байтовый буфер чтения размером с IO_BUFSIZ
(теперь BUFSIZ
сейчас, см. glibc/libio/stdio.h — #define BUFSIZ 8192 и _IO_BUFSIZ
изменено на BUFSIZ
glibc фиксирует 9964a14579e5eef9), и Windows доказывает аналогичный 512-байтовый буфер)
Суть в том, чтобы делать это шаг за шагом, проверять каждое распределение и обрабатывать ошибки по мере необходимости. Вы используете временный указатель с realloc
, потому что, если realloc
терпит неудачу, он возвращает NULL
, а если вы присваиваете возврат realloc
исходному указателю, вы перезаписываете адрес исходного блока памяти с помощью NULL
, создавая утечку памяти. При использовании временного указателя в случае сбоя realloc
ваши существующие данные по-прежнему доступны через исходный указатель.
Например, чтобы удвоить размер текущего выделенного buffer
с текущим размером выделенного buffersize
, вы могли бы наивно сделать:
buffer = realloc (buffer, 2 * buffersize); /* wrong - potential memory leak */
if (buffer == NULL) { /* validate reallocation */
perror ("realloc-buffer"); /* output error message */
/* handle error */
}
buffersize *= 2; /* increment buffersize */
Вместо этого вы будете делать:
void *tmp = realloc (buffer, 2 * buffersize); /* use a temporary pointer */
if (tmp == NULL) { /* validate reallocation */
perror ("realloc-buffer"); /* output error message */
/* handle error, buffer still points to original block */
}
buf = tmp;
buffersize *= 2;
Понять, как это работает, можно на минимальном простом примере. Следующее будет читать строку неизвестного размера из stdin
с использованием переносимых getchar()
, malloc
и realloc
с использованием схемы перераспределения, которая просто удваивает размер буфера каждый раз, когда вы заполняете его. (вы можете увеличить на любую дополнительную сумму, которую хотите, но избегайте перераспределения для каждого прочитанного символа - это было бы неэффективно, удвоение размера буфера или какое-либо подобное увеличение минимизирует количество раз, которое вы перераспределяете)
#include <stdio.h>
#include <stdlib.h>
#define NCHR 8 /* initial number of characters to allocate */
int main (void) {
int c; /* char to read from stdin */
size_t ndx = 0, /* index/count of characters */
nchr = NCHR; /* number of characters allocated in buf */
char *buf = malloc (nchr); /* buffer allocated for nchr chars */
if (buf == NULL) { /* validate that allocation succeeds */
perror ("malloc-buf"); /* otherwise handle error */
return 1; /* bail */
}
/* read chars from stdin until '\n' or EOF */
while ((c = getchar()) != '\n' && c != EOF) {
if (ndx == nchr - 1) { /* check if reallocation is needed */
void *tmp = realloc (buf, 2 * nchr); /* double buf size */
if (tmp == NULL) { /* validate realloc succeeds */
perror ("realloc-buf"); /* handle error */
break; /* break don't bail, buf holds chars read */
}
buf = tmp; /* assign newly sized block of mem to buf */
nchr *= 2; /* update nchr to new allocation size */
}
buf[ndx++] = c; /* assign char to buf, increment index */
}
buf[ndx] = 0; /* nul-terminate buffer */
if (c == EOF) /* if read stopped on EOF */
putchar ('\n'); /* tidy up outputting \n */
printf ("length : %zu\ncontent: %s\n", ndx, buf);
free (buf); /* don't forget to free what you allocate */
}
(Примечание: проверка EOF
, которая будет сгенерирована Ctrl + d (или Ctrl + z в Windows) и вывод дополнительного '\n'
при обнаружении, иначе ваш следующий вывод начнется в конце вашего текущего ввода. Также обратите внимание nchr - 1
в if (ndx == nchr - 1)
убедитесь, что есть всегда доступен 1 символ для хранения нулевое завершение после выхода из цикла.)
Пример использования/вывода
$ ./bin/getchar_dyn
1234 5678 9012 3456 7890
length : 24
content: 1234 5678 9012 3456 7890
Использование памяти/проверка ошибок
В любом коде, который вы пишете, который динамически выделяет память, у вас есть 2 обязанности для любого выделенного блока памяти: (1) всегда сохраняйте указатель на начальный адрес для блока памяти, поэтому (2) это может быть освобожден, когда он больше не нужен.
Крайне важно, чтобы вы использовали программу проверки ошибок памяти, чтобы убедиться, что вы не пытаетесь получить доступ к памяти или писать за пределы/за границы вашего выделенного блока, пытаться читать или основывать условный переход на неинициализированное значение и, наконец, подтвердить что вы освобождаете всю память, которую вы выделили.
Для Linux valgrind
— нормальный выбор. Для каждой платформы есть похожие средства проверки памяти. Все они просты в использовании, просто запустите через них свою программу.
$ valgrind ./bin/getchar_dyn
==28053== Memcheck, a memory error detector
==28053== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==28053== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==28053== Command: ./bin/getchar_dyn
==28053==
1234 5678 9012 3456 7890
length : 24
content: 1234 5678 9012 3456 7890
==28053==
==28053== HEAP SUMMARY:
==28053== in use at exit: 0 bytes in 0 blocks
==28053== total heap usage: 3 allocs, 3 frees, 56 bytes allocated
==28053==
==28053== All heap blocks were freed -- no leaks are possible
==28053==
==28053== For counts of detected and suppressed errors, rerun with: -v
==28053== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Всегда подтверждайте, что вы освободили всю выделенную память и что ошибок памяти нет.
Просмотрите все и дайте мне знать, если у вас есть дополнительные вопросы.
Если это числа, почему вы не используете
%d
вместо%s
?