• 2
  • 3
  • 4
  • 6

Программирование

Язык Си в примерах/Максимум

Материал из Викиучебника — открытые книги для открытого мира
 

Язык Си в примерах


  1. Компиляция программ
  2. Простейшая программа «Hello World»
  3. Учимся складывать
  4. Максимум
  5. Таблица умножения
  6. ASCII коды символов
  7. Верхний регистр
  8. Скобочки
  9. Факториал
  10. Степень числа
  11. Треугольник Паскаля
  12. Корень уравнения
  13. Система счисления
  14. Сортировка
  15. Библиотека complex
  16. Сортировка на основе qsort
  17. RPN калькулятор
  18. RPN калькулятор на Bison
  19. Простая грамматика
  20. Задача «Расчёт сопротивления схемы»
  21. Простая реализация конечного автомата
  22. Использование аргументов командной строки
  23. Чтение и печать без использования stdio
  24. Декодирование звукозаписи в формате ADX
Дано
непустая последовательность разделенных пробельными символами целых чисел (в «текстовом» десятичном представлении) на стандартном вводе программы. Последовательность конечна, но ее длина заранее неизвестна.
Найти
предельные значения последовательности (минимум, максимум) и основные статистики (среднее, среднеквадратичное отклонение.)
Указания
воспользуйтесь циклом «пока» (while) для чтения последовательности и функциями powsqrt для вычисления среднеквадратичного отклонения.

Решение[править]

#include <assert.h>
#include <math.h>
#include <stdio.h>

int
main ()
{
  int n = 1, cur, min, max;
  double sum, sum_sq;

  int r
    = scanf ("%d", &cur);
  assert (r == 1);
  sum = min = max = cur;
  sum_sq = pow (cur, 2);

  while (1 == (r = scanf ("%d", &cur))) {
    if (cur < min) {
      min = cur;
    } else if (cur > max) {
      max = cur;
    }
    ++n;
    sum     += cur;
    sum_sq  += pow (cur, 2);
  }
  assert (r == EOF);
  assert (! ferror (stdin));

  printf (("Range:  [%d, %d]\n"
           "Count:  %d\n"
           "Mean:   %lg\n"
           "StdDev: %lg\n"),
          min, max, n, sum / n,
          sqrt (sum_sq / n - pow (sum / n, 2)));

  return 0;
}

Вычисление и вывод результатов[править]

Разбор программы вновь начнем с ее завершения — вызова функции printf[1] для вывода результатов ее работы. В данном случае, мы выводим значения переменных minmaxn (обновляемых после успешного чтения каждого очередного элемента числовой последовательности.) Среднее значение считаем как отношение суммы всех элементов (sum) к их количеству (n).

Среднеквадратичное отклонение вычислено как корень из дисперсии, которая, в свою очередь, вычислена по правилу «среднее квадратов минус квадрат среднего» —sum_sq / n - pow (sum / n, 2) — тривиально следующему из ее определения. Значение используемой нами здесь функции pow — первый аргумент, возведенный в степень, заданную вторым аргументом; значение sqrt — квадратный корень единственного аргумента. Эти функции является частью стандартной библиотеки и объявлены в заголовке math.h.[2][3]

Чтение и накопление[править]

Чтение последовательности выполнено в два этапа. Вначале, считанный первым элемент используется для инициализации предельных значений (minmax) и сумм(sumsum_sq).

Затем, каждый очередной элемент (все так же считываемый функцией scanf[4]) подвергается следующей обработке:

  1. сравнивается с текущими значениями переменных minmax; если элемент лежит вне этих пределов — один из них изменяется соответственно;
  2. значение суммы (sum) увеличивается на величину текущего элемента; значение суммы квадратов (sum_sq) — на величину квадрата;
  3. кроме того, успешное чтение элемента приводит к инкременту (увеличению на 1) хранящей количество считанных элементов переменной n.

Каждый вызов scanf ("%d", &cur) считывает целое число в десятичной записи (%d) в переменную curПредшествующие числу пробельные символы (пробел, табуляция, перевод строки) при этом игнорируются, а значит могут быть использованы — в любых сочетаниях — для разделения элементов последовательности.

Условия корректности ввода[править]

После завершения чтения последовательности, мы требуем (используя уже известную нам макроподстановку assert[5]) истинности следующих двух условий, смысл которых сводится к тому, что чтение последовательности не было прервано ни появлением во входном потоке каких-либо «нечисловых» данных, ни ошибкой ввода-вывода.

  1. Мы проверяем равенство последнего возвращенного функцией scanf значения константе (макроопределению) EOF (конец файла; англ. end of file), что соответствует или исчерпанию нашего входного потока (стандартного ввода), или возникновению ошибки ввода-вывода. Другое возможное на данном этапе значение — ноль — укажет на наличие во входном потоке данных, которые не были опознаны функцией scanf как требуемое условием целое число в десятичной записи (%d).
  2. Если предыдущее требование выполняется (иными словами — останов произошел не из-за «нечислового» ввода), мы также явно требуем ложности значения функции признака ошибки ferror для стандартного ввода (stdin.)[6]

Цикл «пока»[править]

При чтении элементов последовательности начиная со второго мы используем оператор цикла «пока» whileсинтаксис которого напоминает синтаксисрассмотренного ранее условного оператора контекста утверждения if:[7]

while (выражение)
  тело

Подобно оператору if, если результат вычисления выражения — ложь (другими словами — 0), тело не выполняется и реализация языка Си просто переходит к следующему утверждению (англ. statement) кода.

Если же результат — истина (отличен от 0), выполняется тело, после чего реализация вновь возвращается к началу цикла.

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

пока удается считать очередное значение?
обновляй переменные minmaxnsumsum_sq.

Задания[править]

  1. Измените программу для вычисления предельных значений и статистик последовательности чисел с плавающей запятой.
  2. В условии задачи мы потребовали наличия в последовательности хотя бы одного элемента. Определите, какие сложности возникнут при попытке обобщить данную программу на случай пустой последовательности?
  3. Дополните программу выводом «промежуточных» значений — суммы элементов последовательности и суммы их квадратов.
  4. Реализуйте также вычисление и вывод моментов случайной величины вплоть до четвертого и, на их основе, — коэффициента асимметрии и коэффициента эксцесса.

См. также[править]

Примечания[править]

  1.  7.21.6.1 The fprintf function(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  2.  7.12.7.4 The pow functions(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  3.  7.12.7.5 The sqrt functions(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  4.  7.21.6.2 The fscanf function(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  5.  7.2.1.1 The assert macro(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  6.  7.21.10.3 The ferror function(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.
  7.  6.8.5.1 The while statement(англ.) WG14 N1570 Committee Draft. ISO/IEC (2011-04-12). Проверено 2012-11-19 г.

Assert. Что это?

Assert. Что это? 

Assert — это специальная конструкция, позволяющая проверять предположения о значениях произвольных данных в произвольном месте программы. Эта конструкция может автоматически сигнализировать при обнаружении некорректных данных, что обычно приводит к аварийному завершению программы с указанием места обнаружения некорректных данных. Странная, на первый взгляд, конструкция — может завалить программу в самый неподходящий момент. Какой же в ней смысл? Давайте подумаем, что произойдет, если во время исполнения программы в какой-то момент времени некоторые данные программы стали некорректными и мы не «завалили» сразу же программу, а продолжили ее работу, как ни в чем не бывало. Программа может еще долго работать после этого без каких-либо видимых ошибок. А может в любой момент времени в будущем «завалиться» сама по известной только ей причине.  Это называется неопределенное поведение (undefined behavior) и, вопреки расхожему мнению, оно свойственно не только языкам программирования с произвольным доступом к памяти (aka C, C++). Т.к. assert завершает программу сразу же после обнаружения некорректных данных, он позволяет быстро локализировать и исправить баги в программе, которые привели к некорректным данным. Это его основное назначение. Assert'ы доступны во многих языках программирования, включая java, c#, c и python.
Какие виды assert'ов бывают?
Assert'ы позволяют отлавливать ошибки в программах на этапе компиляции либо во время исполнения. Проверки на этапе компиляции не так важны — в большинстве случаев их можно заменить аналогичными проверками во время исполнения программы. Иными словами, assert'ы на этапе компиляции являются ничем иным, как синтаксическим сахаром. Поэтому в дальнейшем под assert'ами будем подразумевать лишь проверки во время исполнения программы.

Assert'ы можно разделить на следующие классы:

  • Проверка входящих аргументов в начале функции.

Если найдено недопустимое значение какого-либо аргумента, значит, где-то рядом с местом вызова этой функции могут быть баги. Пример:
// Считает факториал числа n.
// Число n должно лежать в пределах от 0 до 10 включительно.
int factorial(int n)
{
  // Факториал отрицательного числа не считается
  assert(n >= 0);
  // Если n превысит 10, то это может привести либо к целочисленному
  // переполнению результата, либо к переполнению стэка.
  assert(n <= 10);
  if (n < 2) {
    return 1;
  }
  return factorial(n - 1) * n;
}
// мы 'забыли' об ограничениях функции factorial() и пытаемся вычислить
// факториалы чисел от 0 до 99.
// проверка внутри factorial() любезно напомнит нам о своих ограничениях,
// так что мы сможем быстро выявить и исправить этот баг.
// если бы эта проверка отсутствовала, то баг мог бы долго оставаться
// незамеченным, периодически давая о себе знать переполнениями стэка и
// некорректным поведением программы.
for (int i = 0; i < 100; ++i) {
  a[i] = factorial(i);
}
Что такое целочисленное переполнение.
Что такое переполнение стэка.

Важно понимать, что входящие аргументы функции могут быть неявными. Например, при вызове метода класса в функцию неявно передается указатель на объект данного класса (aka this и self). Также функция может обращаться к данным, объявленным в глобальной области видимости, либо к данным из области видимости лексического замыкания. Эти аргументы тоже желательно проверять с помощью assert'ов при входе в функцию.
Если некорректные данные обнаружены на этом этапе, то код данной функции может содержать баги. Пример:

int factorial(int n)
{
  int result = 1;

  for (int i = 2; i <= n; ++i) {
    result *= i;
  }

  // С первого взгляда эта проверка никогда не сработает - факториал должен
  // быть всегда положительным числом. Но как только n превысит допустимый
  // предел, произойдет целочисленное переполнение. В этом случае
  // a[i] может принять отрицательное либо нулевое значение.
  //
  // После срабатывания этой проверки мы быстро локализуем баг и поймем,
  // что либо нужно ограничивать значение n, либо использовать целочисленную
  // арифметику с бесконечной точностью.
  assert(result > 0);

  return result;
}

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

  • Проверка данных, с которыми работает функция, внутри кода функции.
Если в середине функции обнаруживаются некорректные данные, то баги могут быть где-то в районе этой проверки. Пример:
int factorial(int n)
{
  int result = 1;

  while (n > 1) {
    // Знакомая нам проверка на целочисленное переполнение.
    //
    // При ее срабатывании мы быстро определим, что эта функция должна уметь
    // корректно обрабатывать слишком большие n, ведущие к переполнению.
    //
    // Эта проверка лучше, чем проверка из предыдущего пункта (перед выходом
    // из функции), т.к. она срабатывает перед первым переполнением result,
    // тогда как проверка из предыдущего пункта может пропустить случай, когда
    // в результате переполнения (или серии переполнений) итоговое значение
    // result остается положительным.
    assert(result <= INT_MAX / n);

    result *= n;
    --n;
  }

  return result;
}

Когда и где стоит использовать assert'ы?
Ответ прост — используйте assert'ы всегда и везде, где они хоть чуточку могут показаться полезными. Ведь они существенно упрощают локализацию багов в коде. Даже проверка результатов выполнения очевидного кода может оказаться полезной при последующем рефакторинге, после которого код может стать не настолько очевидным и в него может запросто закрасться баг. Не бойтесь, что большое количество assert'ов ухудшит ясность кода и замедлит выполнение вашей программы. Assert'ы визуально выделяются из общего кода и несут важную информацию о предположениях, на основе которых работает данный код. Правильно расставленные assert'ы способны заменить большинство комментариев в коде. Большинство языков программирования поддерживают отключение assert'ов либо на этапе компиляции, либо во время выполнения программы, так что они оказывают минимальное влияние на производительность программы. Обычно assert'ы оставляют включенными во время разработки и тестирования программ, но отключают в релиз-версиях программ. Если программа написана в лучших традициях ООП, либо с помощью enterprise методологии, то assert'ы вообще можно не отключать — производительность вряд ли изменится.

Когда можно обойтись без assert'ов?
Понятно, что дублирование assert'ов через каждую строчку кода не сильно улучшит эффективность отлова багов. Не существует единого мнения насчет оптимального количества assert'ов, также как и насчет оптимального количество комментариев в программе. Когда я только узнал про существование assert'ов, мои программы стали содержать 100500 assert'ов, многие из которых многократно дублировали друг друга. С течением времени количество assert'ов в моем коде стало уменьшаться. Следующие правила позволили многократно уменьшить количество assert'ов в моих программах без существенного ухудшения в эффективности отлова багов:
Можно избегать дублирующих проверок входящих аргументов путем размещения их лишь в функциях, непосредственно работающих с данным аргументом. Т.е. если функция foo() не работает с аргументом, а лишь передает его в функцию bar(), то можно опустить проверку этого аргумента в функции foo(), т.к. она продублирована проверкой аргумента в функции bar().
Можно опускать assert'ы на недопустимые значения, которые гарантированно приводят к краху программы в непосредственной близости от данных assert'ов, т.е. если по краху программы можно быстро определить местонахождение бага. К таким assert'ам можно отнести проверки указателя на NULL перед его разыменованием и проверки на нулевое значение делителя перед делением. Еще раз повторюсь — такие проверки можно опускать лишь тогда, когда среда исполнения гарантирует крах программы в данных случаях.
Вполне возможно, что существуют и другие способы, позволяющие уменьшить количество assert'ов без ухудшения эффективности отлова багов. Если вы в курсе этих способов, делитесь ими в комментариях к данному посту.

Когда нельзя использовать assert'ы?
Т.к. assert'ы могут быть удалены на этапе компиляции либо во время исполнения программы, они не должны менять поведение программы. Если в результате удаления assert'а поведение программы может измениться, то это явный признак неправильного использования assert'а. Таким образом, внутри assert'а нельзя вызывать функции, изменяющие состояние программы либо внешнего окружения программы. Например, следующий код неправильно использует assert'ы:

// Захватывает данный мютекс.
//
// Возвращает 0, если невозможно захватить данный мютекс из-за следующих причин:
// - мютекс уже был захвачен.
// - mtx указывает на некорректный объект мютекса.
// Возвращает 1, если мютекс успешно захвачен.
int acquire_mutex(mutex *mtx);

// Освобождает данный мютекс.
//
// Возвращает 0, если невозможно освободить данный мютекс из-за следующих
// причин:
// - мютекс не был захвачен.
// - mtx указывает на некорректный объект мютекса.
// Возвращает 1, если мютекс успешно захвачен.
int release_mutes(mutex *mtx);

// Убеждаемся, что мютекс захвачен.
assert(acquire_mutex(mtx));

// Работаем с данными, "защищенными" мютексом.
process_data(data_protected_by_mtx);

// Убеждаемся, что мютекс освобожден.
assert(release_mutes(mtx));

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

int is_success;

is_success = acquire_mutex(mtx);
assert(is_success);

// Теперь данные защищены мютексом даже при отключенных assert'ах.
process_data(data_protected_by_mtx);

is_success = release_mutex(mtx);
assert(is_success);

Т.к. основное назначение assert'ов — отлов багов (aka ошибки программирования), то они не могут заменить обработку ожидаемых ошибок, которые не являются ошибками программирования. Например:

// Пытается записать buf_size байт данных, на которые указывает buf,
// в указанное сетевое соединение connection.
//
// Возвращает 0 в случае ошибки записи, возникшей не по нашей вине. Например,
// произошел разрыв сетевого соединения во время записи.
// Возвращает 1 в случае успешной записи данных.
int write(connection *connection, const void *buf, size_t buf_size);

int is_success = write(connection, buf, buf_size);

// "Убеждаемся", что данные корректно записаны.
assert(is_success);

Если write() возвращает 0, то это вовсе не означает, что в нашей программе есть баг. Если assert'ы в программе будут отключены, то ошибка записи может остаться незамеченной, что впоследствие может привести к печальным результатам. Поэтому assert() тут не подходит. Тут лучше подходит обычная обработка ошибки. Например:

while (!write(connection, buf, buf_size)) {
  // Пытаемся создать новое соединение и записать данные туда еще раз.
  close_connection(connection);
  connection = create_connection();
}

Я программирую на javascript. В нем нет assert'ов. Что мне делать?
В некоторых языках программирования отсутствует явная поддержка assert'ов. При желании они легко могут быть там реализованы, следуя следующему «паттерну проектирования»:

function assert(condition)
{
  if (!condition) {
    throw "Assertion failed! See stack trace for details";
  }
}

assert(2 + 2 === 4);
assert(2 + 2 === 5);

Вообще, assert'ы обычно реализованы в различных фреймворках и библиотеках, предназначенных для автоматизированного тестирования. Иногда они там называются expect'ами. Между автоматизированным тестированием и применением assert'ов есть много общего — обе техники предназначены для быстрого выявления и исправления багов в программах. Но, несмотря на общие черты, автоматизированное тестирование и assert'ы являются не взаимоисключающими, а, скорее всего, взаимодополняющими друг друга. Грамотно расставленные assert'ы упрощают автоматизированное тестирование кода, т.к. тестирующая программа может опустить проверки, дублирующие assert'ы в коде программы. Такие проверки обычно составляют существенную долю всех проверок в тестирующей программе.

Программирование на C

Этот раздел посвящен языку программирования C, на русском произносится как — Си. Этот язык программирования является прародителем языка С++. Например, если вы хорошо будете знать язык Си, вы без труда сvожете изучить С++, обратное тоже верно. Подробнее про язык Си вы можете почитать на википедии.

Основы языка программирования Cи

Основы программирования на Си Первая программа на языке программирования С.
Оператор выбора if в языке C Управление программным потоком, конструкция if else, условные выражения.
Циклы for, while и do while в языке C Зачем нужны циклы, примеры использования циклов в программах. Введение в операторы break, continue.
Оператор выбора switch в C(Си) Что такое оператор множественного выбора switch? В каких случаях следует использовать оператор switch? Все это есть в статье.
Функции в языке программирования C Введение в функции в языке C, для чего они нужны и как их запрограммировать.
Указатели в Cи Вводная статья в на тему — «Указатели». Пример использования указателей, как их объявлять и для чего они нужны.
Динамическое выделение памяти в C Выделение и высвобождение памяти с помощью функций malloc и free.
Структуры в языке С Структуры и объединения в языке программирования Си.
Массивы в C (часть 1) Введение в массивы в языке Си.
Массивы в C (часть 2): многомерные массивы Учимся работать с двумерными массивами в языке С.
Си-строки Введение в строки в языке программирования Си.

http://cppstudio.com/cat/271/

 

MVSocialButtons

Share this post

Отправить в FacebookОтправить в Google BookmarksОтправить в OdnoklassnikiОтправить в Vkcom

Авторизация

Новые пользователи

  • Richardkewly
  • bendf16
  • HerbertJeort
  • wayneti11
  • AaronSmeds

Статистика сайта

ОС
Linux r
PHP
5.6.30
MySQLi
5.7.21-20-beget-5.7.21-20-1-log
Время
20:28
Кэширование
Отключено
GZip
Отключено
Посетители
29719
Материалы
306
Количество просмотров материалов
398143

Jivosite


cassidy clay free pornmalay young girls sucking cockbeeg gallery hdchina young sexyoung inzestpornfree download sunny leon porn hd vedeos moviesxxx.biz Bangladeshi scandalfree daughter gangbangöld granny fikautumn riley porn video free download Bangladeshi scandalfree daughter gangbangöld granny fikautumn riley porn video free download mobile porn sexyoung inzestpornfree download sunny leon porn hd vedeos