Простой средний расчет

Я пытаюсь написать программу, вычисляющую среднее значение заданных чисел, хранящихся в массиве. Количество чисел должно быть не более 100, и пользователь должен вводить их до тех пор, пока не будет задана переменная !int :

#include <iostream>
#include <conio.h>
using namespace std;

double average(int tab[], int i){

    int sum=0;

    for(int j=0; j<i; ++j){
            sum+=tab[j];
    }
    return (double)sum/i;

}

int main()
{
    int tab[100];
    int n=0;   
    int number=0;


    do {
       if(n < 100){
           cout << "Give " << n+1 << " number : ";
           cin >> number;
           tab[n]=number;
           number=0;
           ++n;       
       }
       else{
            break;
       }
    } while( !isdigit(number) );      

    cout << average(tab, n) << endl;

    getch();
    return 0;
}

Почему после предоставления char он печатает мне «Дайте n число:» для всех пустых ячеек моего массива? Он должен заканчиваться и использовать только заданные числа.


person sasquatch90    schedule 19.05.2010    source источник
comment
нет, это не домашнее задание. Я просто переделываю свои java-программы в cpp, чтобы научиться этому.   -  person sasquatch90    schedule 20.05.2010


Ответы (8)


isdigit проверяет, является ли символ цифрой. Тест достигается только после присвоения 0 номеру, а 0 — это управляющий код, а не цифра, поэтому isdigit(0) всегда ложно, и поэтому ваше условие while всегда истинно.

 ...
       number=0;
 ...
} while( !isdigit(number) );      

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

int main()
{
    const size_t COUNT = 100;
    int tab[COUNT];
    size_t n;   

    cin.tie(&cout); // ensures cout flushed before cin read
    // (not required if your runtime complies with that part of the standard)

    for (n = 0; n < COUNT; ++n ) {
        cout << "Give " << n+1 << " number : ";
        cin >> tab[n];

        if (!cin)
            break;
    }

    if (n > 0) // average is undefined if n == 0
        cout << average(tab, n) << endl;

    return 0;
}
person Pete Kirkham    schedule 19.05.2010
comment
cin все равно привязан к cout: §27.3.1/2 - person Potatoswatter; 20.05.2010
comment
Я уверен, что у меня были случаи, когда это было не так. - person Pete Kirkham; 20.05.2010

Вы используете isdigit здесь неправильно - он используется для проверки того, является ли char числовым или нет - вы не можете использовать его для проверки int.

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

person Paul R    schedule 19.05.2010

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

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

person wallyk    schedule 19.05.2010
comment
Кстати, функция isdigit не проверяет допустимые числовые символы, такие как '.', '-' и '+'. Для научной записи обычная буква «Е» также является допустимым символом для числа. Ознакомьтесь с любым правилом грамматики для C, C++, Java и т. д. - person Thomas Matthews; 20.05.2010
comment
@Thomas: он читает только целые числа, поэтому '.' и «Е» здесь не применимо. - person Paul R; 20.05.2010

Лучшим методом обнаружения ввода не числа является проверка состояния cin после считывания значения:

// ...
if (cin >> number)
{
  tab[n++] = number;
}
else
{
  break;  // break out of loop
}

Также помните, что могут быть и другие причины сбоя ввода, кроме неправильного ввода числа.

person Thomas Matthews    schedule 19.05.2010
comment
да, я знаю об этом, но давайте не будем усложнять идею :) - person sasquatch90; 20.05.2010
comment
@sasquatch90: Чем ответ @Thomas сложнее вашего? Его код, безусловно, проще. - person Void; 20.05.2010
comment
@sasquatch90: Существует два алгоритма обнаружения не числа: 1. Используйте функцию преобразования и проверьте на наличие ошибки или 2. Проверьте каждый символ на наличие допустимой цифры. На самом деле этот последний метод должен включать необязательные символы, такие как десятичная точка, + и '-'. Последний метод требует, чтобы данные вводились в виде строки, а затем проверялись. Кстати, вам все равно нужно проверить cin на успех. - person Thomas Matthews; 20.05.2010
comment
Я имел в виду эту часть: «Также помните, что могут быть и другие причины, по которым ввод не удался, кроме ввода действительного числа». - person sasquatch90; 20.05.2010

Есть несколько проблем с вашим кодом:

cin >> number;

Вы не проверяете, не удалось ли выполнить операцию извлечения потока. Простой способ сделать это — использовать оператор преобразования operator void*():

if (cin >> number)
  ... operation succeeded ...

Приведенный выше код эквивалентен проверке failbit и badbit .

Ваше использование isdigit() также неверно, поскольку вы передаете число (например, 1234 вместо символа (например, 'z'). Несмотря на это, добавление проверки ошибки операции извлечения потока устраняет необходимость в такой проверке на основе цифр.

person Void    schedule 19.05.2010
comment
Предложение: добавьте код, который входит в оператор else (т.е. что-то вроде cin.reset(), точно не помню). - person Raphaël Saint-Pierre; 20.05.2010
comment
@RaphaelISP, подходящий метод - cin.clear() для очистки битов состояния. - person Thomas Matthews; 20.05.2010

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

#include <iostream>
#include <string>
#include <boost/lexical_cast.hpp>

using std::cout;
using std::cin;
using std::endl;
using std::string;
using boost::lexical_cast;
using boost::bad_lexical_cast;

double average(int tab[], int i){

    int sum=0;

    for(int j=0; j<i; ++j){
            sum+=tab[j];
    }
    return (double)sum/i;

}

int main()
{

    int tab[100]; //this is a fairly low level construct which might want to
                  // into a std::vector 

    string input; 

    int n;
    try{
        for (n = 0 ;n < 100; n++) {
           cout << "Give " << n+1 << " number : ";
           cin >> input;                          //read number into string
           tab[n]= lexical_cast<int>(input);     //conversion with lexical_cast
                                                 //if not possible exception is 
                                                 //thrown
        }
     }
     catch(bad_lexical_cast &){
        cout << "not a number" << endl;
     } 

    cout << average(tab, n) << endl;

    return 0;
}
person Lucas    schedule 19.05.2010

isdigit сообщит вам, представляет ли код символа набора символов одну из цифр от 0 до 9.

Следовательно (я предполагаю, что вы используете ASCII), вы можете просто использовать символ и проверить его диапазон кода ASCII:

    int tab[100]; 
    int n = 0;    
    char c;

    while (n++ < 100)
    {
       cout << "Give " << n << " number : "; 
       cin >> c;
       if (c < 48 || c > 57)
          break;
       tab[n - 1] = c - 48;            
    }

    cout << average(tab, n - 1) << endl; 

    getch(); 
    return 0; 

Вы также можете использовать cin.getline и atoi или strtod:

int tab[100]; 
int n=0;    
int number=0; 
char input[10];

while (n++ < 100)
{     
   cout << "Give " << n << " number : ";
   memset(input, 0x00, 10);
   cin.getline(input, 10);
   number = atoi(input);
   if (number > 0)
      tab[n-1] = number; 
   else
      break;
}

cout << average(tab, n-1) << endl; 

getch(); 
return 0; 

Существуют и другие методы, которые вы можете использовать, однако они должны дать вам некоторые идеи.

person Community    schedule 19.05.2010
comment
› isdigit на самом деле сообщает вам, является ли код ASCII символа ASCII одной из цифр от 0 до 9. --- что, даже на машине EBCDIC? - person Pete Kirkham; 20.05.2010
comment
@Pete - Хороший джеб. Я упрощаю понимание для ОП. Очевидно, что на машине EBCDIC он должен работать с символами EBCDIC, однако в прошлом в компиляторах, таких как gcc, были ошибки, из-за которых он не работал на машинах EBCDIC... mail-archive.com/[email protected]/msg92614.html. Похоже, что OP использует ASCII, поэтому мои примеры... - person ; 20.05.2010

person    schedule
comment
Почему не cout для взаимодействия вместо cerr? - person Bill; 20.05.2010
comment
@Bill: cerr переходит к пользователю; cout - это программный вывод. Эту программу гораздо проще использовать в пакетном режиме. - person Potatoswatter; 20.05.2010
comment
Хм. Я всегда использую cerr для вывода ошибок и cout для вывода без ошибок. Я также не понимаю, как это будет проще использовать в пакетном режиме. - person Bill; 20.05.2010
comment
@Bill: эта программа примет файл с рядом чисел. Использовать как avg < numbers_file &> /dev/null > avg_file. Использование cout для подсказок приведет к тому, что avg_file будет заполнено ими. Любое приложение CLI в любом случае должно сообщать об ошибках вместе с выводом пользователю. - person Potatoswatter; 20.05.2010