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

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

Последние темы
» Анонсы монет.
автор Viktor2312 Вчера в 14:30

» Team Red Miner
автор Viktor2312 Вс Фев 28 2021, 17:43

» Пул. beepool
автор Viktor2312 Вс Фев 28 2021, 15:16

» Пул. 2miners
автор Viktor2312 Вс Фев 28 2021, 15:06

» Пул. 0769.it
автор Viktor2312 Вс Фев 28 2021, 14:56

» T-Rex
автор Viktor2312 Вс Фев 28 2021, 14:41

» SRBMiner
автор Viktor2312 Вс Фев 28 2021, 13:53

» PhoenixMiner
автор Viktor2312 Вс Фев 28 2021, 13:26

» NPlusMiner
автор Viktor2312 Вс Фев 28 2021, 13:14

» NiceHash-Miner-Legacy-Fork-Fix
автор Viktor2312 Вс Фев 28 2021, 13:02

» NiceHash-miner
автор Viktor2312 Вс Фев 28 2021, 12:53

» NBMiner
автор Viktor2312 Вс Фев 28 2021, 12:42

» Nanominer
автор Viktor2312 Вс Фев 28 2021, 12:23

» MindMiner
автор Viktor2312 Вс Фев 28 2021, 12:03

» miniZ
автор Viktor2312 Вс Фев 28 2021, 11:32

» lolMiner
автор Viktor2312 Вс Фев 28 2021, 11:01

» KBMiner
автор Viktor2312 Вс Фев 28 2021, 10:58

» Hash Auger
автор Viktor2312 Вс Фев 28 2021, 10:33

» Пул. newpool.pw
автор Atari1974 Вс Фев 28 2021, 10:05

» GMiner
автор Viktor2312 Вс Фев 28 2021, 00:30

» EWBF
автор Viktor2312 Вс Фев 28 2021, 00:28

» Ethminer
автор Viktor2312 Сб Фев 27 2021, 19:57

» Ищу файл печатной платы
автор freddy Пт Фев 26 2021, 14:01

» Языки программирования. Статьи, заметки, очерки, разное...
автор Viktor2312 Чт Фев 25 2021, 19:52

» CryptoDredge
автор Viktor2312 Чт Фев 25 2021, 18:30

Самые активные пользователи за месяц
Viktor2312
Изучаем язык программирования С. Вариант-3. Vote_l10Изучаем язык программирования С. Вариант-3. Voting10Изучаем язык программирования С. Вариант-3. Vote_r10 

Поиск
 
 

Результаты :
 


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


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

Перейти вниз

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

Сообщение  Viktor2312 Ср Дек 02 2020, 03:53

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

____________________________________________________


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

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


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

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

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

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

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

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


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

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

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

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

Изучаем язык программирования С. Вариант-3. 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 и т. д.


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


Последний раз редактировалось: Viktor2312 (Сб Янв 09 2021, 13:48), всего редактировалось 2 раз(а)

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Ср Дек 02 2020, 04:33

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

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

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

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

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

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

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


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

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

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

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


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

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


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

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

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

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

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

***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Ср Дек 02 2020, 05:04

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

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


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

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

Изучаем язык программирования С. Вариант-3. 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-битных слов:

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

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

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

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

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

Изучаем язык программирования С. Вариант-3. 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( )

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

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

***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Ср Дек 02 2020, 07:58

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

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

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

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


extern

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

Изучаем язык программирования С. Вариант-3. 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
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Чт Дек 03 2020, 01:10

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

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

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

где выражение может быть как простой одиночной константой, так и сложной комбинацией переменных, операторов и констант. Как в Бейсике и Фортране, в С используется знак равенства для отображения операции присваивания (не так, как в Паскале или Модуле-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 бит будут потеряны при преобразовании от длинного целого к целому.

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

Таблица: Обычные результаты преобразования типов
Изучаем язык программирования С. Вариант-3. 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;

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


Константы.

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

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

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


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

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

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

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

Например:

ch = '\t';

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

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

***


.


Последний раз редактировалось: Viktor2312 (Чт Дек 03 2020, 02:42), всего редактировалось 1 раз(а)

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Чт Дек 03 2020, 02:41

6
Операторы.

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


Арифметические операторы.

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

Таблица перечисляет допустимые арифметические операторы С. Операторы +, —, * и / работают в С точно так же, как и в большинстве других языков. Их можно применять практически ко всем встроенным типам данных. Когда применяется / к целому числу или символу, остаток обрубается, например: 10/3 равно 3.

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

int х, у;
х = 10;
у = 3;
printf("%d", x/y); /* выводит 3 */
printf ("%d", х%у); /* выводит 1 - остаток целочисленного деления */

x = 1;
y = 2;
printf("%d %d", х/у, х%у)/ /* выводит 0 1*/

Причина того, что последняя строка печатает 0 и 1, заключается в том, что в результате целочисленного деления 1/2 получается 0 с остатком 1. 1 % 2 выдаёт остаток 1.

Унарный минус фактически умножает одиночный операнд на -1, то есть число, перед которым стоит знак минус, меняет свой знак.


Увеличение и уменьшение.

С предоставляет два полезных оператора, обычно отсутствующих в других языках. Это операторы уменьшения и увеличения, -- и ++. Оператор ++ добавляет 1 к операнду, а -- вычитает 1. Поэтому следующие операторы эквивалентны:

х = х + 1;

это то же самое, что и

++ х;

Аналогично

х = х - 1;

это то же самое, что и

--х;

Как оператор увеличения, так и оператор уменьшения могут стоять перед операндом (префиксный) или после операнда (постфиксный). Например:

х = х + 1;

может быть записано как

++ х;

или

х ++;

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

х = 10;
у = ++x;

В этом случае у устанавливается в значение 11. А если записать это как

х = 10;
у = x++;

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

Приоритет выполнения арифметических операций следующий:

высший + (унарный плюс) - (унарный минус) ++ -- * / %
низший + - (бинарные операторы)

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


Операторы отношения и логические операторы.

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

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

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

Таблица показывает операторы отношения и логические операторы. Таблица истинности для логических операторов образована с использованием на входах 1 и 0:
Изучаем язык программирования С. Вариант-3. 9ed_0065

Как операторы отношения, так и логические операторы имеют более низкий приоритет по сравнению с арифметическими операторами. Это означает, что выражение типа 10 > 1+12 вычисляется как 10 > (1 + 12). Результатом, естественно, будет ложь.

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

10 > 5 && 1(10 < 9) || 3 <= 4

в результате чего получаем истину.

Ниже показаны приоритеты выполнения операторов отношения и логических операторов:

высший ! > >= < <=  &&
низший ||

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

! 1 && 0

даст в результате 0, поскольку ! вычисляется первым, а затем вычисляется &&. Если в этом выражении поставить скобки следующим образом:

! (1 && 0)

то получится истина.

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


int x;
x = 100;
printf("%d", x > 1);


Битовые операторы.

В противоположность большинству языков, С поддерживает все существующие битовые операторы. Поскольку С создавался, чтобы заменить ассемблер, то была необходимость поддержки всех (или по крайней мере большинства) операций, которые может выполнить ассемблер. Битовые операции — это тестирование, установка или сдвиг битов в байте или слове, которые соответствуют стандартным типам языка С char и int. Битовые операторы не могут использоваться с float, double, long double, void и другими сложными типами. Таблица содержит имеющиеся операторы.

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

Битовые операторы И, ИЛИ, НЕ используют ту же таблицу истинности, что и их логические эквиваленты, за тем исключением, что они работают по-битно. Исключающее ИЛИ имеет следующую таблицу истинности:

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

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

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

Битовое И чаще всего используется для выключения битов То есть любой бит, установленный в 0, вызывает установку соответствующего бита в Другом операнде также в 0. Например, следующая функция читает символы из порта модема, используя функцию read_modem(), и сбрасывает бит четности в 0.

char get_char_from_modem(void)
{
char ch;
ch = read_modem (); /* получение символа из порта модема * /
return (ch & 127);
}

Чётность отображается восьмым битом, который устанавливается в 0 с помощью битового И, поскольку биты с номерами от 1 до 7 установлены в 1, а бит с номером 8 — в 0. Выражение ch & 127 означает, что выполняется битовая операция И между битами переменной ch и битами числа 127. В результате получим ch со сброшенным старшим битом. В следующем примере предполагается, что ch имеет символ 'А' и имеет бит чётности:

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

Битовое ИЛИ может использоваться для установки битов. Любой бит, установленный в любом операнде, вызывает установку соответствующего бита в другом операнде. Например, в результате операции 128 | 3 получаем:

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

Исключающее ИЛИ или как его ещё называют, XOR, устанавливает бит, если соответствующие биты в операндах отличаются. Например, в результате операции 127 ^ 120 получаем:

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

В общем, битовые И, ИЛИ и исключающее ИЛИ операции применяются к каждому биту переменной. Поэтому битовые операторы обычно не используются в условных операторах, которыми являются операторы отношения и логические операторы. Например: если х содержит 7, то х && 8 выдаст 1, в то время как х & 8 выдаст 0.

ПАМЯТКА: Операторы отношений и логические операторы всегда в качестве результата выдают 0 или 1, в то время как аналогичные им битовые операторы могут создать любое число в соответствии с их работой, другими словами, битовые операторы могут создать значения, отличные от 0 или 1, тогда как логические операторы всегда выдают 0 или 1.

Операторы сдвига >> и << сдвигают биты в переменной вправо и влево на указанное число. Общий вид оператора сдвига вправо:

переменная >> число сдвигов

а общий вид оператора сдвига влево:

переменная << число сдвигов

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

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

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

Оператор дополнение, ~, инвертирует состояние каждого бита указанной переменной, то есть 1 устанавливается в 0, а 0 — в 1.

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

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

Можно использовать показанную ниже функцию encode() для кодирования символа:

/* Простейшая шифрующая функция */

char encode(code ch)
{
return(~ch); /* дополнение */
}

***


.

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Пт Дек 04 2020, 01:58

7
Оператор ?

С имеет очень мощный оператор, который можно использовать вместо структуры if-then-else. Оператор ? имеет следующий вид:

выражение1 ? выражение2 : выражение3;

где выражение1, выражение2 и выражениеЗ - это выражения.

Оператор ? работает следующим образом: вычисляется выражение1; если оно истинно, то вычисляется выражение2 и всё выражение получает это значение; а если оно ложно, то вычисляется выражение3 и всё выражение получает это значение. Например:

х = 10;
у = х > 9 ? 100 : 200;

В данном примере у получает значение 100. Если бы х было меньше, чем 9, то у получило бы значение 200. Ниже приведен фрагмент программы, выполняющий такие же действия, но с использованием операторов if/else:

х = 10;
if (х > 9) у = 100;
else у = 200;

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

Код программы:

#include <stdio.h>

int per_1;
int per_2;

int main(void){
per_1 = 10;
per_2 = per_1 > 9 ? 100 : 200;
printf ("\n");
printf ("%d\n", per_2);

per_1 = 3;
per_2 = per_1 >9 ? 100 : 23 + 7;
printf ("\n");
printf ("%d\n", per_2);
printf ("\n");

return (0);
}

В начале программа должна перевести каретку, после чего напечатать число 100, так как per_1 у нас равен 10, а оно больше 9. Потом снова перевести каретку и во втором случае так как per_1 не больше 9 и результат будет "лож" то есть 0, то должно вычислиться 23 + 7 и результат присвоиться переменной per_2 и напечататься 30, после чего опять произойдёт перевод каретки и программа закончится.

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

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


Операторы указания & и *.

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

  1. Предоставляют быстрое обращение к элементам массива.

  2. Позволяют функциям модифицировать передаваемые параметры.

  3. Поддерживают динамические структуры данных, например списки.

Оператор &. Это унарный оператор, возвращающий адрес операнда в памяти. (Унарному оператору требуется только один операнд.) Например:

m = &count;

помещает в m адрес переменной count. Это адрес внутреннего местоположения переменной в компьютере. С самим значением переменной ничего не делается. Оператор & можно запомнить как «взятие адреса». Поэтому вышеупомянутый оператор присваивания можно прочитать как «m получает адрес count».

Для лучшего понимания данного присваивания предположим, что переменная count находится по адресу 2000. Также предположим, что count имеет значение 100. После данного присваивания m будет содержать 2000.

Оператор *, дополняющий &. Это унарный оператор, возвращающий значение переменной по указанному адресу. Например: если m содержит адрес переменной count, то

q = *m;

помещает значение count в q. Следуя вышеприведённому примеру, q получит значение 100, поскольку 100 хранилось по адресу 2000 - адресу, находящемуся в переменной m. Операция * может быть запомнена как «по адресу». В данном случае оператор можно прочитать как «q получает значение по адресу m».

К несчастью, значки для умножения и для взятия «по адресу» - одинаковы, впрочем как и значки битового И и «взятие адреса». Эти операторы не имеют связи друг с другом. Как & так и * имеют более высокий приоритет по сравнению с остальными арифметическими операциями, за исключением унарного минуса, имеющего такой же приоритет.

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

char *ch;

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

Можно смешивать объявление указателей и обычных переменных в одной строке. Например:

int х, *у, count;

объявляет х и count как переменные целочисленного типа, а у - как указатель на целочисленный тип.

Ниже операторы * и & используются для занесения числа 10 в переменную target:

#include <stdio.h>

/* присвоение с помощью * и & */

int main(void)

{
int target, source;
int *m;

source = 10;
m = &source;
target = *m;

printf("%d", target);

return 0;
}

Добавив в код нашего примера выше и этот код:

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

Получим результат, старые наши 100, 30 и дополнительно 10.

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

***


.

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Вт Дек 08 2020, 21:42

8
Оператор sizeof

sizeof - это унарный оператор, возвращающий длину в байтах переменной или типа, помещенных в скобки. Например:

float f;
printf("%f ", sizeof f);
printf("%d", sizeof(int));

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

Использование sizeof помогает создавать переносимый код для тех случаев, когда код зависит от размера стандартных типов данных С. Например, представим, что программе, работающей с базой данных, необходимо сохранять 6 целочисленных значений в записи. Для того, чтобы сделать эту программу переносимой, не следует предполагать, что размер целочисленного типа - 2 или 4 байта, следует самостоятельно определить настоящую длину, используя sizeof.

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


#include <stdio.h>

int main(void){
printf ("char                   - "); printf ("%d", sizeof(char)); printf (" Byte\n\n");
   printf ("unsigned char          - "); printf ("%d", sizeof(unsigned char)); printf (" Byte\n\n");
   printf ("signed char            - "); printf ("%d", sizeof(signed char)); printf (" Byte\n\n");
   printf ("int                    - "); printf ("%d", sizeof(int)); printf (" Byte\n\n");
   printf ("unsigned int           - "); printf ("%d", sizeof(unsigned int)); printf (" Byte\n\n");
   printf ("signed int             - "); printf ("%d", sizeof(signed int)); printf (" Byte\n\n");
   printf ("short int              - "); printf ("%d", sizeof(short int)); printf (" Byte\n\n");
   printf ("unsigned short int     - "); printf ("%d", sizeof(unsigned short int)); printf (" Byte\n\n");
   printf ("signed short int       - "); printf ("%d", sizeof(signed short int)); printf (" Byte\n\n");
   printf ("long int               - "); printf ("%d", sizeof(long int)); printf (" Byte\n\n");
   printf ("unsigned long int      - "); printf ("%d", sizeof(unsigned long int)); printf (" Byte\n\n");
   printf ("signed long int        - "); printf ("%d", sizeof(signed long int)); printf (" Byte\n\n");
   printf ("float                  - "); printf ("%d", sizeof(float)); printf (" Byte\n\n");
   printf ("double                 - "); printf ("%d", sizeof(double)); printf (" Byte\n\n");
   printf ("long double            - "); printf ("%d", sizeof(long double)); printf (" Byte\n\n");
   printf ("\n\n\n\n\n\n");

return (0);
}

Как она выглядит в редакторе компилятора:

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

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

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


Оператор «запятая».

Оператор «запятая» используется для связки нескольких выражений. Левая сторона оператора «запятая» всегда вычисляется как void (то есть не выдающее значения). Это означает, что значение выражения, находящегося с правой стороны, станет значением разделённого запятыми выражения. Например:

х = (у = 3, у + 1);

Сначала присваивается 3 переменной у, а затем 4 переменной х. Скобки необходимы, поскольку оператор «запятая» имеет более низкий приоритет по сравнению с оператором присваивания.

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

у = 10;

х = (у = у - 5, 25 / у);

После выполнения х получит значение 5, поскольку исходным значением у было 10, а затем оно уменьшилось на 5. Затем 25 поделили на полученное 5 и получили результат.

Об операторе «запятая» следует думать как об обычном слове «и» в нормальном русском языке, когда оно используется в выражении «сделай это, и это, и это».


Операторы [ ] u ()

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

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

#include <stdio.h>

char t[80];

int main(void)
{
t[3] = 'X';
printf("%c", t[3]);
return 0;
}

сначала присваивается значение 'X' четвёртому элементу (надо помнить, что индексация массивов в С начинается с 0) массива t, а затем печатается этот элемент.


Приоритеты в С.

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

Таблица содержит приоритеты всех операторов в С. Следует обратить внимание, что все операторы, кроме унарных операторов и оператора ?, вычисляются слева направо. Унарные операторы (*, &, -) и оператор ? вычисляются справа налево.

***

...

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Ср Дек 09 2020, 10:28

9
Выражения.

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


Преобразование типов в выражениях.

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

  1. Все переменные типа char и short int преобразуются к типу int. Все переменные типа float преобразуются к типу double.

  2. Если один из пары операндов имеет тип long double, другой операнд также преобразуется к long double.
    Иначе если один из операндов имеет тип double, другой операнд также преобразуется к double.
    Иначе если один из операндов имеет тип long, другой операнд также преобразуется к long.
    Иначе если один из операндов имеет тип unsigned, другой операнд также преобразуется к unsigned.

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

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

Сначала символ ch преобразуется к целому, а вещественная переменная с одинарной точностью f преобразуется к double. Затем ch / i преобразуется к double, поскольку f * d имеет тип double. Конечный результат имеет тип double, поскольку оба операнда типа double.


Принудительные преобразования.

Имеется возможность заставить выражение принять определенный тип с помощью оператора принудительных преобразований. Эта операция имеет следующий вид:

(тип) выражение

где тип - это один из стандартных типов данных С или определяемый пользователем тип. Например, если необходимо, чтобы выражение х / 2 имело тип float (остаток сохранится), следует написать:

(float) х / 2

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

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

#include <stdio.h>

/* выводит i и i/2 с дробной частью */

int main (void)
{
int i;
for(i=1; i<=50; ++i )
printf ("%d / 2 is: %f\n", i, (float) i/2);
return 0;
}

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

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

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

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


Пробелы и круглые скобки.

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

х=10/у~(127/х);

х = 10 / у ~ (127 / х) ;

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

х=у/3-34*temp&127;

или

х = (у / 3) - ((34 * temp) & 127);


Сокращённые операторы в С.

С имеет несколько специальных сокращённых операторов, кодирующих некоторые операторы присваивания. Например:

х = х + 10;

может быть кратко записано как

х += 10;

Оператор += сообщает компилятору, что необходимо присвоить переменной х старое значение x плюс 10.

Это сокращение работает для всех бинарных операторов в С (где требуется два операнда). Стандартная форма сокращений следующая:

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

то же самое, что и

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

В другом примере

х = х - 100;

записывается как

х -= 100;

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

***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Чт Дек 10 2020, 00:57

10
Операторы управления программой.

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

  1. Итерационные операторы - это while, for и do/while. Они чаще всего называются циклами.

  2. Операторы выбора или условные операторы - это if и switch.

  3. Операторы перехода - это break, continue и goto. (Оператор return, в принципе, также является оператором перехода, поскольку он воздействует на программу.)

Функция exit()  она также влияет на выполнение программы.


Истина и ложь в С.

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


Операторы выбора.

С поддерживает два типа оператора выбора: if и switch. Кроме этого, оператор ? является альтернативой оператору if.


IF

Стандартная форма оператора if следующая:

if (выражение) оператор;
else оператор;

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

if (выражение) {
последовательность операторов
}

else {
последовательность операторов
}

Если выражение истинно (любое значение, кроме 0), выполняется блок операторов, следующий за if; иначе выполняется блок операторов, следующих за else. Всегда выполняется код ассоциированный или с if или с else, но никогда не выполняются оба кода одновременно.

Рассмотрим следующую программу, являющуюся простейшим вариантом игры «угадай число». Она печатает сообщение «**Rignt**», когда игрок угадывает число.

#include <stdio.h>

/* программа "угадай число" */

int main(void){
int magic = 123; /* искомое число */
int guess;
printf ("Enter your guess: ");
scanf ("%d", &guess);
if (guess == magic) printf("\n\n** Right **\n\n");
return 0;
}

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

Отображение работы программы:

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

Программа использует оператор строгого равенства (==) для определения того, угадал игрок число или нет. Если угадал, то сообщение выводится на экран.

Рассмотрим следующую версию данной программы, иллюстрирующую использование оператора else для вывода сообщения о неправильном числе:

#include <stdio.h>

/* программа "угадай число" */

int main(void){
int magic = 123; /* искомое число */
int guess;
printf ("Enter your guess: ");
scanf ("%d", &guess);
if (guess == magic) printf("\n\n** Right **\n\n");
else printf("\n\n.. Wrong ..\n\n");

return 0;
}

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

И результат её работы, при неправильном числе:

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


Вложенные if

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

if (x)

   if (у) printf("1");

  else printf("2");

Какому if соответствует какое else?

К счастью, С предоставляет очень простое правило для разрешения такого рода проблем. В С else соответствует ближайшему предшествующему if (на том же уровне видимости), еще не имеющему оператора else. В данном случае else связан с оператором if (у). Для того, чтобы связать else с оператором if (х), следует использовать фигурные скобки, как показано ниже:
f (x) {
   if (у) printf ("1");
}
else printf ("2");

Теперь else связано с if (x), поскольку он не принадлежит больше блоку if (у). Из-за правил видимости С else теперь не знает об операторе if (у), поскольку он находится на другом уровне.

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

#include <stdio.h>

/* программа "угадай число" */

int main(void){
int magic = 123; /* искомое число */
int guess;
printf ("Enter your guess: ");
scanf ("%d", &guess);
if (guess == magic){
   printf("\n\n** Right **\n\n");
   printf("%d is the magic number\n\n\n\n", magic);
}
else{
printf("\n\n.. Wrong ..\n\n");
   if(guess > magic) printf("Too high\n\n\n\n");
   else printf("Too low\n\n\n\n");
}

return 0;
}

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

Результат работы программы, при правильно введённом числе:

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

Результат работы программы, если введённое число больше правильного:

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

Результат работы программы, если введённое число меньше правильного:

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


Лесенка if-else-if

Типичной программистской конструкцией является лесенка if-else-if. Она выглядит следующим образом:

if (выражение)
оператор;
else if (выражение)
оператор;
else if (выражение)
оператор;

...

else
 оператор;

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

#include <stdio.h>

/* программа "угадай число 5" */

int main(void)
{
int magic = 5; /* искомое число */
int guess;

printf ("Enter your guess: ");
scanf ("%d", &guess);

if (guess == magic){
   printf ("\n\n** Right **\n\n");
   printf ("%d is the magic number\n\n\n\n", magic);
}
else if(guess > magic)
printf ("\n\nWrong. Too high\n\n\n\n");
   else printf ("\n\nWrong. Too low\n\n\n\n");

return 0;
}

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

Результат работы программы, при правильно введённом числе:

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

Результат работы программы, если введённое число больше правильного:

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

Результат работы программы, если введённое число меньше правильного:

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


Оператор ? подробнее.

Оператор ? может использоваться для замены стандартной конструкции if/else:

if (условие) выражение;

else выражение;

Ограничением в данном случае является использование единственного выражения как после if, так и после else.

Оператор ? называется триадным оператором, поскольку ему требуется три операнда и он имеет следующий вид:

выражение1 ? выражение2 : выражение3

где выражение1, выражение2 и выражение3 - это выражения.

Оператор ? работает следующим образом. Вычисляется выражение1. Если оно истинно, вычисляется выражение2 и вся конструкция получает вычисленное выражение. Если выражение1 ложно, вычисляется выражение3 и вся конструкция получает вычисленное выражение. Например:

х = 10;

у = х > 9 ? 100 : 200;

В данном примере у получает значение 100. Если бы х было меньше, чем 9, то у получило бы значение 200. Ниже приведен фрагмент программы, выполняющий такие же действия, но с использованием операторов if/else:

х = 10;

if (х > 9) у = 100;

else у = 200;

Используя оператор ?, возможно переписать нашу программу следующим образом:

#include <stdio.h>

/* программа "угадай число 5" */

int main(void)
{
int magic = 5; /* искомое число */
int guess;

printf ("Enter your guess: ");
scanf ("%d", &guess);

if (guess == magic){
   printf ("\n\n** Right **\n\n");
   printf ("%d is the magic number\n\n\n\n", magic);
}
else
   guess > magic ? printf("\n\nHigh\n\n") : printf("\n\nLow\n\n") ;

return 0;
}

Здесь оператор ? приводит к выводу сообщения, основываясь на сравнении guess > magic.

***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Чт Дек 10 2020, 12:18

11
Оператор принятия решений switch

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

switch (выражение) {
case константа1:
последовательность операторов
break;
case константа2:
последовательность операторов
break;
case константа3:
последовательность операторов break;
...
default:
последовательность операторов
}

Оператор default выполняется, если не найдено соответствий, default необязателен и, если его нет, то в случае отсутствия совпадений ничего не происходит. Когда обнаруживается совпадение, операторы, ассоциированные с соответствующим case, выполняются до тех пор, пока не встретится оператор break. В случае default (или последнего case, если отсутствует default), оператор switch заканчивает работу при обнаружении конца.

Можно представить в более наглядной и понятной форме, например, так:

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

Следует знать о трех важных моментах оператора switch:

  1. switch отличается от if тем, что он может выполнять только операции проверки строгого равенства, в то время как if может вычислять логические выражения и отношения.

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

  3. Если в операторе switch используются символьные константы, они автоматически преобразуются к целочисленным значениям.


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

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

Так же можно иметь пустые условия. И выполнение переходит к следующему case, если отсутствует break.

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

/* неверно */

switch(с) {
case 1:
    int t;
...

Тем не менее переменная может быть добавлена:

/* верно */
switch(с) {
int t;
case 1:
...

Имеется возможность создания блока кода как одного из операторов в последовательности и объявление в нём переменной, как показано ниже:

/* Это также корректно */
switch (с) {
case 1:
{ /* create a block */
int t;
...
}


Вложенные операторы switch

Оператор switch может иметь среди последовательности операторов другой оператор switch. Даже если константы case внутреннего и внешнего операторов имеют одинаковые значения, не возникнет никакого конфликта. Следующий фрагмент кода совершенно корректен:

switch (х) {
case 1:
    switch(у) {
         case 0: printf("Divide by zero error.");
                      break;
          case 1: process(x,y);
      }
      break;
case 2:

...

***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Пт Дек 11 2020, 06:30

12
Циклы.


FOR

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

Стандартный вид цикла for следующий:

for (инициализация; условие; увеличение) оператор;

Оператор for имеет три главные части:

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

  2. Условие - это место, где находится выражение, определяющее условие работы цикла.

  3. Увеличение - это место, где определяется характер изменения переменной цикла на каждой итерации.

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

В нижеприведенном простом примере осуществляется вывод чисел от 1 до 25 включительно:

#include <stdio.h>

int main(void)
{
int x;
for(x=1; x<=25; x++) printf("%d ", x);
printf ("\n\n");

return 0;
}

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

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

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

В программе переменная x изначально установлена в 1. Поскольку х меньше 25, вызывается printf(), после чего х увеличивается на 1 и проверяется условие: по-прежнему ли х меньше либо равно 25. Данный процесс продолжается до тех пор, пока х не станет больше 25, и в этот момент цикл прервётся. В данном примере х является переменной цикла, которая изменяется и проверяется на каждой итерации цикла.

Ниже приведен пример цикла for, повторяющего несколько операторов:

#include <stdio.h>

int main(void){

int x;
float z;

for(x = 90; x != 45; x = x - 5){
z += 0.1;
printf ("The square root of %d, %f\n", x, z);
}
   printf ("\n\n");

return 0;
}

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

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

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

Как z += 0.1; так и printf(), вызываются и выполняются, пока х не равно 45. Обратим внимание, что в цикле переменная х уменьшается: сначала она получает значение 90 и на каждой итерации Цикла происходит уменьшение на 5.

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

x = 10;
for (у = 10; у != х; ++у) printf ("%d", у);

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


Вариации цикла for

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

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

for (х = 0, у = 0; х + у < 10; ++х)
{
scanf("%d", &у);

...

}

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

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

for (х=0; х != 123; ) scanf ("%d", &х);

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


Бесконечный цикл.

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

for ( ; ; ) printf(" this loop will run forever. \n");

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

На самом деле конструкция for( ; ; ) не обязательно создаёт бесконечный цикл, поскольку в теле цикла может присутствовать оператор break, при достижении которого цикл оканчивает работу. Нижеприведённая программа контролирует нажатие клавиш и, в случае достижения необходимого условия, бесконечный цикл прерывается:

for ( ; ; )
{
ch = getchar(); /* ввод символа */
if (ch == 'A') break; /* выход из цикла */
}
printf("you typed an A");

Цикл будет работать до тех пор, пока на клавиатуре не будет набрана А.


Циклы for без тела.

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

Следующий пример показывает, как создать задержку с помощью for:

for (t = 0; t < SOME_VALUE; t++) ;

Как видно, в цикле отсутствует тело.

***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Сб Янв 02 2021, 13:46

13
Цикл: do while


Есть еще один тип циклов — do while. Этот цикл полезен, когда необходимо выполнить код по крайней мере — 1 раз. Рассмотрим его структуру:

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

Структура очень простая, как видите условие находится в конце цикла, соответственно и проверка условия будет выполняться после того, как выполнятся код в теле цикла. Обратите внимание, что условие проверяется в конце цикла, а не в начале, так что блок кода в теле цикла будет выполнен по крайней мере один раз. Если условие истинно, цикл прыгает обратно в начало и снова выполняет его. Цикл do while почти ничем не отличается от цикла while, за исключением того, что тело цикла гарантированно выполняется хотя бы один раз. Цикл while сначала проверяет условие, а потом выполняет блок кода в теле, конечно же, если условие — истинно. В то время как do while сначала выполняет код в теле цикла, а затем проверяет условие, и если оно — истинное, то он продолжает работать. Пример работы цикла do while показан ниже:

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

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

***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Сб Янв 09 2021, 15:34

14
Оператор BREAK


Оператор break имеет два назначения. Первое - это окончание работы оператора switch. Второе - это принудительное окончание цикла, минуя стандартную проверку условия. Данное назначение здесь и рассматривается. Когда оператор break встречается в теле цикла, цикл немедленно заканчивается и выполнение программы переходит на строку, следующую за циклом. Например:

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

Данная программа выводит числа от 0 до 10 включительно и заканчивает работу, поскольку break вызывает немедленный выход из цикла, минуя условие t< 100.

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

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

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

break вызывает выход из самого внутреннего цикла. Например:

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

выводит числа от 0 до 9 включительно 2 раза. Каждый раз, когда встречается break, контроль передаётся внешнему циклу for.

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

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

break, используемый в операторе switch, влияет только на данный switch, но не на цикл, в котором может находиться switch.


***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Вс Янв 10 2021, 16:00

15
Функция exit()


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

void exit (int статус);

Она использует заголовочный файл stdlib.h. Значение статуса возвращается в операционную систему.

Для индикации корректности завершения работы exit() традиционно вызывается с аргументом 0.

Другие аргументы используются для индикации различного рода ошибок. Можно также использовать предопределенные макросы EXIT_SUCCESS и EXIT_FAILURE в качестве значений для статуса.

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

Код:
#include <stdlib.h>
int main(void)
{
if (!special_adaptor()) exit(1);
play ();
return 0;
}

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

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

В другом примере exit() используется для выхода из программы и возврата в операционную систему:

Код:
void menu(void)
{
char ch;
printf ("1.    Check Spelling\n");
printf ("2.    Correct Spelling, Errors\n");
printf("3.    Display Spelling Errors\n");
printf("4.    Quit\n");
printf("    Enter your choice: ");
do {
ch = getchar(); /* чтение клавиатуры */
switch(ch)
{
case '1':
check_spelling();
break;
case '2':
correct_errors();
break;
case '3':
display_errors ();
break;
case '4':
exit(0); /* возврат в ОС*/
}
}
while(ch!='1' && ch!='2' && ch!='3');
}


***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Пн Янв 11 2021, 15:00

16
CONTINUE


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

Код:
#include <stdio.h>
#include <stdlib.h>

int main (void)
{
 int x;
    do
 {
    scanf ("%d", &x);
    if (x < 0) continue;
    printf ("%d\n", x);
    }
 while(x < 100);

return 0;
}

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

В циклах while и do/while оператор continue вызывает переход к проверке условия и затем продолжает работу цикла. В случае for выполняется часть увеличения, затем проверяется условие и, наконец, выполняется само тело цикла.

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

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

Предыдущий пример может быть изменен для вывода только 100 чисел следующим образом:

Код:
for(t=0; t<100; ++t)
{
scanf ("%d", &x);
if (x<0) continue;
printf ("%d ", x);
}

В следующем примере оператор continue используется для ускорения выхода из цикла путём форсирования проверки, выполненной ранее:

Код:
void code(void)
{
char done, ch;
done = 0;
while (!done) {
ch = getchar();
if (ch == '.') {
done = 1; continue;
}
putchar(ch+1); /* сдвиг алфавита на одну позицию */
}
}

Можно использовать данную функцию для кодирования сообщений, сдвигая все символы на 1 вверх, то есть 'a' станет 'b'. Функция завершает работу при обнаружении точки, и вывода на экран символа, соответствующего точке, не происходит, поскольку благодаря continue выполнение, минуя оператор вывода, переходит к условию проверки, которое обнаруживает, что done истинно, и вызывает выход.


***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Viktor2312 Пт Янв 15 2021, 12:55

17
Метки и GOTO.


Хотя goto уже давно не рекомендуют использовать, он по-прежнему используется в программах. Мы не будем рассматривать целесообразность его использования с точки зрения элегантности управления программой. Следует заметить, что не существует таких ситуаций, где он был бы единственным решением. Общепринято, что он может быть полезен в некоторых ситуациях, goto используется довольно редко. (В языках типа С, имеющих богатый набор структур управления и предоставляющих дополнительные элементы управления типа break и continue, в нём нет необходимости.) Большинство программистов знают, что goto может легко запутать программу и сделать её практически нечитабельной. Тем не менее, бывают моменты, когда goto не только не усложняет программу, но даже её упрощает.

goto требует наличия меток для работы. Метка - это корректный идентификатор С, завершаемый двоеточием. Метка должна находиться в той же функции, что и goto. Например, цикл от 1 до 100 может быть записан с использованием goto и меток следующим образом:

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

Одним из хороших способов использования goto является выход из нескольких уровней вложения. Например:

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

Уничтожение goto приведёт к необходимости выполнения дополнительных проверок. Простой оператор break здесь не работает, поскольку он может выйти только из самого нижнего цикла.

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

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

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

Пример использования goto

Код:
#include <stdio.h>

int main (void)
{
int х = 0;
loop1:
х++;
printf("Hello goto!\n");
if (х < 23) goto loop1;

return 0;
}

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

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

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


***

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  ведущий_специалист Сб Янв 16 2021, 11:48

18
А я бы уточнил, что использовать goto в С совсем не нужно. Как бы совсем.
ведущий_специалист
ведущий_специалист
Мастер+

Сообщения : 281
Дата регистрации : 2020-10-16
Откуда : Санкт Петербург

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

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

Сообщение  Viktor2312 Вс Янв 17 2021, 01:42

19
Функции.


Функции - это базовые блоки С, в которых выполняются все операции. Стандартный вид функций следующий:

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

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


Оператор return

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


Выход из функции.

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

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

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

Тем не менее, не многие функции используют данный метод окончания своего выполнения. Большинство функций используют оператор return для окончания выполнения с целью возврата значения или упрощения кода функции и увеличения его эффективности путем создания нескольких точек выхода. Важно запомнить, что функция может иметь несколько операторов return. Например, функция, показанная ниже, возвращает или индекс первого появления подстроки, указываемой в s1, в строке, указываемой в s2, или —1, если не обнаружено совпадений:

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

Надо обратить внимание, как два оператора return упрощают функцию.


Возвращаемые значения.

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

х = power(у);

if (max (х, у) > 100) printf ("greater");

for (ch=getchar(); isdigit(ch); ) ...;

Тем не менее функция не может стоять с левой стороны оператора присваивания. Оператор типа

swap (х, у) = 100; /* некорректный оператор */

неправилен. Компилятор выдаст ошибку.

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

int t;
t = f(); /* нет значения для присваивания t */
f() + f(); /* нет значений для сложения */

Хотя все функции не типа void имеют значения возврата, при написании программ обычно используется три типа функций. Первый тип - это вычислительные функции. Он предназначен для выполнения операций с аргументами и возвращает значение, основываясь на этих операциях. Примером таких функций являются sqr() и sin() - стандартные библиотечные функции.

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

Последний тип функций не имеет определенного возвращаемого значения. Функция является обычной процедурой и не выдает значения. Примером служит srand(), используемая для инициализации генератора случайных чисел функции rand(). Иногда функции, не выдающие осмысленного результата, что-то все-таки выдают. Например, printf() возвращает число напечатанных символов. Очень трудно найти программу, которая проверяет это. Следовательно, хотя все функции, кроме функций, объявленных как void, возвращают значения, нет необходимости использовать все эти значения. Типичным вопросом по возвращаемым функциями значениям является: «Не должен ли я присвоить данное значение некоторой переменной, поскольку значение возвращается?» Ответ: «Нет». Если не указано, чему присваивается возвращаемое значение, то оно просто отбрасывается. Рассмотрим следующую программу, использующую mul():

Код:
#include <stdio.h>

int mul(int a, int b);

int main(void)
{
int x, y, z;
x = 10; y = 20;
z = mul(x, y); /* 1 */
printf ("%d\n\n", mul(x, y) ); /* 2 */
mul (x, y); /* 3 */
return 0;
}    

int mul (int a, int b)
{
return a*b;
}

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

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

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

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


Значения, возвращаемые функцией main()

Когда используется оператор return в main(), программа возвращает код завершения вызывавшему процессу (операционной системе). Возвращаемое значение должно быть целого типа. Большинство операционных систем, трактуют 0 как нормальное завершение программы. Остальные значения воспринимаются как ошибки.

Если не определено возвращаемое значение, то в операционную систему будет передано неизвестное значение. Поэтому гораздо полезнее использовать оператор return.


Правила видимости для функций.

Правила видимости языка - это правила, управляющие тем, что «видит» часть программы.

Каждая функция в С - это дискретный блок кода. Код функции является собственностью функции, и к нему нельзя получить доступ с помощью какого-либо оператора или другой функции, помимо вызова данной функции. (Например, невозможно, используя goto, перейти на середину другой функции.) Код, образующий тело функции, спрятан от остальной части программы. Если код не использует глобальные переменные или данные, то он и другие части программы не могут влиять друг на друга. Другими словами, код и данные, определённые в одной функции, не могут действовать на код и данные, определённые в другой функции, поскольку данные функции имеют разные области видимости.

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

Все функции в С находятся на одном уровне видимости. То есть невозможно определить функцию в функции.


Аргументы функции.

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

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

Функция is_in() имеет два параметра: s и с. Функция возвращает 1, если символ с является частью строки s. В противном случае она возвращает ноль.

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


Передача по значению и передача по ссылке.

...

_________________
"ЛП & ТИ"
Viktor2312
Viktor2312
Гуру+

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

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

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

Сообщение  Спонсируемый контент

20

Спонсируемый контент


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

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


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