RUЭВМ
Вы хотите отреагировать на этот пост ? Создайте аккаунт всего в несколько кликов или войдите на форум.
Апрель 2024
ПнВтСрЧтПтСбВс
1234567
891011121314
15161718192021
22232425262728
2930     

Календарь Календарь

Последние темы
» Вити больше нет!
автор bug19 Пн Фев 20 2023, 19:54

» Собираем оригинальный Орион 128
автор bug19 Пн Фев 20 2023, 19:47

» Проблема плющеного экрана ОРИОНА
автор kanzler Пн Ноя 28 2022, 12:05

» Орион 128 и его клоны возрождение 2019-2022 год
автор kanzler Пн Ноя 28 2022, 12:03

» Электроника КР-04. Информация, документы, фото.
автор kanzler Пн Ноя 28 2022, 12:02

» Новости форума
автор kanzler Пн Ноя 28 2022, 11:52

» Орион-128 НГМД запуск 2021 года
автор matrixplus Сб Сен 10 2022, 17:36

» ПЗУ F800 для РК86
автор ведущий_специалист Сб Сен 10 2022, 10:37

» Микропроцессорная лаборатория "Микролаб К580ИК80", УМК-80, УМПК-80 и др.
автор Электротехник Вт Июл 26 2022, 19:33

» Орион-128 SD карта в Орионе
автор matrixplus Чт Июн 02 2022, 09:00

» 7 Мая. День Радио!
автор Viktor2312 Чт Май 12 2022, 10:58

» Серия: Массовая радио библиотека. МРБ
автор Viktor2312 Ср Май 11 2022, 12:17

» Полезные книги
автор Viktor2312 Пн Май 09 2022, 15:07

» Орион 128 Стандарты портов и системной шины Х2
автор matrixplus Вс Май 08 2022, 23:08

» Орион-128 и Орион ПРО еще раз про блоки питания
автор matrixplus Вс Май 08 2022, 19:09

» Орион-128 Программаторы
автор matrixplus Вс Май 08 2022, 19:02

» Орион ПРО история сборки 2021 до 2022
автор matrixplus Вс Май 08 2022, 18:47

» Анонсы монет (New coin).
автор Viktor2312 Сб Май 07 2022, 23:11

» Хочу свой усилок для квартиры собрать не спеша
автор Viktor2312 Сб Май 07 2022, 19:33

» Амфитон 25у-002С
автор Viktor2312 Сб Май 07 2022, 09:38

» Майнер: T-Rex
автор Viktor2312 Вс Май 01 2022, 09:12

» GoWin. Изучение документации. SUG100-2.6E_Gowin Software User Guide. Среда разработки EDA.
автор Viktor2312 Пн Апр 25 2022, 01:01

» GoWin. Изучение документации. UG286-1.9.1E Gowin Clock User Guide.
автор Viktor2312 Сб Апр 23 2022, 18:22

» GoWin. Documentation Database. Device. GW2A.
автор Viktor2312 Ср Апр 20 2022, 14:08

» GOWIN AEC IP
автор Viktor2312 Ср Апр 20 2022, 12:08

Самые активные пользователи за месяц
Нет пользователей

Поиск
 
 

Результаты :
 


Rechercher Расширенный поиск


Изучаем язык программирования Си. Вариант-2.

Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty Изучаем язык программирования Си. Вариант-2.

Сообщение  Viktor2312 Вт Янв 21 2020, 10:27

1
Язык программирования Си.


Данная тема будет посвящена языку программирования Си.


«Си — инструмент, острый, как бритва: с его помощью можно создать и элегантную программу, и кровавое месиво».
Брайан Керниган


Программирование – это процесс написания компьютерных программ.

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

Компьютер не понимает ничего, кроме нулей и единиц. Уж тем более он не понимает никаких слов, поэтому написанный текст программы для него, примерно как и для вас сейчас – набор непонятных символов. Чтобы компьютер понял то, что мы от него хотим, нам нужна специальная программа, которая, если так можно выразиться, переведёт программу, записанную на языке программирования, на язык, понятный компьютеру. Такая программа называется компилятором, а сам процесс перевода программы на понятный компьютеру язык - компиляцией. Итак, процесс написания программ состоит в самом простом виде из следующих шагов:

  • написать код программы в любом текстовом редакторе;

  • скомпилировать исходный код.

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


IDE Pelles C.

Pelles C - небольшая, бесплатная среда разработки под Windows.

Для новичка имеется всё необходимое - встроенный редактор и компилятор. Поддерживаются новые стандарты языка Си (C99, С11).

Отличается простотой в установке и использовании. На официальном сайте имеются версии как для 32-х, так и для 64-разрядных систем.


Установка IDE Pelles C.

1 шаг. Переходим на официальную страницу проекта IDE Pelles C.

Выберите подходящую версию установочного пакета и скачайте его. После завершения скачивания запустите установку.

2 шаг. Первым делом необходимо ознакомиться с условиями лицензионного соглашения.

Изучаем язык программирования Си. Вариант-2. 00001__YAzyk_S.1579594087

Если вы со всем согласны, то поставьте галочку напротив пункта "I accept the terms in the license agreement" и нажмите кнопку "Next".

3 шаг. На этом шаге вы можете выбрать файлы, которые Wndows в дальнейшем будет ассоциировать с Pelles C. Я рекомендую выбрать *.С и *.H.

Изучаем язык программирования Си. Вариант-2. 00002__YAzyk_S.1579594697

4 шаг. В следующем окне можно выбрать каталог установки для Pelles C.

Изучаем язык программирования Си. Вариант-2. 00003__YAzyk_S.1579594866

Выбрав каталог установки нажмите "Install".

5 шаг Дождитесь окончания процесса установки и нажмите "Close".

Изучаем язык программирования Си. Вариант-2. 00004__YAzyk_S.1579595187

6 шаг Запустите программу из меню "Пуск" операционной системы. При первом запуске программы появится предложение создать базу данных с определениями, которые будут использоваться для автозавершения кода. Советую нажать "Build".

Изучаем язык программирования Си. Вариант-2. 00005__YAzyk_S.1579595447

Дождитесь окончания процесса и нажмите "Close".

7 шаг Поздравляю! Вы успешно установили IDE Pelles C на свой компьютер.

Изучаем язык программирования Си. Вариант-2. 00006__YAzyk_S.1579595956

Инструкция по установке IDE Pelles C с официального сайта (на английском).


Создание проекта в IDE Pelles C.


1 шаг. Запускаем Pelles C.

Изучаем язык программирования Си. Вариант-2. 00007__YAzyk_S.1579604119

Для того чтобы создать новый проект на стартовой странице нажмите на "Start a new project".

2 шаг. В появившемся окне выбираем пункт "Win64 Console programm (EXE)", так как мы собираемся создавать консольное приложение. Пока попробуем выбрать именно WIN64, так как ОС 64-разрядная, можно поэкспериментировать и выбрать Win32.

Изучаем язык программирования Си. Вариант-2. 00008__YAzyk_S.1579604693

Кроме того, в поле "Name" необходимо ввести название для проекта. В поле "Location" указана основная папка проекта, в ней будут храниться все файлы проекта. Используя кнопку "..." вы можете выбрать самостоятельно, где нужно сохранить проект. Закончив с основными настройками проекта нажмите кнопку "Ok".

3 шаг. Если всё сделано правильно, то в правой части окна появится отдельная вкладка, в которой появится ваш проект.

Изучаем язык программирования Си. Вариант-2. 00009__YAzyk_S.1579604936

Теперь добавим в проект файл с исходным кодом. Именно в нём мы и будем писать текст будущей программы.

4 шаг. Правой кнопкой мышки кликните по значку проекта.

Изучаем язык программирования Си. Вариант-2. 00010__YAzyk_S.1579605116

В появившемся контекстном меню выберите пункт "Add files to project...".

5 шаг. Появится дополнительное окно, в котором необходимо ввести имя для файла с кодом. Я назвал source_001.с, вы можете выбрать другое имя.

Изучаем язык программирования Си. Вариант-2. 00011__YAzyk_S.1579605288

После того, как записали имя файла нажмите на кнопку "Открыть". Кроме того, используя это окно можно добавить любой ранее написанный файл с кодом в проект.

6 шаг. В панели с проектом должен появится добавленный вами файл.

Изучаем язык программирования Си. Вариант-2. 00012__YAzyk_S.1579605446

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

7 шаг. Откроется новая вкладка редактора с исходным файлом. Именно в этом окошке вы и будете писать свои программы.

Изучаем язык программирования Си. Вариант-2. 00013__YAzyk_S.1579606143

Для каждой новой программы требуется создать новый проект.


Скопируйте в окно редактора следующий код:

Листинг 1. Программа «Hello, World!»

Код:

#include <stdio.h>
int main(void) {
  printf("Hello, World!\n");
  return (0);
}

Это простая программа, которая выводит на экран сообщение: "Hello, World!". Большинство пособий по программированию начинается как раз с написания этой программы. Можно сказать, что это стало уже доброй традицией в обучении программированию.

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

Компилируем исходный код, используя кнопку меню.

Изучаем язык программирования Си. Вариант-2. 00015__YAzyk_S.1579608517

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

Так как ошибок при компиляции не обнаружено, то запускаем нашу программу, используя горячие клавиши Ctrl+F5, либо кнопку меню. Если всё сделано верно, то перед вам откроется окно консоли, в котором будет выведена строка "Hello, world!". И наша первая программа РАБОТАЕТ.

Изучаем язык программирования Си. Вариант-2. 00014__YAzyk_S.1579608753


Про сообщения компилятора, ошибки и предупреждения.

Компилятор выдаёт два типа сообщений: предупреждения (warning) и ошибки (error).
Предупреждения нам не страшны. К ним можно относиться как к хорошим советам, которыми, впрочем, можно не пользоваться. Программу можно запускать даже при наличии предупреждений, но вот если компилятор выдаёт ошибку (error), то такую программу запустить не получится. Необходимо сначала исправить все ошибки в вашей программе.

Традиционно в большинстве книг по программированию, в качестве первой программы предлагается программа "Hello, world". Этим никого не удивишь. Но вот что интересно.
Такая традиция появилась благодаря книге "Язык программирования Си" (The C programming language) Б. Керниган, Д. Ричи выпущенной в свет в 1978 году. Эту книгу еще называют "white book".
Второе издание (1988) этой книги было переведено на 27 языков.


Продолжение в следующем посте...


.


Последний раз редактировалось: Viktor2312 (Чт Сен 16 2021, 20:19), всего редактировалось 7 раз(а)

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Вт Янв 21 2020, 14:30

2
Происхождение языка Си.

Деннис Ричи разработал и первым реализовал язык программирования Си на DEC PDP-11 с операционной системой UNIX. Язык является результатом разработки старого языка BCPL. Мартин Ричардс разработал BCPL под влиянием созданного Кеном Томсоном языка В, который привёл к разработке Си в 1970 году. В течение многих лет стандартом Си де-факто была версия, поставляемая для операционной системы UNIX System V. Она описана в книге Брайана Кернигана и Денниса Ричи «Язык программирования С» (Prentice-Hall, 1978). Растущая популярность компьютеров привела к созданию множества приложений для Си. Казалось просто чудом, что исходные коды для различных реализаций Си имеют высокую степень совместимости. Тем не менее, поскольку не существовало стандарта, были и несоответствия. Для исправления ситуации ANSI предложил летом 1983 года создать комитет по созданию ANSI-стандарта языка Си. Стандарт был полностью принят в декабре 1989 года.


Язык среднего уровня.

С часто называют языком среднего уровня. Это не означает, что Си менее мощный, более труден в изучении или менее разработан, чем языки высокого уровня типа Бейсик или Паскаль. Также это не означает, что у него имеются проблемы, присущие ассемблеру. Определение Си как языка среднего уровня означает, что он объединяет элементы языков высокого уровня с функциональностью ассемблера. В таблице показанно место Си среди других языков.

Таблица. Место Си в мире языков.
Изучаем язык программирования Си. Вариант-2. 9ed_0048

Как язык среднего уровня, Си позволяет манипулировать битами, байтами и адресами — основными элементами, с которыми работает компьютер. И это всё несмотря на то, что код Си имеет высокую степень переносимости. (Под переносимостью подразумевается возможность переноса программного обеспечения, написанного для одного типа компьютеров, на другой.) Например, если программа, написанная для Apple Macintosh, может быть легко перенесена на IBM PC, то программа является переносимой.

Языки высокого уровня поддерживают концепцию типов данных. Тип данных определяет набор значений, которые переменная может хранить, и набор операций, которые могут выполняться над переменной. Стандартными типами данных является целочисленный, символьный и вещественный. Хотя Си имеет 5 основных встроенных типов данных, он не так сильно типизирован, как языки типа Паскаль или Ада. Фактически Си позволяет осуществлять большинство преобразований типов. Например, символьный и целочисленный типы могут быть свободно смешаны в большинстве выражений. В противоположность языкам высокого уровня Си в основном не выполняет проверку ошибок времени выполнения типа проверки выхода за границы массива. Данные проверки возлагаются на программиста.

Как было упомянуто выше, Си позволяет напрямую манипулировать битами, байтами, словами и указателями. Это необходимо для Программирования на системном уровне, где такие операции наиболее типичны. Другим важным аспектом Си является то, что в нём имеется только 32 ключевых слова (27 — из стандарта Кернигана и Ричи и 5 добавлено комитетом по стандартизации ANSI), являющихся командами, образующими язык Си. Высокоуровневые языки, как правило, имеют несколько больше ключевых слов.


Структурированный язык.

Хотя термин структурированный язык не применим напрямую к Си, Си часто называют структурированным языком, поскольку он структурно схож с Алголом, Паскалем и Модулой-2. (Структурированный язык позволяет объявлять процедуры или функции внутри других процедур или функций. Таким образом, концепции глобальности и локальности расширяются с использованием дополнительных правил видимости, управляющих видимостью переменных или процедур. Поскольку Си не позволяет создавать функции внутри функции, он не является полностью структурированным языком.)

Отличительной особенностью структурированного языка является разделение кода и данных. Разделение является способностью языка отделять и прятать от оставшейся части программы всю информацию и инструкции, необходимые для выполнения некоторых задач. Одним из способов достижения разделения является использование подпрограмм, применяющих локальные (временные) переменные. Используя локальные переменные, программист может создавать подпрограммы таким образом, что события, возникающие в них, не будут влиять на оставшуюся часть программы. Данная возможность позволяет легко осуществлять разделение кода в Си-программах. При разработке такого рода функции, всё, что нужно знать — это что делает функция, а не как она это делает. Следует помнить, что излишнее использование глобальных переменных (переменных, видимых всей программой), может приводить к ошибкам и ужасающим эффектам в программах. (Каждый, кто программировал на Бейсике, хорошо знаком с этой проблемой.)

Структурированный язык предоставляет множество возможностей программирования. Он поддерживает несколько типов циклов, таких как while, do-while и for. В структурированном языке использование goto нежелательно (лучше вообще полностью отказаться от goto). Структурированный язык не требует концепции строгих полей. Ниже приведено несколько примеров структурированных и неструктурированных языков:

Изучаем язык программирования Си. Вариант-2. 9ed_0049

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

Основным структурным компонентом Си является функция — самостоятельная подпрограмма Си. В Си функции являются кирпичиками, на которых основаны все программы. Они позволяют определять и кодировать отдельные задачи в программах, тем самым позволяя программе быть модульной. После того, как функция создана, можно полагаться на корректность её работы в различных ситуациях без создания побочных эффектов в других частях программы. Фактически можно создать самостоятельные функции, код которых не будет влиять на другие функции, что жизненно необходимо для больших проектов.

Другим способом структуризации и разделения кода Си является использование блоков кода. Блок кода — это группа логически связанных операторов, которая воспринимается как модуль. В Си блок кода создаётся путём помещения последовательности операторов в фигурные скобки. Например:

if (х<10) {
printf("мало, попробуйте ещё раз");
reset_counter (-1);
}

Два оператора после if, находящиеся в фигурных скобках, будут выполняться, если х меньше 10. Данные два оператора со скобками образуют блок кода. Они являются логической единицей — один из операторов не может выполниться без другого. Блок кода не только позволяет чётко, элегантно и эффективно реализовывать большинство алгоритмов, но также позволяет программисту понять истинную природу кода.


Язык программирования.

Рассмотрим классические примеры не программистских языков COBOL и Бейсик. COBOL предназначен для того, чтобы не программисты могли читать и понимать программу. Бейсик был создан, чтобы не программисты могли заставить компьютер решать относительно простые проблемы.

В противоположность им, Си был создан для настоящих программистов. Конечным результатом является то, что Си предоставляет всё, необходимое программисту: некоторые ограничения, блоки структур, самостоятельные функции и небольшой набор ключевых слов. Просто изумительно, что, используя Си, программист может достичь эффективности ассемблерного кода, вместе со структурами Алгола и Модулы-2. Нет ничего удивительного, что Си — это один из наиболее популярных языков среди профессиональных программистов.

Си может часто использоваться вместо ассемблера, что благоприятно воздействует на его популярность среди программистов. Ассемблер использует символическое представление двоичного кода выполняемого компьютером. Каждая ассемблерная операция преобразуется в отдельную команду для компьютера. Хотя ассемблер предоставляет программисту возможность выполнения задач с максимальной гибкостью и эффективностью, существуют некоторые трудности при разработке и отладке программ. Более того, поскольку ассемблер является неструктурированным языком, конечная программа постепенно превращается в мешанину переходов, вызовов и индексов. Из-за данного недостатка программы, написанные на ассемблере, трудно читать, улучшать и поддерживать. Возможно, более важным является то, что подпрограммы ассемблера не переносимы между компьютерами с различными процессорами.

Первоначально Си использовался для системного программирования. Системные программы являются частью большого класса программ, среди которых находятся операционная система компьютера и утилиты поддержки. Например, нижеприведённые программы обычно называются системными:

  • Операционные системы.

  • Интерпретаторы.

  • Редакторы.

  • Ассемблерные программы.

  • Компиляторы.

  • Процессоры баз данных.

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

Возможно, наиболее существенной причиной того, что Си используется для программирования различных задач, — это то, что программистам он нравится! Он обладает скоростью ассемблера и гибкостью Форта, но и некоторыми ограничениями Паскаля и Модулы-2. Каждый Си-программист может создавать и поддерживать уникальную библиотеку функций, которая может быть использована в различных программах. Си позволяет программистам легко управлять проектами. И, конечно, это язык прародитель, на основе которого создан С++.


Компиляторы против интерпретаторов.

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

Интерпретатор построчно читает исходный код программы и выполняет инструкции, содержащиеся в текущей строке, потом переходит к следующей строке. Компилятор читает всю программу и преобразует её в объектный код, который является такой формой кода, которая может напрямую выполняться компьютером. Объектный код также называется двоичным кодом или машинным кодом. Если программа скомпилирована, то сам исходный код больше не влияет на дальнейшую работу программы.

Когда используется интерпретатор, он должен присутствовать все время для выполнения программы. Например, в традиционном Бейсике необходимо запустить интерпретатор Бейсика, затем загрузить программу и набрать RUN для запуска. Интерпретатор построчно проверит на правильность и выполнит программу. Этот медленный процесс возникает каждый раз при запуске программы. В противоположность этому, компилятор преобразует программу в объектный код, который может напрямую выполняться компьютером. Поскольку компилятор переводит программу только один раз, то всё, что необходимо сделать, — это запустить программу, обычно путём простого набора имени. Таким образом, компиляция кода Должна выполняться только один раз, в то время как интерпретация — каждый раз при запуске программы.

Два термина, которые будут часто встречаться, — это время компиляции, которое соответствует событиям, возникающим в процессе компиляции, и время выполнения, которое соответствует событиям, возникающим при выполнении программы. Обычно данные термины встречаются при обсуждении ошибок в фразах «ошибки времени компиляции» и «ошибки времени выполнения».


Несколько основных принципов.


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

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

Чтобы решить задачу с помощью компьютера, вы должны представить решение задачи в терминах команд специфического компьютера. Компьютерная программа - это только набор команд, необходимых для решения определённой задачи. Подход или метод, который используется для решения задачи, известен как алгоритм. Например, если Вы хотите разработать программу, которая проверяет, является ли число чётным или нечётным, то вы пишете последовательность команд, которые решают задачу, после чего эта последовательность команд становится программой. Метод, который используется в целях проверки того, является ли число чётным или нечётным, называется алгоритмом. Обычно, чтобы разработать программу для решения определённой задачи, решение сначала описывается в виде алгоритма, а затем на его основе разрабатывается программа, которая и выполняет этот алгоритм. Так, алгоритм для проверки числа на чётность мог бы быть описан следующим образом.

Сначала число делится на два. Если остаток от деления является нулевым, число является чётным, в ином случае число нечётное.

Имея логически правильный алгоритм, можно последовательно написать утверждения, необходимые для выполнения заданного алгоритма на специфической компьютерной системе. Эти утверждения должны быть выражены в командах определённого языка или как частный случай языка Си.


Языки высокого уровня (ЯВУ).

Когда появились первые вычислительные устройства, то единственным способом их программирования было ручное введение в память устройства двоичных чисел, которые соответствовали определённым машинным командам. Следующее усовершенствование технологии ввода программы произошло с развитием ассемблеров - программ, которые давали возможность программисту работать с компьютером на несколько более высоком уровне. Вместо того, чтобы вводить последовательности двоичных чисел, составляющих программу для выполнения специфической задачи, ассемблер позволяет программисту использовать мнемокоды, или понятные аббревиатуры, которые преобразуются в соответствующие машинные команды. С помощью мнемокодов можно также управлять расположением команд в памяти. Программа, называемая ассемблером, транслирует исходную программу на языке ассемблера из её символического формата в соответствующие машинные команды компьютерной системы.

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

Поэтому с развитием технологий программирования произошёл закономерный переход к т. н. языкам высокого уровня, где одним из первых был язык FORTRAN (FORmula TRANslation). Программисты, разрабатывающие программы на языке FORTRAN, больше не должны были интересоваться архитектурой компьютера, а отдельные операции, выполняемые на FORTRAN, были намного более сложными по сравнению с командами ассемблера и не были связаны с системой команд специфической машины. Однако команды языка FORTRAN, или утверждение, приводили к выполнению многих отдельных машинных команд, в отличии от взаимно-однозначного соответствия между командами ассемблера и машинными командами.

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


Операционные системы.

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

Существует много различных операционных систем, например Unix, Linux, Mac OS X, Windows и т. д.


Компиляция программ.

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

Изучаем язык программирования Си. Вариант-2. 00016__YAzyk_S.1579649721

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

Программа, которая должна компилироваться, сначала записывается в виде файла на компьютерной системе. Имеются различные соглашения по именованию файлов, но в основном, выбор названия остаётся за вами. Исходной программе на языке Си можно давать любое имя, но последними двумя символами должны быть ".c" (это не является обязательным требованием, это просто соглашение). Поэтому, например, имя prog1.c является допустимым именем файла для программы на языке Си в вашем компьютере.

Для написания программ на языке Си можно использовать текстовый редактор, но лучше использовать специализированную IDE. Программа на языке программирования Си, которая записана в файл, называется исходной программой, или исходным модулем, поскольку представляет собой оригинальный текст программы. После того как исходная программа записана в файл, её можно компилировать.

На первом шаге процесса компиляции компилятор анализирует каждое утверждение исходной программы и производит проверку на соответствие синтаксису и семантике языка.

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

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

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

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

Ассемблер анализирует каждую команду языка ассемблера и преобразовывает её в двоичный формат. При этом формируется объектный код, который записывается в соответствующий файл. Этот файл обычно имеет то же самое имя, что и исходный файл, но с расширением ".obj" которое заменяет ".с" в имени файла.

После того, как исходная программа переводится в объектный код, её необходимо скомпоновать. Этот процесс также выполняется автоматически. Цель этапа компоновки состоит в том, чтобы получить законченную программу, готовую для выполнения на компьютере. Если исходная программа использует другие программы, которые были предварительно скомпилированы, то на этом этапе все программы объединяются. В объектные программы, которые используют библиотечные файлы системы, также будут сделаны вставки из библиотек во время выполнения этого этапа.

Процесс компиляции и объединения программ часто называют построением. Конечный объединённый файл, который является выполнимым форматом объектного кода, сохраняется в заключительном файле, который можно запустить или выполнить. В операционной системе Windows исполняемый файл обычно имеет то же самое имя, что и исходный файл с расширением ".exe".

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

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


Интегрированная среда разработки (IDE).

Ранее были рассмотрены отдельные этапы создания исполняемой программы на языке Си.  Этапы редактирования, компилирования, выполнения и отладки программ. Но обычно эти этапы объединены в едином приложении, называемом интегрированной средой разработки, для обозначения которой часто используют аббревиатуру IDE (Integrated Development Environment). Интегрированная среда разработки - это программа на основе оконного интерфейса, например Pelles C, которая позволяет легко управлять большими объектами, редактировать файлы, компилировать, компоновать, выполнять и отлаживать ваши программы.


Продолжение в следующем посте...


...


Последний раз редактировалось: Viktor2312 (Ср Сен 22 2021, 17:05), всего редактировалось 5 раз(а)

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Ср Янв 22 2020, 01:15

3
Рассмотрим первую программу.

Рассмотрим нашу первую программу более подробно.

Код:
#include <stdio.h>
int main (void)
{
  printf ("Hello, World!\n");
  return (0);
}

Первая строка программы:

#include <stdio.h>

означает, что заголовочный файл с именем stdio.h должен быть включён в начало программы. В нём содержится информация для компилятора о том, как выполнить вывод значений (процедура printf) из программы. Или иными словами, первая строка программы сообщает компилятору, что он должен включить информацию о стандартной библиотеке ввода-вывода. Эта строка встречается в начале многих исходных файлов Си-программ. Позже мы вернёмся к этому, для более детального рассмотрения.

Вторая строка нашей программы:

int main (void)

сообщает системе, что именем программы является main и что эта программа возвращает целое число, о чём говорит аббревиатура int. Имя main - это специальное имя, которое указывает, где программа должна начать выполнение. Круглые скобки сразу после слова main свидетельствуют о том, что это имя функции. Ключевое слово void, которое находится в круглых скобках, означает, что в функцию main не передаётся никаких аргументов. Функции в языке Си похожи на подпрограммы. Обычно вы вольны придумывать любые имена для своих функций, но main - особое имя, любая программа начинает свои вычисления с первой инструкции функции main. Обычно main для выполнения своей работы пользуется услугами других функций, одни из них пишутся самим программистом, а другие берутся готовыми из имеющихся в его распоряжении библиотек. Один из способов передачи данных между функциями состоит в том, что функция при обращении к другой функции передаёт ей список значений, называемых аргументами. Этот список берётся в скобки и помещается после имени функции. В нашем примере main определена как функция, которая не ждёт никаких аргументов. Теперь, когда мы определили функцию, мы должны написать, что именно эта функция должна выполнять. Это можно сделать, разместив все утверждения программы в паре фигурных скобок. Все утверждения программы, заключённые в фигурные скобки, будут относиться к функции main. Инструкции функции заключаются в фигурные скобки {}. В нашей программе записано только два таких утверждения. Первое утверждение определяет, что должна быть вызвана подпрограмма с именем printf. В качестве аргумента в подпрограмму printf будет передана последовательность символов.

printf ("Hello, World!\n");

Функция вызывается по имени, после которого, в скобках, указывается список аргументов. Таким образом, приведенная выше строка — это вызов функции printf с аргументом "Hello, World!\n". Функция printf — это библиотечная функция, которая в данном случае напечатает последовательность символов, заключённую в двойные кавычки.

Последовательность символов в двойных кавычках, такая как "Hello, World!\n", называется строкой символов или строковой константой.

Функция printf находится в библиотеке компилятора Си, и она просто печатает или отображает те аргументы, которые были подставлены вместо параметров. Последние два символа в строке, а именно обратная наклонная черта "\" и символ "n", вместо составляют символ newline (новая строка). При этом система должна сделать то, о чём говорит название символа - выполнить переход на новую строку. Любые символы, которые будут напечатаны после символа newline, появятся на следующей строке дисплея. Фактически символ newline подобен клавише возврат каретки на пишущей машинке.

Все утверждения в программе на языке С должны заканчиваться точкой с запятой (;). Именно поэтому точка с запятой должна быть сразу поставлена после закрывающей круглой скобки при вызове функции printf.

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

Последнее утверждение, написанное как:

return (0);

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

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

А теперь немного усложним нашу программу. Результат её работы будет следующим:

Изучаем язык программирования Си. Вариант-2. 00017__YAzyk_S.1580071494

Код этой простенькой программки следующий:

Изучаем язык программирования Си. Вариант-2. 00018__YAzyk_S.1580071733

Код:
#include <stdio.h>
int main (void)
{
  printf ("Testing...\n.1\n..2\n...3\n....4\n.....5\n\n\n");
  printf ("\tHello forum ruecm\n");
  printf ("\n");
  printf ("\tHello Vita\n");
  printf ("\n");
  printf ("\t@_Yann._lisss._ is the best human being");
  printf ("\n\n\n\n\n\n\n\n\n\n");
  return (0);
}

Как видно из текста программы мы добавили ещё шесть функций printf, а также применили символ табуляции \t. И если детально обратить внимание на первую функцию printf

printf ("Testing...\n.1\n..2\n...3\n....4\n.....5\n\n\n");

и на результат её работы, то можно сделать вывод, что можно не делать отдельный вызов функции printf, для вывода каждой строки.


Отображение значений переменных, комментарии,
цикл while.

Функция printf очень часто будет использоваться в программах, поскольку она обеспечивает простой и удобный способ для отображения результатов работы программы. Мало того, что могут быть отображены простые фразы, как мы уже делали это раньше, но также можно отображать и значения переменных и результаты вычислений. Программа, показанная на листинге ниже, использует функцию printf для отображения результатов сложения двух чисел, а именно чисел 50 и 25.

Изучаем язык программирования Си. Вариант-2. 00019__YAzyk_S.1580111621

Код, для вставки в IDE:

Код:
#include <stdio.h>
int main (void)
{
  int sum;

  sum = 50 + 25;
  printf("The sum of 50 and 25 %i\n", sum);

  return (0);

}

Результат её работы:

Изучаем язык программирования Си. Вариант-2. 00020__YAzyk_S.1580111879

В нашей программе, в первом утверждении объявляется переменная sum, которая имеет тип int. В языке С требуется, чтобы все переменные были объявлены прежде, чем они будут использованы в программе. Объявление переменной сообщает компилятору языка С, как именно будет использоваться отдельная переменная в данной программе. Эта информация необходима компилятору для генерирования команд, с помощью которых будут храниться и извлекаться значения для данной переменной. Переменная, объявленная с ключевым словом int, может использоваться только для хранения целочисленных значений, т. е. чисел без дробной части. Примеры целочисленных значений: -5; 4; 89; -23; 23; 70; 200 и 0. Числа с дробной частью, такие как: 3,24; 2,305; 7,845; 26,0. Называются вещественными числами или числами с плавающей запятой.

Целочисленная переменная sum используется для хранения результатов сложения двух целых чисел 50 и 25. Пустая строка после объявления этой переменной оставлена преднамеренно, для визуального отделения объявления переменных программы от утверждений программы. Это не обязательно, но лучше оформлять программу в соответствии с требованиями стиля. Иногда добавление отдельной пустой строки в программе, может сделать программу более читаемой.

Утверждение программы:

sum = 50 + 25;

выглядит как и в большинстве других языков программирования: число 50 суммируется (о чём говорит знак "+") с числом 25, и результат сохраняется (что делается с помощью оператора присваивания, который выражен знаком "=") в переменной sum.

Вызов функции printf в программе записан с двумя параметрами, заключёнными в круглые скобки. Эти параметры разделяются запятой. Однако наряду с символьной строкой мы также должны отобразить значения некоторых переменных программы. В нашем случае будет отображено значение переменной sum, которое будет выведено в той же строке, что и надпись The sum of 50 and 25.

Будет отображена сумма чисел 50 и 25. Для этого в строке, которая используется в качестве первого параметра, поставлен символ процента - специальный символ, который распознаётся функцией printf. Символ, который непосредственно следует за знаком процента, определяет, какое значение должно быть отображено в этом месте. В нашей программе, символ i будет распознан функцией printf как команда отображать значение как целое число.

Всякий раз, когда функция printf находит символы "%i" в символьной строке, она отображает значение очередного параметра функции. Поскольку именно sum и будет очередным параметром функции printf, его значение и будет отображено после того, как будет отображена строка "The sum of 50 and 25".

Рассмотрим следующий пример, на листинге ниже:

Изучаем язык программирования Си. Вариант-2. 00021__YAzyk_S.1580131656

Код, для вставки в IDE:

Код:
#include <stdio.h>
int main (void)
{
  int value1, value2, sum;

  value1 = 50;
  value2 = 25;
  sum = value1 + value2;
  printf ("The sum of %i and %i is %i\n", value1, value2, sum);

  return (0);
}

Результат её работы:

Изучаем язык программирования Си. Вариант-2. 00022__YAzyk_S.1580132212

В первом утверждении программы объявлены три переменные, названные value1, value2 и sum. Все они имеют тип int. Это утверждение будет эквивалентно трём строкам, где будут описаны три утверждения следующим образом.

int value1;
int value2;
int sum;

После того как эти три переменные были объявлены, программа присваивает значение 50 для переменной value1 и затем присваивает значение 25 переменной value2. После этого вычисляется сумма этих двух переменных и результат присваивается переменной sum. При обращении к функции printf теперь записано четыре аргумента. Повторим ещё раз, что первый аргумент, обычно называемый строкой форматирования, указывает системе, как остающиеся аргументы должны быть отображены. Значение переменной value1 должно быть отображено непосредственно после вывода строки "The sum of". Точно так же значения для переменных value2 и sum должны быть отображены в соответствующих местах, где в строке форматирования встречаются символы "%i".

Следует обратить внимание, что функция printf также позволяет использовать для отображения целого числа символы управления форматом %d.

Приведённая ниже программа выполняет вычисления и печатает в два столбика соответствия температур по Фаренгейту температурам по Цельсию.

Изучаем язык программирования Си. Вариант-2. 00023__YAzyk_S.1580152037

Код, для вставки в IDE:

Код:
#include <stdio.h>

/* Печать таблицы температур по Фаренгейту
и Цельсию для fahr = 0, 20, ..., 300 */

int main (void)
{
  int fahr, celsius;
  int lower, upper, step;

  lower = 0; /* нижняя граница таблицы температур */
  upper = 300; /* верхняя граница */
  step = 20; /* шаг*/

  fahr = lower;
  while (fahr <= upper)
 {
 celsius = 5 * (fahr-32) / 9;
 printf ("%d\t%d\n", fahr, celsius);
 fahr = fahr + step;
    }
  printf ("\n\n\n\n\n");

  return (0);
}

Результат её работы:

Изучаем язык программирования Си. Вариант-2. 00024__YAzyk_S.1580152340

Как и предыдущие программы, эта программа состоит из определения одной-единственной функции main. Она длинее предыдущих программ, но по сути ненамного сложнее. На ней мы освоим несколько новых возможностей, вкратце комментарии, повторим объявления, переменные, арифметические выражения, циклы и форматный вывод.

Две строки:

/* Печать таблицы температур по Фаренгейту
и Цельсию для fahr = 0, 20, ..., 300 */

являются комментарием, который в данном случае кратко объясняет, что делает программа. Все символы, помещённые между /* и */, игнорируются компилятором, и этим можно свободно пользоваться, чтобы сделать программу более понятной. Комментарий можно располагать в любом месте.

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

Есть два способа вставить комментарии в программу на языке С. Первый мы уже рассмотрели, на примере последней программы. Эта форма комментария часто используется в том случае, когда для комментария необходимо несколько строк программы. Второй способ добавлять комментарий к программе - использовать два последовательных символа наклонной черты вправо (//). Всё, что следует за этими наклонными чертами вплоть до конца строки, игнорируется компилятором.

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

Это должно войти в привычку - вставлять комментарии в программу во время её написания, поскольку именно в этот период намного проще её документировать.

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

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

int fahr, celsius;
int lower, upper, step;

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

Числа типа int бывают как 16-разрядные (лежат в диапазоне от -32768 до 32767), так и 32-разрядные. Числа типа float обычно представляются 32-разрядными словами, имеющими по крайней мере 6 десятичных значащих цифр (лежат приблизительно в диапазоне от 10-38 до 1038).

Помимо int и float в языке С имеется ещё несколько базовых типов для данных, это:

char - символ - единичный байт;

short - короткое целое;

long - длинное целое;

double - с плавающей запятой с двойной точностью.

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

Вычисления в нашей программе преобразования температур начинаются с инструкций присваивания:

lower = 0;
upper = 300;
step = 20;

fahr = lower;

которые устанавливают указанные в них переменные в начальные значения. Любая инструкция заканчивается точкой с запятой.

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

while (fahr <= upper)
{
тело цикла
}

Он работает следующим образом. Проверяется условие в скобках. Если оно истинно (значение fahr меньше или равно upper), то выполняется тело цикла (три инструкции заключённые в фигурные скобки). Затем опять проверяется условие, и если оно истинно, то тело цикла выполняется снова. Когда условие становится ложным (fahr превысило upper), цикл завершается, и вычисления продолжаются с инструкции, следующей за циклом. Поскольку никаких инструкций за циклом нет, кроме return (0); то программа по сути завершает работу.

Телом цикла while может быть одна или несколько инструкций, заключённых в фигурные скобки, как в программе преобразования температур, или одна-единственная инструкция без скобок, как в цикле представленном ниже:

Код:
while (i < J)
    i = 2 * i;

И в том и в другом случае инструкции, находящиеся под управлением while, мы будем записывать со сдвигом, равным одной позиции табуляции, которая в программе указывается четырьмя пробелами. Благодаря этому будут ясно видны инструкции, расположенные внутри цикла. Отступы подчёркивают логическую структуру программы. С-компилятор не обращает внимания на внешнее оформление программы, но наличие в нужных местах отступов и пробелов существенно влияет на то, насколько легко она будет восприниматься человеком при просмотре. Чтобы лучше была видна логическая структура выражения, рекомендуется на каждой строке писать только по одной инструкции и с обеих сторон от операторов ставить пробелы.

Большая часть вычислений в нашей программе выполняется в теле цикла. Температура по Фаренгейту переводится в температуру по Цельсию и присваивается переменной celsius посредством инструкции:

celsius = 5 * (fahr-32) / 9;

Причина, по которой мы сначала умножаем на 5 и затем делим на 9, а не сразу умножаем на 5/9, связана с тем, что в С, как и во многих других языках, деление целых сопровождается отбрасыванием (truncation - усечение), т. е. потерей дробной части. Так как 5 и 9 — целые, отбрасывание в 5/9 дало бы нуль, и на месте температур по Цельсию были бы напечатаны нули.

Этот пример прибавил нам ещё немного знаний о том, как работает функция printf. Функция printf - это универсальная функция форматного ввода-вывода. Её первый аргумент - строка символов, в которой каждый символ % соответствует одному из последующих аргументов (второму, третьему, ...), а информация, расположенная за символом %, указывает на вид, в котором выводится каждый из этих аргументов. Например %d специфицирует выдачу аргумента в виде целого десятичного числа, и инструкция

printf ("%d\t%d\n", fahr, celsius);

печатает целое fahr, выполняет табуляцию (\t) и печатает целое celsius.

В функции printf каждому спецификатору первого аргумента (конструкции начинающейся с %) соответствует второй аргумент, третий аргумент и т. д. спецификаторы и соответствующие им аргументы должны быть согласованы по количеству и типам: в противном случае напечатано будет не то, что нужно.

Функция printf не является частью языка С, и вообще в языке нет никаких специальных конструкций, определяющих ввод-вывод. Функция printf - лишь полезная функция стандартной библиотеки, которая обычно доступна для С-программ. Поведение функции printf, однако, оговорено стандартом ANSI, и её свойства должны быть одинаковыми во всех С-системах, удовлетворяющих требованиям стандарта.

Существует ещё две проблемы, связанные с программой преобразования температур. Одна из них (более простая) состоит в том, что выводимый результат выглядит несколько неряшливо, поскольку числа не выровнены по правой позиции колонок, да и хоть какой-то таблицы нет вообще, её контуров. Это легко исправить, добавив в каждый из спецификаторов формата %d указание о ширине поля и символы которые будут обозначать контуры самой таблицы, при этом программа будет печатать числа, прижимая их к правому краю указанных полей. Например мы можем написать:

printf ("| %4d | %4d | \n", fahr, celsius);

чтобы в каждой строке первое и второе число печатать в поле из четырёх позиций. В результате имеем следующую программу:

Изучаем язык программирования Си. Вариант-2. 00025__YAzyk_S.1580504819

Код, для вставки в IDE:

Код:
#include <stdio.h>

/* Печать таблицы температур по Фаренгейту
и Цельсию для fahr = 0, 20, ..., 300 */

int main (void)
{
    int fahr, celsius;
    int lower, upper, step;

    lower = 0; //нижняя граница таблицы температур
    upper = 300; // верхняя граница
    step = 20; // шаг

    fahr = lower;
 printf ("--------------- \n");
    while (fahr <= upper)
    {
        celsius = 5 * (fahr-32) / 9;
        printf ("| %4d | %4d | \n", fahr, celsius);
        fahr = fahr + step;
    }
 printf ("---------------");
    printf ("\n\n\n\n\n");

    return (0);
}

Результат её работы:

Изучаем язык программирования Си. Вариант-2. 00026__YAzyk_S.1580504991

Вторая, более серьёзная проблема связана с тем, что мы пользуемся целочисленной арифметикой и поэтому не совсем точно вычисляем температуры по шкале Цельсия. Например, 0 оF на самом деле (с точностью до десятой) равно -17,8 оС, а не -17. Чтобы получить более точные значения температур, нам нужно пользоваться не целочисленной арифметикой, а арифметикой с плавающей точкой (далее именно так будем называть арифметику с плавающей запятой). А это потребует некоторых изменений в программе.

Изучаем язык программирования Си. Вариант-2. 00027__YAzyk_S.1580522931

Код, для вставки в IDE:

Код:
#include <stdio.h>

/* Печать таблицы температур по Фаренгейту и Цельсию для
fahr = 0, 20, ..., 300; вариант с плавающей точкой. */

int main (void)
{
    float fahr, celsius;
    int lower, upper, step;

    lower = 0; //нижняя граница таблицы температур
    upper = 300; // верхняя граница
    step = 20; // шаг

    fahr = lower;
    printf ("\n\n  ----------------- \n");
    while (fahr <= upper)
    {
        celsius = (5.0/9.0) * (fahr-32.0);
        printf ("  | %4.0f | %6.1f | \n", fahr, celsius);
        fahr = fahr + step;
    }
    printf ("  -----------------");
    printf ("\n\n\n\n\n");

    return (0);
}

Результат её работы:

Изучаем язык программирования Си. Вариант-2. 00028__YAzyk_S.1580523098

Программа мало изменилась. Она отличается от предыдущей лишь тем, что fahr и celsius объявлены как float, а формула преобразования написана в более естественном виде. В предыдущем варианте нельзя было написать 5/9, так как целочисленное деление в результате отбрасывания дробной части дало бы нуль. Десятичная точка в константе указывает на то, что последняя рассматривается как число с плавающей точкой, и 5.0/9.0, таким образом, есть частное от деления двух значений с плавающей точкой, которое не предполагает отбрасывания дробной части. В том случае, когда арифметическая операция имеет целые операнды, она выполняется по правилам целочисленной арифметики. Если же один операнд с плавающей точкой, а другой - целый, то перед тем, как операция будет выполнена, последний будет преобразован в число с плавающей точкой. Если бы мы написали fahr-32, то 32 было бы автоматически преобразовано в число с плавающей точкой. Тем не менее при записи констант с плавающей точкой мы всегда используем десятичную точку, причём даже в тех случаях, когда константы на самом деле имеют целые значения. Это делается для того, чтобы обратить внимание читающего программу на их природу.

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

fahr = lower;

и проверка

while (fahr <= upper)

работают естественным образом, т. е. перед выполнением операции значение int приводится к float.

Спецификация %4.0f в printf определяет печать числа с плавающей точкой (в данном случае числа fahr в поле шириной не более четырёх позиций без десятичной точки и дробной части. Спецификация %6.1f описывает печать другого числа celsius в поле из шасти позиций с одной цифрой после десятичной точки.

Ширину и точность можно не задавать: %6f означает, что число будет занимать не более шести позиций; %.2f - число имеет две цифры после десятичной точки, но ширина не ограничена; %f просто указывает на печать числа с плавающей точкой.

%d - печать десятичного целого;

%6d - печать десятичного целого в поле из шести позиций;

%f - печать числа с плавающей точкой;

%.2f - печать числа с плавающей точкой с двумя цифрами после десятичной точки;

%6.2f - печать числа с плавающей точкой с двумя цифрами после десятичной точки, в поле из шести позиций.

Кроме того, printf допускает следующие спецификаторы: %o для восьмеричного числа; %x для шестнадцатеричного числа; %c для символа; %s для строки символов и %% для самого %.


Продолжение в следующем посте...


...


Последний раз редактировалось: Viktor2312 (Ср Сен 22 2021, 17:03), всего редактировалось 6 раз(а)

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Вс Янв 26 2020, 12:08

4
Переменные, типы данных и арифметические выражения.


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


Работа с переменными.

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

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

В наших предыдущих примерах, были использованы несколько переменных, для хранения целочисленных значений. Например, была использована переменная sum для хранения суммы двух целых чисел 50 и 25. В языке программирования, как мы уже убедились, разрешены и другие типы данных, которые необходимо присвоить переменным до того, как они будут использованы в программе. Переменные могут обозначать числа с плавающей точкой, символы и даже указатели на определённые ячейки памяти.

Правила для написания имён переменных очень простые. Имя должно начинаться с буквы или с символа подчёркивания (_), за которыми могут следовать любые комбинации букв в любом регистре, символы подчёркивания или цифры от 0 до 9. Ниже показан пример правильных имён:

sum, pieceFlag, i, J5x7, Number_of_moves, _sysflag, sign_1 и т. д.

Но следующие имена уже не будут являться правильными:

sum$value$ - включён недопустимый символ;

piece flag - пробел не разрешён;

4Spencer - имя переменной не должно начинаться с числа;

int - int является зарезервированным словом.

Слово int не может использоваться как имя переменной, поскольку оно имеет специальное назначение и компилятор связывает с ним определённые действия. Такие слова называются зарезервированными или ключевыми словами. Они распознаются компилятором Си как директивы и поэтому не могут использоваться в качестве имён переменных.

Необходимо всегда помнить, что в языке программирования Си различаются заглавные и строчные буквы. Поэтому имена переменных sum, Sum и SUM будут ссылаться на разные переменные. Длина имени может быть сколь угодно большой, но только первые 63 символа будут учитываться, или будут являться значимыми, а в некоторых специальных случаях, только первый набор из 31 символа будет значимым. Но обычно на практике не используются такие длинные имена, для себя можно даже выработать правило - не превышать 31 символ, поскольку их довольно неудобно набирать и при этом ухудшается зрительное восприятие программы.

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

Использование имён переменных, состоящих из нескольких слов, может улучшить читабельность программы. Слова в именах можно отделять символом подчёркивания, например total_commissions, или, если слова записываются слитно, каждое последующее слово, кроме первого, можно начинать с буквы верхнего регистра, например totalCommissions. Последний способ считается более предпочтительным.


Типы данных и константы.

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

В языке программирования Си используются ещё четыре базовых типа данных: float, doble, shar и _Bool. Переменные объявленные как тип float, могут использоваться для хранения вещественных чисел (чисел с плавающей точкой). Переменные типа doble также используются для хранения вещественных чисел, но удвоенной точности. Переменные типа shar могут использоваться для хранения отдельных символов, таких как буквы, цифры или точки с запятой. Наконец тип _Bool используется только для хранения двух значений: 0 или 1.

Переменные типа _Bool используются в тех ситуациях, когда требуется хранить только один из двух вариантов: да/нет, включено/выключено, правда/ложь. В языке программирования Си любое число, отдельный символ или строка символов считаются константами. Например, число 59 представляет целочисленное значение, которое является константой. Строка символов "Программирование на языке Си забавно\n" также является константой.

Выражение, состоящее исключительно из констант, является константным выражением. Поэтому выражение

256 + 8 - 16

является константным выражением, поскольку каждый элемент этого выражения является константой, но если будет присутствовать переменная, например, t_1, объявленная как тип int, то выражение

256 + 8 - t_1

уже не будет являться константным.


Базовый тип int.

В языке программирования Си целочисленная константа состоит из одной цифры или последовательности нескольких цифр. Предшествующий цифрам знак минус говорит о том, что значение является отрицательным. Значения 165, -12 и 0 являются примерами целочисленных констант. Никаких пробелов между цифрами не допускается и значения больше 999 не могут форматироваться с помощью запятой. Поэтому значение 15,000 не является правильной целочисленной константой и должно быть записано как 15000.

В языке Си для отображения целочисленных констант можно использовать не только базовое число 10 (десятичные числа). Если первое число является 0, то это значит, что данное число рассматривается как восьмеричное число (базовое число 8 ). В этом случае все остальные числа должны составлять допустимые восьмеричные числа, т. е. быть числами из диапазона чисел 0 - 7. Таким образом, для того, чтобы задать восьмеричное число 50, которое эквивалентно десятичному числу 40, необходимо написать 050.

Аналогичным образом, восьмеричное число 0177 равно десятичному числу 127 (1*64+7*8+7). Целочисленное значение может отображаться в восьмеричной нотации с помощью символа форматирования "%o", который ставится в строке форматирования в утверждении вывода. В этом случае отображается восьмеричное значение без предшествующего нуля. Символ форматирования "%#o" приводит к отображению восьмеричного значения с предшествующим нулём.

Если целочисленной константе предшествует нуль и символ X (независимо в каком регистре), то целочисленная константа считается шестнадцатеричным значением (базовое число 16). Символы непосредственно следующие за символом X, и составляют само число. Эти символы могут быть числами от 0 до 9, а также буквами от A до F (a, b, c, d, e, f или A, B, C, D, E, F). Буквы представляют значения от 10 до 15 соответственно. Поэтому для присваивания целочисленного шестнадцатеричного значения FFEF0D переменной rgbColor, необходимо написать следующее утверждение.

rgbColor = 0xFFEF0D;

Символ форматирования "%x" используется для отображения значений в шестнадцатеричном виде без предшествующих символов "0x" и букв a - f в нижнем регистре. Для отображения значения с предшествующими символами, необходимо использовать символ форматирования "%#x", как показано в следующем примере.

printf ("Color is %#x\n", rgbColor);

Для отображения символов в верхнем регистре, необходимо использовать символы форматирования "%X" и "%#X".


Хранение и диапазоны.

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

Обычно этот диапазон зависит от компьютера, на котором вы работаете, и поэтому реализация кода называется машинно-зависимой. Например, целочисленное значение может занимать 32 бита на вашем компьютере и 64 бита на другом компьютере. Вы никогда не должны писать программу, в которой учитываются размеры типов данных, хотя при этом гарантируется, что минимальное значение занимаемой памяти будет одинаковым для всех типов данных на всех компьютерах. Например, гарантируется, что целочисленное значение типа int сохраняется как минимум в 32 битах, что соответствует размеру слова (word) на большинстве компьютеров.


Вещественный тип float.

Переменные, объявленные как тип float, могут использоваться для хранения вещественных чисел, т. е. чисел с плавающей точкой. Именно для этих целей и введён тип float. Значения 3. или 125.8 и -.001 являются правильными примерами задания вещественных чисел. Для отображения вещественных чисел в процедуре printf используется символ форматирования "%f".

Вещественные константы могут также отображаться в научной нотации. Константа 1.7e+4 является вещественным числом, выраженным в научной нотации и представляющим значение 1.7*104.

Значение перед буквой e называется мантиссой тогда как значение после буквы e называется показателем степени. Показатель степени, который может быть со знаком плюс или минус, представляет число, в которое должно быть возведено число 10 и на которое должна быть умножена мантисса. Поэтому константа 2.25e-3 представляет собой значение мантиссы 2.25, умноженное на 10-3 (число -3 является показателем степени), т. е. число 0.00225.

Символ e, который разделяет мантиссу и показатель степени, может быть написан как в верхнем, так и в нижнем регистре.

Для отображения на экране вещественных чисел в научной нотации используется символ форматирования "%e". При этом значения могут отображаться как в виде чисел с плавающей запятой, так и в виде чисел в научной нотации. Если показатель степени меньше -4 или больше 5, то удобнее использовать научную нотацию, в противном случае надо использовать символ форматирования "%f".


Числа удвоенной точности double.

Числа удвоенной точности подобны числам типа float, но используются в тех случаях, когда недостаточно точности чисел типа float. Переменные, объявленные как тип double, могут сохранять вдвое больше цифр, чем переменная типа float. В большинстве компьютеров переменные типа double занимают 64 бита.

Если специально не оговорено, то все вещественные константы трактуются компилятором С как переменные типа double. Для того чтобы вещественную переменную объявить как тип float, необходимо добавить символ f или F.

12.5f

Для отображения переменной типа double на экране, используются символы форматирования "%f", "%e" или "%g", которые используются и при отображении вещественных чисел.


Символьный тип char.

Переменные типа char можно использовать для хранения отдельных символов. Символьные константы объявляются с помощью отдельных символов, заключённых в одиночные кавычки. Поэтому записи вида 'a', ';' и '0' являются примерами правильно заданных символьных констант. В первом случае задаётся буква a, во втором случае это точка с запятой и в третьем случае это число 0. Не путайте символьные константы, которые представляют один символ, со строкой символов, которая представляет один или несколько символов, заключённых в двойные кавычки.

Символьная константа '\n' (символ новой строки) является допустимым символом, хотя по внешнему виду не соответствует ранее приведённому правилу. Но это правильная запись, т. к. символ обратной черты '\' для компилятора языка Си не является действительно учитываемым символом, а имеет специальное назначение.

Другими словами, компилятор языка С трактует запись '\n' как один символ, хотя в действительности записано два символа. Но при этом наличие обратной черты заставляет компилятор языка Си по-другому воспринимать символ n.

Символ форматирования "%c" может использоваться для отображения значений символьных переменных на экране.


Булев тип _Bool.

Переменные типа _Bool предназначены для хранения только двух значений, 0 и 1. Размер памяти, выделяемый для хранения каждой переменной, не оговаривается. Переменные типа _Bool используются для хранения результатов булевых вычислений. Например, переменные такого типа можно использовать для указания состояния режима чтения из файла, т. е. закончено или нет чтение из файла.

По соглашению, 0 используется для указания состояния "ложь", а 1 используется для состояния "истина". При присваивании значения переменной типа _Bool, 0 записывается как 0, а любое значение, отличное от 0, записывается как единица.

Для более удобной работы с переменными типа _Bool, в стандартном заголовочном файле stdbool.h для этих переменных определены значения true и false.

Ниже показан листинг программы и результат её работы, где показано использование основных типов данных языка Си.

Изучаем язык программирования Си. Вариант-2. 00029__YAzyk_S.1580869451

Код для вставки в IDE:

Код:
#include <stdio.h>

int main (void)
{
 int integerVar = 100;
 float floatingVar = 331.79;
 double doubleVar = 8.45e+11;
 char charVar = 'Q';
 _Bool boolVar = 0;

 printf ("integerVar = %i\n", integerVar);
    printf ("floatingVar = %f\n", floatingVar);
 printf ("doubleVar = %e\n", doubleVar);
 printf ("doubleVar = %g\n", doubleVar);
 printf ("charVar = %c\n", charVar);
 printf ("boolVar = %i\n", boolVar);
 printf ("\n\n\n\n\n");

 return (0);
}

Результат работы программы:

Изучаем язык программирования Си. Вариант-2. 00030__YAzyk_S.1580869758

В первом утверждении в листинге выше объявлена целочисленная переменная integerVar (о чём говорит ключевое слово int), и ей присвоено значение 100. Это же самое можно сделать и с помощью двух следующих утверждений.

int integerVar;
integerVar = 100;

Обратите внимание, что во второй строке, выведенной программой на экран, значение 331.79, которое было присвоено переменной floatingVar, отображается как 331.790009. На самом деле, действительное значение, которое отображается, зависит от особенностей системы, которая используется для вычислений. Причина такой "неаккуратности" кроется в особенностях хранения чисел в компьютере. Некоторые вещественные числа не могут быть точно сохранены в памяти.

Для отображения переменных типа float и double можно выбрать один из трёх форматов. Символ форматирования "%f" используется для вывода значения в наиболее привычном виде.

Если специально не оговорено, то вещественные числа отображаются округлёнными с шестью цифрами после точки.

Символ форматирования "%e" используется для отображения значений вещественных чисел в научной нотации. Как и ранее, только шесть цифр по умолчанию отображаются на экране.

Если используется символ форматирования "%g", то производится выбор между форматами "%f" и "%e" и при этом удаляются все последующие нули. Если нет цифр после запятой, то дробная часть не отображается совсем.

В предпоследнем утверждении вывода, символ форматирования "%c" используется для отображения одного символа'Q', который был присвоен переменной charVar при объявлении переменной. Поскольку любая символьная строка (такая как первый аргумент процедуры printf) выделяется двойными кавычками, то и символьная константа должна заключаться в двойные кавычки.

В последней процедуре printf показан вывод переменной типа _Bool, значение которой отображается с помощью символа форматирования для целых чисел "%i".
`

Спецификаторы типов: long, long long, short, unsigned, signed.


Если при объявлении переменной спецификатор типа помещён непосредственно перед ключевым словом int, то на некоторых компьютерах объявленная переменная будет иметь расширенный диапазон значений. Объявление для расширенного диапазона чисел можно сделать следующим образом.

long int factorial;

Такое объявление переменной factorial задаёт целочисленную переменную с расширенным диапазоном. Но как и при объявлении переменных типа float и double, действительный диапазон переменных зависит от используемого типа компьютера. На многих компьютерах типы int и long int имеют один и тот же диапазон и для их хранения выделяется 32 бита (231-1 или 2,147,483,647).

При объявлении типа long int к задаваемому значению необходимо добавить букву L (в верхнем или нижнем регистре). При этом недопустимы пробелы между цифрами и буквой. Поэтому следующее объявление:

long int numberOfPoints = 131071100L;

создаст переменную numberOfPoints и присвоит ей начальное значение 131,071,100.

Для отображения переменной типа long int с помощью процедуры printf, в качестве модификатора используется буква l, которая размещается перед символами форматирования i, o и x. Таким образом, символ форматирования "%li" необходимо использовать для отображения типа long int в десятичном формате. Символ форматирования "%lo" используется для отображения переменных с расширенным диапазоном значений в восьмеричном формате, и символ форматирования "%lx" - для отображения в шестнадцатеричном формате.

Также можно объявить тип long long int, при этом объявление переменной как:

long long int maxAllStorage;

создаст переменную для хранения расширенного диапазона значений, при котором будет использоваться по крайней мере 64 бита. При выводе значений типа long long int в символе форматирования необходимо использовать две буквы l, например "%lli".

Спецификатор long можно использовать и при объявлении переменных типа double, как показано ниже:

long double Nomber_A_001;

Константа типа long double записывается как константа типа float с добавленной буквой l или L сразу после цифр.

1.235e+8L

Для отображения переменных типа long double, используется модификатор L. Поэтому символ форматирования для вещественных переменных с расширенным диапазоном значений будет иметь вид "%Lf". Если использовать символ форматирования "%Le", то эта же переменная будет отображена в научной нотации, а при использовании символа форматирования "%Lg" автоматически будет сделан выбор между выводом в формате "%Lf" или в формате "%Le".

Спецификатор short, будучи помещён перед ключевым словом int, указывает компилятору Си на то, что данная переменная объявлена для хранения небольших целочисленных значений. Причиной использования таких переменных может быть желание сэкономить память, когда в некоторых ситуациях используется значительное число переменных с небольшими значениями, но при этом ограничен ресурс памяти.

В некоторых компьютерах размер типа short int будет занимать в два раза меньше битов, чем для типа int. Но в любом случае гарантируется, что количество выделяемых битов для переменных типа short int будет не меньше 16 бит.

Для задания переменных типа short int в языке Си нет специального формата. Для отображения переменных типа short int используется модификатор h, который помещается перед символом форматирования: "%hi", "%ho" и "%hx". Но можно использовать и обычное форматирование для целочисленных переменных при отображении типа short int, при этом формат short int будет преобразован в формат int.

Спецификатор unsigned помещается перед ключевым словом int при объявлении переменных целочисленного типа и указывает компьютеру на то, что задаётся положительное число. Это число обычно используется как счётчик и не может иметь отрицательное значение.

Используя целочисленные переменные типа unsigned для хранения положительных чисел, можно расширить диапазон используемых значений. Константа типа unsigned int задаётся с помощью буквы u или U, которая помещается после цифр, как показано ниже:

0x00ffU

Можно комбинировать буквы u (или U) и l (или L) при задании целочисленных констант, при этом запись:

20000UL

указывает компилятору на то, что константа 20000 имеет тип unsigned long.

Целочисленная константа, за которой не следуют буквы u, U, l или L, но которая превышает допустимый диапазон для типа int расширяется до диапазона unsigned int. Если значение константы превышает и этот диапазон, то компилятор рассматривает её как тип long int. Если и этого диапазона не достаточно для использования этого значения, то предпринимается попытка использовать тип unsigned long int. Если недостаточно и этого типа, то берётся диапазон, связанный с типом long long int, и наконец используется тип unsigned long long int

Когда объявляются переменные типа long long int, long int, short int или unsigned long, то можно пропустить ключевое слово int. Поэтому целочисленную переменную без знака counter можно объявить так:

unsigned counter;

Переменные типа char тоже можно объявлять как беззнаковые.

Квалификатор signed используется для дополнительного указания компилятору на то, что задаваемая переменная может принимать значения как со знаком, так и без знака. Такие квалификаторы в основном используются при объявлении переменных типа char, более подробно это будет рассмотрено позже.

На данный момент произведено поверхностное описание типов данных, но постепенно, на практических примерах, этот вопрос будет изучен детально. Ниже представлена таблица основных типов данных:

Изучаем язык программирования Си. Вариант-2. 00031__YAzyk_S.1581243707


Продолжение в следующем посте...


...


Последний раз редактировалось: Viktor2312 (Ср Ноя 03 2021, 02:27), всего редактировалось 9 раз(а)

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Пн Янв 27 2020, 10:58

5
Блин, работает, но...

Изучаем язык программирования Си. Вариант-2. 76767734_00.1581955124


Вечно всё идёт не так, как задумывалось...


Вид программ на Си.

Таблица содержит 32 ключевых слова, образующих синтаксис языка Си. Также показано 12 расширенных ключевых слов, добавленных фирмой Borland, которые могут включаться в Си-программы. Конечно, использование расширенных ключевых слов приводит к тому, что программа становится непереносимой.

Все ключевые слова Си записываются строчными буквами. В Си прописные и строчные буквы различаются: else — ключевое слово, a ELSE — нет. Ключевое слово не может быть использовано в каких-либо других целях в Си-программах. Таким образом, оно не может служить в качестве имени переменной или функции.

Все Си-программы содержат одну или более функций. Единственная функция, которая всегда должна присутствовать, называется main(), и она является первой функцией, получающей управление. В хорошо написанных Си-программах main() выделяет суть работы программы. Она содержит вызовы функций. Хотя main() не является частью языка, но, тем не менее, она трактуется как часть. Не следует пытаться использовать main как, например, имя переменной.

Таблица. Список ключевых слов языка программирования Си.
Изучаем язык программирования Си. Вариант-2. 9ed_0050

Типичный вид Си-программы показан ниже, где функции от f1() до fN() являются функциями пользователя.

Общая форма программы на Си.
Изучаем язык программирования Си. Вариант-2. 9ed_0051


Библиотеки и компоновка.

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

Язык Си определяет стандартные библиотеки, предоставляющие функции, выполняющие наиболее типичные задачи. При вызове функции, которая не является частью написанной программы, компилятор запоминает её имя. Позже компоновщик объединяет код, написанный программистом, с объектным кодом, уже находящимся в стандартных библиотеках. Этот процесс называется компоновкой.

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


Раздельная компиляция.

Большинство коротких Си-программ умещаются в одном исходном файле. По мере того, как программа растёт, на её компиляцию требуется всё больше времени, а это раздражает. Поэтому Си позволяет разбивать программы на куски, содержащиеся в нескольких файлах, и каждый файл может компилироваться отдельно. Файлы, будучи откомпилированными, подсоединяются вместе с подпрограммами библиотек для создания полного объектного кода программы. Преимущество раздельной компиляции заключается в том, что при изменении одного файла не требуется перекомпиляция всей программы.


Карта памяти Си-программы.

Откомпилированная Си-программа создаёт и использует 4 логически разделённых области памяти, имеющих своё назначение. Первая область — это память, содержащая код программы. Следующая область предназначена для хранения глобальных переменных. Оставшиеся две —это стек и куча. Стек используется для самых различных целей при выполнении программы. Он содержит адреса возвратов вызываемых функций, аргументы, передаваемые в функции, и локальные переменные. Он также используется для хранения текущего состояния процессора. Куча - это область свободной памяти, которую программа может использовать для динамического выделения памяти под такие объекты, как списки и деревья.

Хотя точное физическое местоположение каждой области памяти зависит от способа компиляции программы, диаграмма ниже, показывает, как в общих чертах Си-программа располагается в памяти.

Концептуальная карта памяти программы на Си:

Изучаем язык программирования Си. Вариант-2. 9ed_0052


Продолжение в следующем посте...


...


Последний раз редактировалось: Viktor2312 (Ср Сен 22 2021, 16:59), всего редактировалось 6 раз(а)

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Сб Фев 01 2020, 16:08

6
Пометки, заметки для себя. Кому нужно читайте, стырено где-то в инете...

Директива #define определяет идентификатор и последовательность символов, которой будет за­мещаться данный идентификатор при его обнаружении в тексте программы. Идентификатор так­же называется именем макроса, а процесс замещения называется подстановкой макроса. Стандар­тный вид директивы следующий:

#define имя_макроса последовательность_символов

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

Например, если необходимо использовать TRUE для значения 1, a FALSE для 0 то можно объявить следующие два макроса:

#define TRUE 1
#define FALSE 0

В результате, если компилятор обнаружит в тексте программы TRUE или FALSE, то он заменит их на 1 и 0 соответственно. Например, следующая строка выводит на экран «0 1 2»:

printf ("%d %d %d", FALSE, TRUE, TRUE + 1);

В случае, если макрос определен, он может использоваться для определения других макросов. Например, следующий код сопоставляет с именами ONE, TWO и THREE их численные значения:

#define ONE 1
#define TWO ONE + ONE
#def ine THREE ONE + TWO

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

#define E_MS "Standart error on input.\n"
/*...*/
printf(E_MS);

Если компилятор обнаруживает идентификатор E_MS, то он замещает его строкой «Standart error on input.» На самом деле компилятор увидит оператор в виде

printf("Standart error on input.\n");

Если идентификатор находится в строке, то подстановка не происходит. Например:

#define XYZ this is a test
/*...*/
printf("XYZ");

выведет не «this is a test», a «XYZ».

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

#define LONG_STRING "This is a very long" \
string that is used as an example."

Программисты, пишущие на С, часто используют заглавные буквы для определения идентифика­торов. Данное соглашение помогает любому человеку, читающему программу, бросив на нее один взгляд, узнать, что он имеет дело с макросом. Также вce #define лучше помещать в начале файла или вообще в отдельный заголовочный файл.

Очень часто макросы используют для определения «магических чисел», используемых в про­грамме. Например, программа может определять массив и иметь несколько процедур для работы с ним. Вместо того, чтобы жестко кодировать размер массива, лучше определить макрос, соответ­ствующий размеру массива, и использовать его в тех местах, где необходимо использование раз­мера. Таким образом, если необходимо изменить размер массива, единственное, что требуется сделать, — это изменить оператор #define и перекомпилировать программу. Везде, где использо­вался данный макрос, произойдут автоматические изменения. Рассмотрим пример:

#define MAX_SIZE 100
/*...*/
float balance[MAX_SIZE];
/*...*/
float temp[MAX_SIZE];

Для изменения размеров обоих массивов просто изменим определение MAX_SIZE.

Директива #define имеет еще одну возможность: макрос может иметь аргументы. Каждый раз при встрече такого макроса аргументы макроса будут замещаться реальными аргументами про­граммы. Такой тип макроса называется макрос типа функция. Например:

#include <stdio.h>
#define MIN(a,b) ((a)<(b)) ? (a) : (b)
int main(void)
{
int x, y;
x = 10; у = 20;
printf("The minimum is: %d", MIN(x, y) );
return 0;
}

При компиляции программы вместо MIN(a, b) подставляется указанное выражение, причем вме­сто фиктивных параметров а и b подставляются реальные х и у. Таким образом, в результате подстановки функция printf() примет следующий вид:

printf ("The minimum is: %d",((x) < (y) ) ? (x) : (y) );

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

/* программа выдает неправильный результат */
#include <stdio.h>
#define EVEN(a) a%2==0 ? 1 : 0
int main(void)
{
if (EVEN(9+1) ) printf("is even");
else printf ("is odd");
return 0;
}

Из-за способа подстановки данная программа работает неправильно. В результате компиляции программы EVEN(9 + 1) расширится до

9 + 1% 2 == 0 ? 1 : 0

Как известно, оператор взятия по модулю имеет более высокий приоритет, чем оператор сло­жения. Это означает, что сначала выполнится взятие по модулю с числом 1, а затем результат прибавится к 9, что, естественно, не может быть равно 0. Для устранения данной проблемы сле­дует заключить а в макросе EVEN в круглые скобки, как показано в следующей правильной вер­сии программы:

#include <stdio.h>
#define EVEN(a) (a)%2==0 ? 1 : 0
int main(void)
{
if(EVEN(9 + 1) ) printf("is even");
else printf("is odd");
return 0;
}

Обратим внимание, что 9+1 вычисляется до взятия по модулю. В целом заключение параметров макроса в скобки — это достаточно хорошая идея, и она позволяет избежать множества проблем.
Использование макроподстановок вместо реальных функций имеет одно большое преимуще­ство — существенно увеличивается скорость работы программы, поскольку нет необходимости тратить время на вызов функции и возврат из нее. Тем не менее, за данное увеличение скорости работы следует платить увеличением размера исполнимого кода программы, поскольку програм­ма вынуждена дублировать код макроса.


Продолжение в следующем посте...


...


Последний раз редактировалось: Viktor2312 (Ср Сен 22 2021, 16:58), всего редактировалось 4 раз(а)

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Ср Фев 05 2020, 05:42

7
Замечания о мерах безопасности при программировании на языке Си.


Как показывает опыт, очень сложно писать промышленные системы, устойчивые к вирусам, "червям" и другим видам нападений. В настоящее время подобные нападения из Интернета могут происходить мгновенно и носить глобальный характер. Однако уязвимостей в программном обеспечении легко можно избежать. Если заниматься проблемами безопасности с самого начала разработки, это может значительно уменьшить трудозатраты на обеспечение безопасности и повысить устойчивость программного продукта к нападениям.

Для анализа нападений и противодействия им, был создан координационный центр CERT www.cert.org или https://www.sei.cmu.edu/about/divisions/cert/index.cfm

Изучаем язык программирования Си. Вариант-2. 444_e814

Группа CERT - Computer Emergency Response Team (группа реагирования на нарушения компьютерной безопасности) - издаёт и пропагандирует стандарты безопасного программирования, помогая программистам, использующим Си и другие языки для создания промышленных систем, избежать применения приёмов, открывающих системы для нападений. Группа CERT продолжает выпускать новые стандарты по мере выявления новых проблем безопасности.

Если вам предстоит заниматься созданием промышленных систем на языке Си, обязательно прочитайте книги Роберта Сикорда (Robert Seacord) "The CERT C Secure Coding Standatd" (Addison-Wesley Professional, 2009) и "Secure Coding in C and C++" (Addison-Wesley Professional, 2013). Руководства CERT доступны в электронном виде https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard. Роберт Сикорд руководит отделом безопасного программирования в группе CERT института разработки программного обеспечения (Software Engineering Institute, SEI) университета Карнеги-Меллона (Carnegie Mellon University) и занимает пост адъюнктпрофессора в школе информатики университета Карнеги-Меллона.

В понятие безопасное программирование входят многие понятия, такие как: тестирование арифметических операций на переполнение; использование беззнаковых целочисленных типов; новые безопасные функции, определяемые стандартом Annex K; важность проверки информации, возвращаемой функциями из стандартной библиотеки; проверка диапазона; безопасное воспроизведение последовательности случайных чисел; проверка границ массивов; приёмы предотвращения переполнения буфера; проверка ввода; исключение неопределённостей в поведении; предпочтение функций, возвращающих информацию о состоянии, перед аналогичными функциями, не возвращающими таких данных; гарантия сохранения в указателях допустимых адресов или значения NULL; предпочтение функций Си перед макросами препроцессора и многие другие.


Продолжение в следующем посте...


...


Последний раз редактировалось: Viktor2312 (Ср Сен 22 2021, 16:56), всего редактировалось 3 раз(а)

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Пн Фев 17 2020, 19:02

8
Арифметические операции в языке Си.


Большинство программ на языке Си выполняют арифметические вычисления, используя для этого арифметические операторы.

Сложение +

Вычитание -

Умножение *

Деление /

Остаток от деления %

Звёздочка (*) обозначает операцию умножения, знак процента (%) - оператор получения остатка от деления. В алгебре умножение a на b записывается просто как ab. Однако если то же самое сделать в программе на Си, последовательность символов ab будет интерпретироваться как двухбуквенное имя переменной (или идентификатор). То есть операция умножения должна обозначаться явно, с помощью оператора *, например a * b. Все арифметические операторы являются двухместными. Например, выражение 3 + 7 содержит двухместный оператор + и два операнда 3 и 7.

Изучаем язык программирования Си. Вариант-2. 444_e815


Целочисленное деление и оператор остатка от деления.

При делении целых чисел возвращается целое число. Например, выражение 7 / 4 вернёт 1, а выражение 17 / 5 вернёт 3. В языке Си имеется оператор остатка от деления %, который возвращает остаток от целочисленного деления. Оператор остатка - это целочисленный оператор, который можно использовать только с целочисленными операндами. Выражение x % y вернёт остаток от деления x на y. То есть 7 % 4 вернёт 3, а 17 % 5 вернёт 2.

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

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

Изучаем язык программирования Си. Вариант-2. 444_e816

Код для вставки в редактор:
Код:
#include <stdio.h>

int main (void)
{
int a = 5;
int b = 2;
int c;
int d;

c = 5 / 2;
d = 5 % 2;

   printf("\n\nc = 5 / 2 = %i\n\n", c);
   printf("d = 5 mod 2 = %i\n\n", d);
   printf("\n\n\n\n\n\n");

return(0);
}

И результат работы:

Изучаем язык программирования Си. Вариант-2. 444_e817

Думаю комментарии излишни, всё итак понятно.


Линейная форма записи арифметических выражений.

Арифметические выражения в языке Си должны записываться в линейной форме, чтобы упростить её выполнение компьютером. То есть выражения, такие как "a разделить на b", должны записываться как:

a / b,

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


Группировка подвыражений с помощью круглых скобок.

Круглые скобки используются в выражениях на языке Си с той же целью, что и в алгебраических выражениях. Например, чтобы умножить a на b + c, можно записать выражение a * (b + c).


Правила предшествования операторов.

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

  1. Сначала выполняются операторы в выражениях, заключённых в круглые скобки. Круглые скобки имеют "наивысший приоритет". В случае наличия в выражении вложенных скобок, как, например, в выражении:

    ((a + b) + c)

    первым вычисляется оператор в самых внутренних скобках.

  2. Далее выполняются операторы умножения, деления и остатка от деления. Если выражение содержит несколько операторов умножения, деления или остатка от деления, они вычисляются в порядке слева направо. Операторы умножения, деления или остатка от деления имеют одинаковый приоритет.

  3. Далее вычисляются операторы сложения и вычитания. Если выражение содержит несколько операторов сложения и вычитания, они вычисляются в порядке слева на право. Операторы сложения и вычитания также имеют одинаковый приоритет, который ниже приоритета операторов умножения, деления и остатка от деления.

  4. Последним выполняется оператор присваивания (=).

Изучаем язык программирования Си. Вариант-2. 444_e820

Правила предшествования определяют порядок вычисления выражений в языке Си. Для описания порядка вычисления операторов выше использовались простые выражения. Однако в более сложных выражениях возникают некоторые малозаметные проблемы. Когда мы говорим о порядке выполнения слева на право, мы подразумеваем ассоциативность операторов. Но есть несколько операторов, имеющих ассоциативность справа на лево.


Примеры выражений, алгебраических и на языке Си.

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

Изучаем язык программирования Си. Вариант-2. 444_e821

Круглые скобки, окружающие группу операторов сложения, здесь совершенно необходимы, потому что деление имеет более высокий приоритет, чем сложение. В данном случае делиться на 5 должно всё выражение (a + b + c + d + e). Если скобки опустить, мы получим выражение a + b + c + d + e / 5, дающее совсем другой результат:

Изучаем язык программирования Си. Вариант-2. 444_e822

Следующее выражение записывается линейно:

Изучаем язык программирования Си. Вариант-2. 444_e823

Здесь круглые скобки не нужны. Первым будет выполнен оператор умножения как имеющий более высокий приоритет перед оператором сложения.

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

Изучаем язык программирования Си. Вариант-2. 444_e824

Цифры в кружочках указывают очерёдность вычисления операторов в языке Си. Операторы умножения, остатка от деления и деления выполняются первыми, в порядке слева на право (то есть все они являются ассоциативными слева на право), потому что они имеют более высокий приоритет, чем операторы сложения и вычитания. Они также выполняются в порядке слева на право. Последним выполняется оператор присваивания, сохраняющий результат в переменной z.

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

Изучаем язык программирования Си. Вариант-2. 444_e825


Вычисление значения полинома второй степени.

Чтобы полнее усвоить правила предшествования операторов, рассмотрим пример вычисления значения полинома второй степени на языке Си.

Изучаем язык программирования Си. Вариант-2. 444_e826

Пояснение:

Полином — это степенная функция y = ax2 + bx + c (полином второй степени) и y = ax3 + bx2 + cx + d (полином третей степени) и т. д. Степень полинома определяет количество экстремумов (пиков).

Цифры в кружочках под инструкцией указывают очерёдность вычисления операторов. В языке Си нет оператора возведения в степень, поэтому член полинома х2 здесь представлен выражением x * x. Стандартная библиотека включает в себя функцию pow ("power" - "степень") возведения в степень, но из-за некоторых тонкостей, связанных с типами данных, которые должны передаваться функции pow отложим это на потом...

Допустим, что переменные a, b, c и x в предыдущем выражении инициализированы следующими значениями: a = 2, b = 3, c = 7 и x = 5. На рисунке показано, в каком порядке будут вычисляться операторы:

Изучаем язык программирования Си. Вариант-2. 444_e827

Как и в алгебре, в языке Си допускается вставлять необязательные круглые скобки, чтобы упростить чтение выражения. Такие скобки называют избыточными. Например, в предыдущую инструкцию можно было бы включить круглые скобки, как показано ниже:

Изучаем язык программирования Си. Вариант-2. 444_e828


Продолжение в следующем посте...


...


Последний раз редактировалось: Viktor2312 (Пт Сен 17 2021, 10:17), всего редактировалось 5 раз(а)

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Ср Сен 22 2021, 17:23

9
Дополнение. Повторение.


Переменные, константы, операторы и выражения.

Операторы манипулируют переменными и константами и тем самым образуют выражения. Все они - базовые элементы языка Cи.


Идентификаторы.

Имена, использующиеся для переменных, функций, меток и других определяемых пользователем объектов, называются идентификаторами. Идентификаторы могут состоять как из одного, так и из нескольких символов. Тем не менее в Си только первые 31 символ являются значимыми. Первым символом должна быть буква или знак подчеркивания, а за ним могут стоять буквы, числа или знак подчеркивания. Ниже приведено несколько примеров корректных и некорректных имен идентификаторов.

Изучаем язык программирования Си. Вариант-2. 9ed_0053

В Си прописные и строчные буквы трактуются по-разному. Следовательно, count, Count и COUNT — это три различных идентификатора. Идентификатор не может совпадать с ключевым словом Си и не должен иметь такое же имя, как функция, уже содержащаяся в библиотеке Си.


Типы данных.

Имеется 5 базовых типов данных в Си: символьный, целочисленный, вещественный с одинарной точностью, вещественный с двойной точностью и void. Значения типа char используются для хранения ASCII-кодов или любых 8-битных величин. Переменные типа int используются для хранения целых величин. Переменные типа float и double содержат вещественные числа. (Вещественные числа имеют как целую, так и дробную часть.)

Тип void имеет три назначения. Первое — указание о невозвращении значения функцией. Второе — указание о неполучении параметров функцией. Третье — создание нетипизированных указателей.

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


Модификаторы типов.

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

signed
unsigned
short

Модификаторы signed, unsigned, long и short могут применяться к целочисленным типам. К символам можно применять signed и unsigned, long может применяться к типу double. Таблица показывает все допустимые комбинации стандарта ANSI С для 16-битных типов данных вместе с размером типа в битах и границами применения в Borland С++.

Все допустимые комбинации базовых типов и модификаторов для 16-битных слов:

Изучаем язык программирования Си. Вариант-2. 9ed_0054

Следующая таблица  содержит информацию о 32-битных типах данных. Все допустимые комбинации базовых типов и модификаторов для 32-битных слов:

Изучаем язык программирования Си. Вариант-2. 9ed_0055

Использование signed для целочисленных типов является избыточным (но допустимым), поскольку объявление целочисленных типов по умолчанию предполагает знаковое число.

Различие между знаковыми и беззнаковыми целочисленными типами заключается в способе интерпретации старшего бита. Если используется знаковый тип, то компилятор генерирует код, предполагающий, что старший бит используется как знак числа. Если знаковый бит равен 0, то число положительное, а если 1 - отрицательное. Ниже приведены простые примеры:

Изучаем язык программирования Си. Вариант-2. 9ed_0056

Следует предупредить, что фактически все компьютеры используют дополнительную арифметику, что приводит к представлению —127 в виде, слегка отличном от вышеприведенного примера. Тем не менее, использование знакового бита является однотипным. У отрицательного числа в дополнительном виде все биты инвертированы, и к числу добавляется 1. Например, —127 в дополнительном виде выглядит следующим образом:

1111111110000001

Знаковые числа важны для многих алгоритмов, но они могут вмещать только половину значений, которые могут содержать их беззнаковые «братья». Например, 32767 выглядит следующим образом:

01111111 11111111

Если старший бит установлен в 1, то число будет интерпретироваться как —1. Тем не менее, если объявить его как unsigned int, то, если старший бит установлен, число будет содержать 65535.


Модификаторы доступа.

Си имеет 2 типа модификаторов, которые используются для контроля за способом доступа или модификации переменных. Эти модификаторы называются const и volatile.

Переменные типа const не могут изменяться во время выполнения программы. Например:

const int а;

создаст целочисленную переменную, называемую а, которая не может быть модифицирована в программе. Она может использоваться в других типах выражений. Переменная с модификатором const получает свое значение или при инициализации, или каким-либо аппаратно-зависимым способом. Например, нижеприведённая строка присваивает count значение 100:

const int count = 100;

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

Модификатор volatile используется для сообщения компилятору о возможности изменения значения способами, не определёнными в программе. Например, адрес глобальной переменной может быть передан в подпрограмму часов операционной системы и использован для хранения времени системы. В данной ситуации содержимое переменной изменяется без использования оператора присваивания в программе. Это важно, поскольку компилятор автоматически оптимизирует некоторые выражения, делая предположения, что содержимое переменных не изменяется в выражениях. Также некоторые виды оптимизации могут изменять порядок вычисления выражений во время процесса компиляции. Модификатор volatile предотвращает возникновение данных изменений.

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

const volatile unsigned char *port = 0x30;


Объявление переменных.

Все переменные должны объявляться перед использованием. Ниже показана стандартная форма объявления:

тип список_переменных

Здесь тип должен быть корректным типом данных Си, а список переменных может содержать одно или более имён идентификаторов, разделённых запятыми. Ниже показаны некоторые виды объявлений:

int i, j, l;

short int si;

unsigned int ui;

double balance, profit, loss;

Помните, что в Си имя переменной ничего не делает со своим типом.

Имеется три основных места, где объявляются переменные: внутри функций, при определении параметров функции и вне функций. Эти переменные называются соответственно локальными переменными, формальными параметрами и глобальными переменными.


Локальные переменные.

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

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

Наиболее типичным блоком кода, в котором объявляются локальные переменные, является функция. Например, рассмотрим две функции:

void func1 (void){
int x;
x= 10;
}

void func2(void){
int x;
x = -199;
}

Целочисленная переменная x объявляется дважды: один раз в func1() и другой раз в func2(). х в func1() не имеет отношения к х в func2(), поскольку каждая х известна только в блоке, где произошло объявление переменной.

Язык Си содержит ключевое слово auto, которое можно использовать для объявления локальных переменных. Тем не менее, поскольку предполагается, что все не глобальные переменные по умолчанию созданы с ключевым словом auto, то оно на самом деле никогда не используется.

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

void f(void){
int t;
scanf ("%d", &t);
if (t==1) {
char s[80]; /* s существует только в данном блоке */
printf("введите имя:");
gets (s);
process (s);
}
/* s здесь неизвестна */
}

Здесь локальная переменная s известна только в блоке кода if. Поскольку s известна только в блоке кода if, то к ней не может быть осуществлен доступ откуда-либо ещё — даже из других частей функции.

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

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

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


Формальные параметры.

Если функция использует аргументы, то в ней должны объявляться переменные, которые будут принимать значения аргументов. Данные переменные называются формальными параметрами функции. Они ведут себя, как любые другие локальные переменные в функции. Как показано в следующем фрагменте программы, они объявляются в круглых скобках, следующих за именем функции.

/* возвращает 1, если с является частью строки s; в противном случае - 0 */

int is_in (char *s, char c) {

while(*s)

if(*s==c) return 1;
else s++;

return 0;
}

Функция is_in() имеет два параметра: s и с. Необходимо сообщить компилятору тип этих переменных, как показано выше. Когда это сделано, они могут использоваться в функции как обычные локальные переменные. Надо помнить, что локальные переменные также являются динамическими и уничтожаются при выходе из функции.

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


Глобальные переменные.

В противоположность локальным переменным глобальные переменные видны всей программе и могут использоваться любым участком кода. Они хранят свои значения на протяжении всей работы программы. Глобальные переменные создаются путём объявления вне функции. К ним можно получить доступ в любом выражении, независимо от того, в какой функции находится данное выражение.

В следующей программе можно увидеть, что переменная count объявлена вне функций. Она объявляется перед функцией main(). Тем не менее, она может быть помещена в любое место до первого использования, но не внутри функции. Общепринятым является объявление глобальных переменных в начале программы.

#include <stdio.h>

void func1(void) , func2(void);

int count; /* count является глобальной переменной */

int main(void)
{
count = 100;
func1 ();
return 0; /* сообщение об удачном завершении работы */
}

void func1 (void)
{
func2 ();
printf("счетчик %d", count); /* выведет 100 */
}

void func2(void)
{
int count;
for(count=1; count<10; count++)
putchar('  ');
}

Рассмотрим поближе данный фрагмент программы. Следует понимать, что хотя ни main(), ни func1() не объявляют переменную count, но они оба могут её использовать. func2() объявляет локальную переменную count. Когда func2() обращается к count, она обращается только к локальной переменной, а не к глобальной. Надо помнить, что если глобальная и локальная переменные имеют одно и то же имя, все ссылки на имя внутри функции, где объявлена локальная переменная, будут относиться к локальной переменной и не будут иметь никакого влияния на глобальную, это очень удобно. Если забыть об этом, то может показаться, что программа работает странно, даже если всё выглядит корректно.

Глобальные переменные хранятся в фиксированной области памяти, устанавливаемой компилятором. Глобальные переменные чрезвычайно полезны, когда одни и те же данные используются в нескольких функциях программы. Следует избегать ненужного использования глобальных переменных по трём причинам:

  1. Они используют память в течение всего времени работы программы, а не тогда, когда они необходимы.

  2. Использование глобальных переменных вместо локальных приводит к тому, что функции становятся более частными, поскольку они зависят от переменных, определяемых снаружи.

  3. Использование большого числа глобальных переменных может вызвать ошибки в программе из-за неизвестных и нежелательных эффектов.

Одним из краеугольных камней структурных языков является разделение кода и данных. В Си разделение достигается благодаря использованию локальных переменных и функций. Например, ниже показаны два способа написания mul() — простой функции, вычисляющей произведение двух целых чисел.

Два способа написания mul( )

Изучаем язык программирования Си. Вариант-2. 9ed_0057

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


Продолжение в следующем посте...


...

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Ср Сен 22 2021, 17:30

10
Дополнение. Повторение.


Спецификаторы хранения.

Имеется четыре спецификатора хранения, поддерживаемые в Си. Это extern static register auto
Они говорят компилятору, как должны храниться переменные. Спецификаторы предшествуют объявлению переменной. В общем случае это выглядит так:

спецификатор_храпения тип имя_переменной;

Теперь рассмотрим спецификатор extern.


extern

Поскольку Си позволяет выполнять раздельную компиляцию модулей для большой программы в целях ускорения компиляции и помощи управлению большими проектами, должны быть способы передачи информации о глобальных переменных файлам программы. Решение заключается в объявлении всех глобальных переменных в одном файле и использовании при объявлении в других файлах слова extern, как показано в таблице.
Таблица. Использование глобальных переменных в раздельно компилируемых файлах:

Изучаем язык программирования Си. Вариант-2. 9ed_0058

В файле 2 список глобальных переменных копируется из файла 1 и при объявлении добавляется спецификатор extern. Спецификатор extern сообщает компилятору, что следующие за ним типы и имена переменных объявляются где-то в другом месте. Другими словами, extern позволяет компилятору знать о типах и именах глобальных переменных без действительного создания этих переменных. Когда два модуля объединяются, все ссылки на внешние переменные пересматриваются.

Если при объявлении выделяется память под переменную, то процесс называется определением. Использование extern приводит к объявлению, но не к определению. Оно просто говорит компилятору, что определение происходит где-то в другом месте программы.

Имеется другой вариант использования extern. Когда используется глобальная переменная внутри функции, находящейся в том же файле, где происходит объявление глобальной переменной, то можно объявлять её как extern, хотя делать это не обязательно. Следующий фрагмент программы демонстрирует, как использовать данный вариант.

int first, last; /*глобальное определение first и last */
int main (void)
{
extern int first; /* необязательное использование extern объявления */
......
}

Хотя объявление переменной с extern может иметь место в одном файле с объявлением глобальной переменной, в этом на самом деле нет необходимости. Если компилятор Си встречает переменную, которая не была объявлена, то компилятор проверяет, соответствует ли она какой-либо глобальной переменной. Если это так, то компилятор предполагает, что эта переменная ссылается на глобальную.


Статические переменные.

Статические переменные являются долговременными переменными, существующими на протяжении функции или файла. Они отличаются от глобальных переменных, поскольку не известны за пределами функции или файла, но могут хранить свои значения между вызовами. Данная возможность оказывается очень полезной тогда, когда необходимо написать универсальные функции и библиотеки функций, которые могут использоваться программистами. Поскольку эффект использования static на локальных переменных отличается от эффекта на глобальных переменных, то мы рассмотрим их по отдельности.


Статические локальные переменные.

Когда static применяется к локальной переменной, это приводит к тому, что компилятор создаёт долговременную область для хранения переменной почти таким же способом, как это делается для глобальной переменной. Ключевое различие между статической локальной и глобальной переменными заключается в том, что статическая локальная переменная остаётся известной только в том блоке, в котором она была объявлена. Проще говоря, статическая локальная переменная - это локальная переменная, сохраняющая своё значение между вызовами функций.

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

#include <stdio.h>
#include <conio.h>

int count (int i) ;

int main(void)
{

do {
count(0);
}
while(!kbhit());
printf("count called %d times", count (1));
return 0;
}

int count (int i)
{
static int c=0;

if(i) return c;
else c++;
return 0;
}

Иногда полезно знать, как часто функция вызывается во время работы программы. Это возможно сделать через использование глобальных переменных, но лучшим вариантом является использование функции, сохраняющей информацию внутри себя, как это сделано в функции count(). В данном примере, если count() вызывается со значением 0, то переменная с увеличивается. (Скорее всего в настоящих приложениях функция будет также выполнять некоторую другую полезную работу.) Если count() вызывается с любым другим значением, то она возвращает число сделанных вызовов. Подсчет числа вызовов функции может быть полезен при разработке программы, которая вызывает эти функции достаточно часто, и требуется привлечь к вызовам внимание.

Другим хорошим примером функции, требующей использования статических локальных переменных, является генератор последовательности чисел, создающий новое число, основываясь на старом. Это можно сделать, объявив глобальную переменную. Тем не менее, каждый раз, когда функция используется в программе, следует помнить об объявлении глобальной переменной и постоянно необходимо смотреть: не конфликтует ли переменная с другими, ранее объявленными, переменными. Также использование глобальной переменной приводит к тому, что функцию трудно поместить в библиотеку функций. Лучшим решением является объявление переменной, содержащей сгенерированное число как static, как в нижеприведенном фрагменте.

int series(void)
{
static int series_num;
series_num = series_num+23;
return(series_num);
}

В данном примере переменная series_num существует между вызовами функций вместо того, чтобы каждый раз создаваться и уничтожаться как обычная локальная переменная. Это означает, что каждый вызов series() может создать новый член серии, основываясь на последнем члене без глобального объявления переменной.

Можно было заметить нечто необычное в функции series(). Статическая переменная series_num не инициализируется. Это означает, что при первом вызове функции series_num имеет значение по умолчанию 0. Хотя это приемлемо для некоторых приложений, большинство генераторов последовательности требуют какую-либо другую стартовую точку. Чтобы сделать это, требуется инициализировать series_num до первого вызова series(), что может быть легко сделано, если series_num является глобальной переменной. Тем не менее, следует избегать использования series_num как глобальной переменной и лучше объявить ее как static. Это приводит ко второму способу использования static.


Статические глобальные переменные.

Когда спецификатор static применяется к глобальной переменной, он сообщает компилятору о необходимости создания глобальной переменной, которая будет известна только в файле, где статическая глобальная переменная объявлена. Это означает, что, даже если переменная является глобальной, другие подпрограммы в других файлах не будут знать о ней. Таким образом, не возникает повода для побочных эффектов. В некоторых ситуациях, где локальные статические переменные неприменимы, можно создать раздельно компилируемый файл и использовать его без боязни возникновения побочных эффектов.

Для того, чтобы понять, как можно использовать статические глобальные переменные, пример с генератором последовательности из предыдущего примера переделан таким образом, что стартовое значение может использоваться для инициализации серии путём вызова второй функции — series_start(). Ниже показан файл, содержащий series(), series_start() и series_num:

/* все должно быть в одном файле * /
static int series_num;

int series(void) ;
void series_start(int seed);

series(void)
{
series_num = series_num + 23;
return(series_num);
}

/* инициализация series_num */
void series_start (int seed)
{
series_num = seed;
}

Вызывая series_start() с некоторым известным целым числом, мы инициализируем генератор последовательности. После этого вызов series() приводит к генерации следующего элемента последовательности.

Имена статических локальных переменных известны только функции или блоку кода, в которых они объявлены, а имена статических глобальных переменных известны только в файле, в котором они находятся. Это означает, что если поместить функции series() и series_start() в отдельный файл, то можно использовать данные функции, но нельзя обращаться к переменной series_num. Она спрятана от остального кода программы. Фактически можно даже объявлять и использовать другую переменную, называемую series_num, в программе (в другом файле) и не бояться напутать. В сущности модификатор static разрешает использование функциями переменных, не беспокоя другие функции.

Статические переменные позволяют прятать части программы. Это может привести к большим преимуществам при разработке больших и сложных программ.


Регистровые переменные.

Си имеет ещё один спецификатор, который первоначально применялся только к переменным int и char. Стандарт ANSI С расширил сферу его применения. Спецификатор register просит, чтобы компилятор сохранил переменную способом, позволяющим осуществлять наибыстрейший доступ. Для целых чисел и символов это обычно подразумевает размещение не в памяти, а в регистрах процессора. Для других типов переменных компилятор может использовать другие способы для уменьшения времени доступа. Компилятор может просто игнорировать данную просьбу.

В Borland С++ спецификатор register может применяться к локальным переменным и формальным параметрам функции. Нельзя применять register к глобальным переменным. Также, поскольку регистровая переменная может быть сохранена в регистре процессора, нельзя получить адрес регистровой переменной. (Данное ограничение присутствует только в Си, но не в С++)

В целом операции с регистровыми переменными выполняются гораздо быстрее, чем с переменными, сохраненными в памяти. Фактически, когда значение переменной содержится в процессоре, не требуется доступа к памяти для определения или модификации значения. Это делает регистровые переменные идеальным средством для управления циклами. Ниже приведён пример объявления регистровой переменной типа int и дальнейшего её использования для управления циклом. Данная функция вычисляет m* для целых чисел.

int int_pwr (register int m, register int e)
{
register int temp;
temp = 1 ;
for( ; e; e--) temp *= m;
return temp;
}

В данном примере m, e и temp объявлены как регистровые переменные, поскольку они используются в цикле. Обычно регистровые переменные используются там, где они принесут наибольшую пользу, то есть в местах, где делается много ссылок на одну и ту же переменную. Это важно, поскольку не все переменные можно оптимизировать по времени доступа.

Важно понять, что спецификатор register - это просто запрос компилятору, который не обязательно будет удовлетворен. Как правило, можно использовать по крайней мере две регистровые переменные, размещаемые в регистрах процессора, типа char или int. Дополнительные регистровые переменные оптимизируются более продвинутыми компиляторами.


Продолжение в следующем посте...


...

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Ср Сен 22 2021, 17:44

11
Дополнение. Повторение.


Оператор присваивания.

Общий вид оператора присваивания следующий:

имя_переменной = выражение;

где выражение может быть как простой одиночной константой, так и сложной комбинацией переменных, операторов и констант. Как в Бейсике и Фортране, в Си используется знак равенства для отображения операции присваивания (не так, как в Паскале или Модуле-2, где используется конструкция := ). В левой части оператора присваивания должна стоять переменная, а не функция или константа.


Многочисленное присваивание.

С позволяет присваивать нескольким переменным одни и те же значения путём использования многочисленных присваиваний в одном операторе. Например, данный фрагмент программы присваивает переменным х, у и z значение 0:

х = у = z = 0;

В профессиональных программах переменным часто присваиваются стандартные значения с использованием данного метода.

Преобразование типов при присваивании.

Преобразование типов предназначено для ситуации, в которой переменные одного типа смешиваются с переменными другого типа. Когда возникает подобная ситуация в операторе присваивания, используется правило преобразования типов: значение справа (выражение) от оператора присваивания преобразуется к типу объекта, стоящего слева (целевой переменной). Это демонстрируется следующим примером:

int х;
char ch;
float f;

void func(void)
{
ch = х; /*    1    */
х = f;    /*    2    */
f = ch;  /*    3    */
f = x;    /*    4    */
}

В строке 1 левые, старшие, биты целочисленной переменной х обрубаются, оставляя в ch младшие 8 битов. Если х содержит число между 256 и 0, то ch и х будут иметь одинаковое значение Иначе значение ch будет содержать только младший набор битов переменной х. В строке 2 х получает целую часть переменной f. В строке 3 f получает 8-битное целое число, хранящееся в ch преобразованное к формату с плавающей точкой. В строке 4 f получает значение целочисленной переменной х, преобразованное к формату с плавающей точкой.

Когда происходит преобразование из целого числа к символу, из длинного целого — к целому и из целого — к короткому целому, то старшие биты будут потеряны. Когда используется 16-битное целое, это означает, что 8 бит будут потеряны при преобразовании от целого к символу и 16 бит будут потеряны при преобразовании от длинного целого к целому.

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

Таблица: Обычные результаты преобразования типов
Изучаем язык программирования Си. Вариант-2. 9ed_0060

  1. Преобразование из int в float или из float в double и тому подобное не добавляет точности. Такого рода преобразования только изменяют формат представления значения.

  2. Некоторые компиляторы Си (и процессоры) всегда трактуют переменные типа char как положительные, независимо от того, какое значение было при преобразовании к целому или плавающему типам. Другие компиляторы могут трактовать переменную типа char как знаковую, где числа, большие, чем 127, трактуются как отрицательные (как делает Borland С++). В целом следует использовать переменные типа char для символов, a int, short int или signed char когда необходимо избежать проблем с переносимостью.

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

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


Инициализация переменных.

Во время объявления переменных можно сообщить им значение путём помещения знака равенства и константы после имени переменной. Этот процесс называется инициализацией и в общем случае имеет вид:

тип имя_переменной — константа;

Ниже приведено несколько примеров

char ch = 'а';

int first = 0;

float balance = 123.23;

Глобальные и статические глобальные переменные инициализируются только при запуске программы. Локальные переменные инициализируются каждый раз при входе в блок, где они были объявлены. Статические локальные переменные инициализируются только один раз, а не каждый раз при входе в блок. Глобальные и статические локальные переменные инициализируются нулём, если не указано инициализационное значение. Не инициализирующиеся не статические локальные и регистровые переменные будут иметь неопределенное значение.


Константы.

Константы в Си — это фиксированные значения, которые не могут изменяться программой. Они могут быть любых типов, как показано в таблице.

Таблица: Примеры констант:
Изучаем язык программирования Си. Вариант-2. 9ed_0061

Си поддерживает ещё один тип констант в дополнение к предопределенным типам данных. Это строки. Все строковые константы заключаются в двойные кавычки, например: "тестовый текст". Не следует путать строки с символами. Одиночный символ заключается в одинарные кавычки, как например: 'а'. Поскольку строки - это обычные массивы символов.


Символьные константы с обратным слэшем.

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

Таблица: Кодировка с обратным слэшем:
Изучаем язык программирования Си. Вариант-2. 9ed_0062

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

Например:

ch = '\t';

printf("тестовая строка\n");

в результате первого присваивания переменная ch получает символ табуляции, после чего печатается строка «тестовая строка» с переводом курсора на новую строку.


Продолжение в следующем посте...


...

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Изучаем язык программирования Си. Вариант-2. Empty .

Сообщение  Viktor2312 Ср Сен 22 2021, 20:52

12
Принятие решений: операторы сравнения.


Выполняемые инструкции либо выполняют некоторые действия (например, вычисления или ввод-вывод), либо принимают решения. У нас имеется возможность принимать решения в программе, например выяснить, набрал ли ученик на экзамене больше 70 баллов, и должна ли программа вывести на экран сообщение: "Поздравляем! Экзамен сдан!" Далее мы познакомимся с простейшей версией условной инструкции if в языке Си, позволяющей программе принимать решения, исходя из истинности или ложности условного выражения. Если условное выражение истинно (то есть требуемое условие соблюдается), выполняется тело инструкции if. Если условное выражение ложно (то есть требуемое условие не соблюдается), тело инструкции if не выполняется. Независимо от того, выполняется тело инструкции if или нет, выполнение программы будет продолжено с инструкции, следующей за инструкцией if.

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

Изучаем язык программирования Си. Вариант-2. 444_e829

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


Спойлер:

Код для вставки в IDE:
Код:
// Пример: Prog_001
// Использование функций if, операторов отношений
// и операторов равенства.
#include <stdio.h>

// Функция печатающая строку текста следующего содержания:
// "Введите два целых числа, и я скажу вам"
int stroka_1() {

    printf("%c", 130), printf("%c", 162), printf("%c", 165);
    printf("%c", 164), printf("%c", 168), printf("%c", 226);
    printf("%c", 165), printf("%c", 255), printf("%c", 164);
    printf("%c", 162), printf("%c", 160), printf("%c", 255);
    printf("%c", 230), printf("%c", 165), printf("%c", 171);
    printf("%c", 235), printf("%c", 229), printf("%c", 255);
    printf("%c", 231), printf("%c", 168), printf("%c", 225);
    printf("%c", 171), printf("%c", 160), printf("%c", 44 );
    printf("%c", 255), printf("%c", 168), printf("%c", 255);
    printf("%c", 239), printf("%c", 255), printf("%c", 225);
    printf("%c", 170), printf("%c", 160), printf("%c", 166);
    printf("%c", 227), printf("%c", 255), printf("%c", 162);
    printf("%c", 160), printf("%c", 172), printf("%c", 255);
    printf("\n");
}

// Функция печатающая строку текста следующего содержания:
// "отношения, которым они удовлетворяют."
int stroka_2() {

    printf("%c", 174), printf("%c", 226), printf("%c", 173);
    printf("%c", 174), printf("%c", 232), printf("%c", 165);
    printf("%c", 173), printf("%c", 168), printf("%c", 239);
    printf("%c", 44 ), printf("%c", 255), printf("%c", 170);
    printf("%c", 174), printf("%c", 226), printf("%c", 174);
    printf("%c", 224), printf("%c", 235), printf("%c", 172);
    printf("%c", 255), printf("%c", 174), printf("%c", 173);
    printf("%c", 168), printf("%c", 255), printf("%c", 227);
    printf("%c", 164), printf("%c", 174), printf("%c", 162);
    printf("%c", 171), printf("%c", 165), printf("%c", 226);
    printf("%c", 162), printf("%c", 174), printf("%c", 224);
    printf("%c", 239), printf("%c", 238), printf("%c", 226);
    printf("%c", 46), printf("\n\n");
}

// Функция печатающая строку текста следующего содержания:
// "Введите первое число: "
int stroka_3() {

    printf("%c", 130), printf("%c", 162), printf("%c", 165);
    printf("%c", 164), printf("%c", 168), printf("%c", 226);
    printf("%c", 165), printf("%c", 255), printf("%c", 175);
    printf("%c", 165), printf("%c", 224), printf("%c", 162);
    printf("%c", 174), printf("%c", 165), printf("%c", 255);
    printf("%c", 231), printf("%c", 168), printf("%c", 225);
    printf("%c", 171), printf("%c", 174), printf("%c", 58 );
    printf(" ");
}

// Функция печатающая строку текста следующего содержания:
// "Введите второе число: "
int stroka_4() {

    printf("%c", 130), printf("%c", 162), printf("%c", 165);
    printf("%c", 164), printf("%c", 168), printf("%c", 226);
    printf("%c", 165), printf("%c", 255), printf("%c", 162);
    printf("%c", 226), printf("%c", 174), printf("%c", 224);
    printf("%c", 174), printf("%c", 165), printf("%c", 255);
    printf("%c", 231), printf("%c", 168), printf("%c", 225);
    printf("%c", 171), printf("%c", 174), printf("%c", 58 );
    printf(" ");
}

// Функция печатающая строку текста следующего содержания:
// " равно "
int stroka_5() {

    printf("%c", 255), printf("%c", 224), printf("%c", 160);
    printf("%c", 162), printf("%c", 173), printf("%c", 174);
    printf("%c", 255);
}

// Функция печатающая строку текста следующего содержания:
// " не равно "
int stroka_6() {

    printf("%c", 255), printf("%c", 173), printf("%c", 165);
    printf("%c", 255), printf("%c", 224), printf("%c", 160);
    printf("%c", 162), printf("%c", 173), printf("%c", 174);
    printf("%c", 255);
}

// Функция печатающая строку текста следующего содержания:
// " меньше чем "
int stroka_7() {

    printf("%c", 255), printf("%c", 172), printf("%c", 165);
    printf("%c", 173), printf("%c", 236), printf("%c", 232);
    printf("%c", 165), printf("%c", 255), printf("%c", 231);
    printf("%c", 165), printf("%c", 172), printf("%c", 255);
}

// Функция печатающая строку текста следующего содержания:
// " больше чем "
int stroka_8() {

    printf("%c", 255), printf("%c", 161), printf("%c", 174);
    printf("%c", 171), printf("%c", 236), printf("%c", 232);
    printf("%c", 165), printf("%c", 255), printf("%c", 231);
    printf("%c", 165), printf("%c", 172), printf("%c", 255);
}

// Функция печатающая строку текста следующего содержания:
// " меньше или равно "
int stroka_9() {

    printf("%c", 255), printf("%c", 172), printf("%c", 165);
    printf("%c", 173), printf("%c", 236), printf("%c", 232);
    printf("%c", 165), printf("%c", 255), printf("%c", 168);
    printf("%c", 171), printf("%c", 168), printf("%c", 255);
    printf("%c", 224), printf("%c", 160), printf("%c", 162);
    printf("%c", 173), printf("%c", 174), printf("%c", 255);
}

// Функция печатающая строку текста следующего содержания:
// " больше или равно "
int stroka_10() {

    printf("%c", 255), printf("%c", 161), printf("%c", 174);
    printf("%c", 171), printf("%c", 236), printf("%c", 232);
    printf("%c", 165), printf("%c", 255), printf("%c", 168);
    printf("%c", 171), printf("%c", 168), printf("%c", 255);
    printf("%c", 224), printf("%c", 160), printf("%c", 162);
    printf("%c", 173), printf("%c", 174), printf("%c", 255);
}

// Выполнение программы начинается с функции main
int main( void )
{
    int num1; // Первое число, введёное пользователем
    int num2; // Второе число введёное пользователем

 system("color A"); // Цвет зелёный на чёрном

 stroka_1();         // Выводим первую строку
 stroka_2();         // Выводим вторую строку
 stroka_3();         // Выводим третью строку
    scanf("%d", &num1); // Прочитать первое число
    stroka_4();         // Выводим четвёртую строку
    scanf("%d", &num2); // Прочитать второе число
    printf("\n\n\n");
  
    if (num1 == num2) {
        printf("[ OK ]\t\t");                          
        printf("%d", num1);
        stroka_5();             // Выводим " равно "
        printf("%d\n\n", num2);
    } // конец if                                    

    if (num1 != num2) {
        printf("[ OK ]\t\t");
        printf("%d", num1);
        stroka_6();             // Выводим " не равно "
        printf("%d\n\n", num2);
    } // конец if

    if (num1 < num2) {
        printf("[ OK ]\t\t");
        printf("%d", num1);
        stroka_7();             // Выводим " меньше чем "
        printf("%d\n\n", num2);
    } // конец if

    if (num1 > num2) {
        printf("[ OK ]\t\t");
        printf("%d", num1);
        stroka_8();             // Выводим " больше чем "
        printf("%d\n\n", num2);
    } // конец if

    if (num1 <= num2) {
        printf("[ OK ]\t\t");
        printf("%d", num1);
        stroka_9();             // Выводим " меньше или равно "
        printf("%d\n\n", num2);
    } // конец if

    if (num1 >= num2) {
        printf("[ OK ]\t\t");
        printf("%d", num1);
        stroka_10();            // Выводим " больше или равно "
        printf("%d\n\n", num2);
    } // конец if

    printf("\n\n\n\n\n\n\n\n\n");

 return (0);
} // конец функции main

Код в данном примере, не в полной мере соответствует стандартам безопасности CERT.

Результат работы программы:

Изучаем язык программирования Си. Вариант-2. 444_e838
Изучаем язык программирования Си. Вариант-2. 444_e839
Изучаем язык программирования Си. Вариант-2. 444_e840

Ввод двух чисел программой осуществляется с помощью функции scanf. Спецификатор преобразования функции передаёт аргумент, куда будет сохранено прочтённое значение. Спецификатор %d сохранит введённое значение в переменной num1, или при следующем вызове функции в num2. Таким образом первое введённое число будет сохранено в переменную num1, а второе в переменную num2.

...
scanf("%d", &num1); // Прочитать первое число
stroka_4(); // Выводим четвёртую строку
scanf("%d", &num2); // Прочитать второе число
...


Сравнение чисел.

Инструкция if в представленных ниже строках кода:

Код:
if (num1 == num2) {
        printf("[ OK ]\t\t");                          
        printf("%d", num1);
        stroka_5();             // Выводим " равно "
        printf("%d\n\n", num2);
    } // конец if

Проверяет значения переменных num1 и num2 на равенство. Если значения равны, то выполняется тело инструкции, то что находится внутри фигурных скобок, и выводится текст, сообщающий о равенстве значений. Если условное выражение в какой-либо из оставшихся инструкций if вернёт истинное значение, выполнится тело этой условной инструкции и на экран будет выведен соответствующий текст. Наличие дополнительных отступов в телах условных инструкций и пробельные строки выше и ниже каждой инструкции if повышают удобочитаемость программы.

Тело каждой условной инструкции if начинается с открывающей фигурной скобки {. Парная ей закрывающая фигурная скобка } завершает тело инструкции if. Тело инструкции if может состоять из любого количества инструкций.

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

Изучаем язык программирования Си. Вариант-2. 444_e841

Знак "равно" (=) также является оператором. Все эти операторы, кроме оператора присваивания = , имеют ассоциативность слева на право. Оператор присваивания (=) ассоциативен справа на лево.

Обращайтесь к таблице приоритетов, когда будете писать выражения с множеством операторов. Убедитесь, что операторы выполняются в правильном порядке. В случае каких-либо сомнений используйте круглые скобки для группировки подвыражений или для разбиения сложной инструкции на последовательность более простых инструкций. Помните, что некоторые операторы в языке Си, такие как оператор присваивания (=), ассоциативны справа на лево.

Некоторые слова, использовавшиеся нами в программах на Си, в частности слово int, являются ключевыми словами, или зарезервированными, словами языка. В таблице ниже перечислены ключевые слова языка Си:

Изучаем язык программирования Си. Вариант-2. 444_e842

Эти слова имеют специальное значение для компилятора, поэтому их не следует использовать в качестве идентификаторов, таких как имена переменных.



...

Viktor2312
RIP

Сообщения : 15492
Дата регистрации : 2012-08-10
Возраст : 45
Откуда : Пятигорск

Вернуться к началу Перейти вниз

Вернуться к началу

- Похожие темы

 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения