Суммирование ввода пользователя

Небольшой проект, который я недавно начал. Программа должна суммировать все введенные пользователем цифры. Вы должны сначала указать, сколько номеров будет.

Sumaster.c

#include <stdio.h>

int main(void)
{
    unsigned short int num = 0;

    while(num <= 0 || num >= 256)
    {
        printf("Enter number of values to sum: ");
        scanf("%d", &num);
    }

    unsigned long long int arr[num];
    unsigned long long int total = 0;
    unsigned short int i;

    for(i = 0; i < num; i++)
    {
        scanf("%lld", arr + i);
        total += arr[i];

        if(i < num - 1) printf("+\n");
        else printf("=\n%lld\n", total);
    }

    return(0);
}
c io
11 голосов | спросил Genis 28 Jam1000000amWed, 28 Jan 2015 04:54:50 +030015 2015, 04:54:50

5 ответов


-1

Небольшие программы как таковые должны быть относительно честными, поскольку они часто используются в качестве практических примеров, как правило, для начинающих. Таким образом, соответствующий обзор следует считать попыткой улучшения Нобелевской стороны в этом конкретном варианте.


Название программы

Название такой программы должно быть исключительно идентично его функциональному поведению.

Он должен намекнуть на понятное для пользователя понятие, до того как он потратит усилия и ресурсы на открытие «таинственного исходного файла» . Имя, которое вы выбрали ... «Сумастер» - это как-то своеобразно и, конечно, не так самоочевидно. Его можно истолковать многими больными способами. Если он пытается превознести программу, вызывая ее " Sum mation M aster " , тогда она применяется, но это немного смешно, если вы ассимилируете он.

Мое предложение просто «Sumilizer» или что-то более упрощенное «Автоматическое суммирование» (энтузиасты могут называть его «AS Bot» , чтобы принести некоторую 217-ю версию программного обеспечения.)

Кроме того, если у вас проблемы с определением имени, вы можете посетить этот мощный международный интернет-кибер-искатель и тип «Хорошие имена для ...», где ... - целевой объект, для которого вы ищете хорошее имя.

Описание программы

... который отсутствует. В описании должно быть указано несколько дополнительных сведений об использовании программы. Если вы будете использовать только один, то, очевидно, нет необходимости (за исключением того времени, когда мы забываем). Если кто-то хочет использовать программу, он не сможет угадать уникальный функциональный механизм, который вы изобрели. Скажите мне: сколько раз у вас возникли проблемы с пониманием того, как работает одна программа?

Вам не нужно выделять веб-страницу или файл readme.txt. Файл readme.txt может находиться в исходном файле. Если это предназначено для использования с открытым исходным кодом. Вряд ли это не будет. Если будущее этой программы связано с новичками, учиться в программе, тогда вам будет полезно добавить какое-то описание. Если это для вашего собственного опыта, вы можете решить. В каждом случае это не совсем неуместно.

/* Summation automatized program by Genis
 * Copyright: Free For Any Purposes
 * Compilation arguments: std=c99
 * 
 * The first thing you'll have to specify is the length of the number table.
 * Enter to continue..
 * Then you are typing the first number you wish to add to the number table.
 * Enter to continue.. then the next number.
 * When the numbers reach the length of the number table, the program will output the total of all the numbers in the number table.
 */

Интернационализация

Предполагая, что эта программа будет широко использоваться людьми во всем мире, она может быть интернационализирована (то есть переведена на ряд региональных языков). В кодировке MinGW по умолчанию используется UTF-8, тип данных char должен быть способен представлять любого члена базового набора символов выполнения и UTF -8 единиц кода. Unicode Programming позволяет использовать более широкий набор символов.

Переменная тип-определение

Что вы сделали, вызывает:

  • Проблема чтения.
  • Проблема с переносимостью.

Более бессмысленные дополнительные слова и больше времени, необходимые пользователю для чтения и переваривания сформированного типа данных. Я предлагаю вам «typedef» создания этих типов данных.

typedef unsigned short int uint_16, word;
typedef unsigned long long int uint_32, dword;

И затем используйте эти определения. Используйте word и dword, если программа предназначенный для работы под Windows.

Именование переменных

Проблема читаемости №1.

  • Первое имя первой переменной num неточно. Если кто-то (или вы в будущем забывчивости) хочет отредактировать /проанализировать код, ему потребуется больше времени, чтобы ассимилировать точную цель этой переменной. Почему не имя arrSize, поскольку оно ведет себя как индекс и является индексом. Альтернативы: table_length для snake_case или TableLength /tableLength для стилей имени CamelCase.
  • Второе имя второй переменной arr будет отлично, если первая переменная называется arrSize. То есть, если мы хотим назватьпеременные по их поведению, а не логические цели. В противном случае .. вы можете выбрать table.
  • total или tableTotal одинаково хорош.

Несогласованное преобразование

Как мы выяснили, num - это тип переменной unsigned short. В строке 10 мы исключаем signed int как input для num. Это пробудит оптимизацию компилятора, возможно, -Woverflow, и в конце концов это может быть совершенно нежелательное поведение. В названии смысла вы можете использовать спецификатор формата printf "%hu", который уравнивает ожидания.

Устранение проблемной функции

Если вы скопируете /вставьте этот код в «Skype». return(0) превратится в часы. Похоже, вы возвращаете часы. Избавьтесь от скобок! Просто шучу.

В целом

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

ответил Edenia 28 Jam1000000amWed, 28 Jan 2015 05:00:21 +030015 2015, 05:00:21
12

Это хорошо в целом, а для игрушечной или классной программы это достаточно, но прошло какое-то время, так как я сделал обзор, поэтому пришло время получить супер придирчивый:).


Существует очень серьезная проблема в том, что обработка ввода-вывода неверна. Попробуйте запустить свою программу, а вместо действительного количества элементов для суммирования введите что-то нечисловое. Это будет бесконечный цикл.

Короче говоря, scanf не перемещается вперед в потоке, когда он не потребляет ничего 1 . Вам нужно либо прочитать строку за раз (fgets), и разобрать целое число из строки, или вам понадобится чтобы использовать оставшуюся часть линии и повторить попытку (самым простым подходом к этому будет цикл на getline, пока вы не получите \n)

В аналогичном ключе ваше входное чтение в цикле для сбора входов просто неверно. Все, что требуется, - это один неверный ввод, и программа распадается.

1 В этой связи стоит отметить, что scanf имеет другие проблемы .


Также есть проблемы с переносимостью. До C99 массивы переменной длины не должны поддерживаться стандартом, а после C99 они стандартизированы, но не требуются. Это означает, что C99 является единственным стандартом, требующим VLA. Это довольно опасная ситуация, чтобы ввести себя.

Если вы не знаете, о чем я говорю, arr - массив переменной длины, так как num не является константой времени компиляции (есть волшебство, происходящее под капотом - это не обычный массив, компилятор просто скрывает это).

(Реально, каждый компилятор, с которым вы столкнетесь, будет поддерживать это. Только одна из многих ошибок в C в отношении переносимости, если вы когда-нибудь столкнетесь с действительно редкой средой.)


Я не понимаю, почему массив и сумма не определены. Кажется, что те же функции будут работать так же хорошо, если вы должны разрешить подписанные номера? Единственная причина, по которой я могу думать о том, что использовать неподписанные значения, заключается в том, что вы пытаетесь вывести немного больше диапазона, но это, вероятно, неверно, и если 64-битное целое не достаточно велико, вы, вероятно, попадете в произвольную область точности.

Короче говоря, я думаю, что весь ваш материал unsigned long long должен быть просто long long int или int64_t (из stdint.h). Даже если long long int на самом деле гарантированно будет 64 бит или более, тогда как int64_t требуется ровно 64 бита (и не требуется поддержка), я думаю, что int64_t понятнее прочитайте (и реально, если long long существует, int64_t будет существовать).


Это слегка спорно, но я на самом деле не поклонник short, если вы делаете это потому, что вы на самом деле нужен конкретный размер (с ограниченным объемом памяти, у вас их много, проблемы с выравниванием и т. д.). int с чем-то более естественно работать, и в зависимости от архитектуры процессора, short, вероятно, в любом случае рассматривается как int.

ответил Corbin 28 Jam1000000amWed, 28 Jan 2015 07:56:54 +030015 2015, 07:56:54
4

Вы всегда должны компилироваться с включенными предупреждениями. Хороший компилятор должен был предупредить вас ...

$ clang -Wall -o sumaster sumaster.c
sumaster.c:10:21: warning: format specifies type 'int *' but the argument has
      type 'unsigned short *' [-Wformat]
        scanf("%d", &num);
               ~~   ^~~~
               %hd
1 warning generated.

В результате я могу это сделать (результаты будут отличаться, поскольку это неопределенное поведение):

 Enter number of values to sum: -2147483645
1
+
2
+
3
=
6

Функцию scanf() на самом деле довольно сложно использовать правильно, особенно при обработке ошибок. Например, что происходит в вашей программе, когда в приглашении "Enter number of values to sum: " вы нажмете Ctrl D (Unix) или Ctrl Z (Windows), чтобы создать условие конца файла? Что, если вы введете A ? Оба случая приводят к бесконечному циклу . Одним из средств является полагаться на fgets() вместо ; другой - отбросить любой плохой ввод до первой новой строки перед повторной попыткой .

Так как процедура ввода числовых ошибок является довольно сложной, имеет смысл определить для нее функцию.


Беззнаковые числа на самом деле сложны в работе. Например, сравнение num <= 0 ведет себя как num == 0, так как num никогда не будет отрицательным.

Удивительно, но ваша программа будет принимать и добавлять отрицательные числа (хотя, опять же, это неопределенное поведение, на которое вы не можете рассчитывать).

Из-за этих обманчивых действий вы лучше использовать подписанные типы данных (и выполняете проверку, если хотите запретить отрицательные числа). Существует также не много смысла использовать тип данных, более узкий, чем int . Компиляторы, скорее всего, будут помещать short для выравнивания памяти, поэтому вы не сэкономите место. A short имеет смысл только в том случае, если вы имеете дело с двоичным файлом или сетевым протоколом, где имеет значение точный размер (в этом случае явно размерный тип типа int16_t будет более уместным) или если у вас есть массив, который достаточно велик, чтобы пространство занимало.


Нет никакого смысла хранить все входы в массиве. Вы можете добиться такого же эффекта, просто сохраняя общее количество.


Предлагаемое решение

 #include <stdio.h>

/**
 * Prompts the user using the given prompt until a long long int is
 * successfully read.  If the input is not an integer, prints err_msg
 * and prompts the user again.
 *
 * Returns 1 on success, or 0 if EOF is encountered.
 */    
static int prompt_ll(const char *prompt, long long *n, const char *err_msg)
{
    do
    {
        printf("%s", prompt);
        switch (scanf("%lld", n))
        {
          case EOF:
            return 0;
          case 1:
            return 1;
          default:
            scanf("%*[^\n]");   /* Discard the rest of the bad line */
            printf("%s", err_msg);
        }
    } while (1);
}

int main(void)
{
    long long n;
    do
    {
        if (!prompt_ll("Enter number of values to sum: ", &n, "")) return 1;
        if (0 <= n && n < 256) break;
        printf("Enter a number between 0 and 255.\n");
    } while (1);
    int num = (int)n;

    long long total = 0;
    for (int i = 0; i < num; i++)
    {
        if (!prompt_ll(i ? "+\n" : "", &n, "Try again.\n")) return 1;
        total += n;
    }
    printf("=\n%lld\n", total);
}
ответил 200_success 28 Jpm1000000pmWed, 28 Jan 2015 12:03:27 +030015 2015, 12:03:27
1

В этом контексте, по-видимому, нет причин использовать VLA, вы уже указали, что массив имеет максимальный размер 255, поэтому почему бы просто не объявить его как таковой.

unsigned long long int arr[255];

таким образом вы более независимы от версии /параметров компилятора.

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

например.

// read an integer 1..255
// @returns -1 if user wants to quit, 1..255
int getInt()
{
  char line[32];

  while (fgets(line, sizeof(line), stdin) != NULL) 
  {
    if ( sscanf( line, "%d", &num ) == 1 )
    {
      if (num > 0 && num < 256)
      {
        return num;
      }
      puts( "invalid input, please enter a number in range [1,255]" ); 
    }
    else
    {
      puts( "please enter a number" );
    }
  }
  return -1;
}
ответил Anders K. 28 Jpm1000000pmWed, 28 Jan 2015 22:18:07 +030015 2015, 22:18:07
0

Я сохраню это просто.

Документация действительно НЕ имеет значения. Цель кажется довольно ясной.

Суммируйте все целые числа при использовании VLA.

Заголовка в порядке. Самая сложная вещь, которая здесь происходит, - I /O.

Возвращение может быть отличным, это личное предпочтение.

Вы можете использовать return 0; или return (0); или return(0);

, и если вы действительно чувствуете себя дерзким; так ужасно, как может показаться, и запугать сообщество в массовую пандемию террора и тревоги ... вы можете включить stdlib.h, void main (void) и использовать exit (0).

Даже если short равен 2 байтам, у вас все еще есть диапазон -65,537 to 65,536 возможные значения. Предполагая, что большинство систем могут обрабатывать 4 байта int, long long на самом деле не требуется. unsigned, в этом контексте может вызвать серьезные проблемы даже в самых основных понятиях, таких как этот. Вы должны разрешить подписанные значения. Я объясню, почему. Но пока мы изменим этот

unsigned short int num = 0;

к этому

int num = 0;

Цикл while() хорош. Если num is <= 0 или num is >= 256 запросить пользователя снова , Ваша фильтрация ввода, но есть catch с кодом scanf() и его, который он считывает до первого символа пробела. Вам нужно очистить ввод.

while (getchar() != '\n'); или while (getchar() != '\n') continue;

обычно решает эту проблему. Таким образом, любой недопустимый ввод очищается, и новый вход готов. Также обратите внимание, что while num is less than zero. Если ваша переменная num имеет тип unsigned , вы создали семантическую ошибку. Тот, который заставил меня некоторое время привыкнуть. Это невероятно легко сделать. Вот почему это должно быть signed, а не unsigned. unsigned, и этот контекст не таков.

while(num < 0 || num > 255)
{
    printf("Enter number of values to sum: ");
    scanf("%d", &num);
    while (getchar() != '\n') continue; //clear the input buffer
}

, что и для vla arr, общей переменной и переменной i ...

unsigned long long int arr[num];
unsigned long long int total = 0;
unsigned short int i;

действительно должно быть

int arr[num], total, i;

вам даже не нужно объявлять i, но если вы хотите совместимость, вы можете объявить i вне цикла for (). Опять же, это зависит от контекста и предпочтений.

, нам не нужен тип long long. просто введите int.

    scanf("%lld", arr + i);

должен быть просто

scanf("%d", &arr[i]);

, так как мы работаем с массивами, а не с указателями (хотя массив является типом указателя), цель понятна, а последовательность выполняется во всем коде; и помните, сохраняли значение в этом элементе массива.

нам не нужно смещать num на 1, так как i должно быть меньше num. Элементы уже смещены на один, и инициализация i до 0 достигает этой цели. но на самом деле, нам даже не нужен этот код вообще. все, что нам нужно сделать, это распечатать общее количество.

for(i = 0; i < num; i++)
{
    scanf("%d", &arr);
    while (getchar() != '\n') continue; //clear the buffer
    total += arr[i];
    printf(" + ");
}
printf("total sum is %d\n", total);

, но вы должны получить эту идею.

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

Предполагая, что vlas используется, мы можем предположить, что используется компилятор, совместимый с c99.

#include <stdio.h>

int main(void)
{
    int num = 0;

    printf("Enter number of values to sum: ");
    while(1 != scanf("%d", &num) && (num < 0 || num > 256))
    {
        printf("Enter number of values to sum: ");
        while (getchar() != '\n'); //clear buffer
        scanf("%d", &num);
    }
    while (getchar() != '\n');

    int arr[num], total = 0;

    printf("%d number(s) to be summed: ", num);
    for(int i = 0, j = num - 1; i < num; i++, j--)
    {
        while (1 != scanf("%d", &arr[i]))
        {
            printf("%d number(s) to be summed: ", j);
            while (getchar() != '\n') continue; //clear buffer
            scanf("%d", &num);
        }
        while (getchar() != '\n');
        total += arr[i];
        if (j != 0) printf("%d number(s) to be summed: ", j);
    }
    printf("The total sum is %d\n", total);

    return 0;
}
ответил jargonjunkie 30 Jam1000000amFri, 30 Jan 2015 10:49:57 +030015 2015, 10:49:57

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132