Программирование на языке Си для решения физических задач. Учебное пособие
.pdfМинистерство науки и высшего образования Российской Федерации
Национальный исследовательский ядерный университет «МИФИ»
Е.Д. Маренков
Программирование на языке Си для решения физических задач
Учебное пособие
Москва 2025
УДК 519.6+004.43 ББК 32.97 М25
Маренков Е.Д. Программирование на языке Си для ре-
шения физических задач: Учебное пособие. М.: НИЯУ МИФИ, 2025. 64 с.
Рассмотрены основные численные методы, применяемые при моделировании физических процессов, и приемы их программирования на языке Си. Каждый метод изложен в виде задачи, предлагаемой для решения студентам. Также приведены лабораторные работы и задачи, посвященные численному расчету конкретных физических процессов.
Пособие предназначено для студентов бакалавриата НИЯУ МИФИ, предполагается знание основ программирования на языке Си.
ISBN 978-5-7262-3159-4
Рецензенты:
канд. физ.-мат. наук, доц. НИЯУ МИФИ И.В. Цветков;
© Национальный исследовательский ядерный университет
канд. физ.-мат. наук, доц. НИЯУ МИФИ П.Н. Рябов
«МИФИ», 2025
ISBN 978-5-7262-3159Издается-4 в авторской© Национальныйредакцисследовательский. Оригинал-макет подготовилядерныйЕуниверситет.Д. Маренков«МИФИ», 2025
Подписано в печать 01.07.2025. Формат 60х84 1/16.
Уч.-изд. л. 4,0. Печ. л. 4,0. Изд. № 007-1.
Издается в авторской редакции
Тираж 100 экз. Заказ № 31
Оригинал-макет подготовил Е.Д. Маренков
Национальный исследовательский ядерный университет
Подписано в печать 01.07.2025. Формат 60х84 1/16. Уч.-изд. л. 4,0. «МИФИ»Печ. л. 4,0.. Изд. № 007-1.
ТипографияТираж 100 экзНИЯУ. ЗаказМИФИ№ 31. .
115409, Москва, Каширское ш., 31.
Национальный исследовательский ядерный университет «МИФИ». Типография НИЯУ МИФИ.
115409, Москва, Каширское ш., 31.
Содержание
Глава 1. Язык С . . . . . . . . . . . . . . . . . . . . . . . |
5 |
||
1.1. |
Общие сведения . . . . . . . . . . . . . . . . . . . . |
5 |
|
1.2. |
О стиле написания кода . . . . . . . . . . . . . . . |
8 |
|
1.3. |
Синтаксис . . . . . . . . . . . . . . . . . . . . . . . . |
13 |
|
|
1.3.1 |
Типы данных . . . . . . . . . . . . . . . . . |
13 |
|
1.3.2 Операции . . . . . . . . . . . . . . . . . . . . |
14 |
|
|
1.3.3 Ветвление . . . . . . . . . . . . . . . . . . . . |
15 |
|
|
1.3.4 |
Циклы . . . . . . . . . . . . . . . . . . . . . |
15 |
1.3.5Переменные . . . . . . . . . . . . . . . . . . 16
1.3.6Функции . . . . . . . . . . . . . . . . . . . . 17
1.3.7Структуры . . . . . . . . . . . . . . . . . . . 18
1.4.Указатели и массивы . . . . . . . . . . . . . . . . . 20
1.5. |
Аргументы функций . . . . . . . . . . . . . . . . . |
26 |
1.6. |
Ввод и вывод . . . . . . . . . . . . . . . . . . . . . . |
27 |
1.7. |
Проекты из многих файлов . . . . . . . . . . . . . |
31 |
Глава 2. Программирование численных методов . . |
35 |
|
2.1.Решение квадратных и кубических уравнений . . . . . . . . . . . . . . . . . . . . . . . . 35
2.2.Вычисление некоторых специальных
функций . . . . . . . . . . . . . . . . . . . . . . . . 38
2.3.Статистика . . . . . . . . . . . . . . . . . . . . . . . 40
2.4.Вычисление интегралов . . . . . . . . . . . . . . . . 41
2.5.Простейшие операции с матрицами . . . . . . . . . 43
2.6.Решение систем линейных уравнений . . . . . . . . 44
2.7. |
Кубические сплайны . . . . . . . . . . . . . . . . . |
46 |
2.8. |
Решение нелинейных уравнений . . . . . . . . . . . |
48 |
3
2.9. Системы нелинейных уравнений . . . . . . . . . . 49
2.10.Решение ОДУ (явные методы) . . . . . . . . . . . . 51
2.11.Системы ОДУ . . . . . . . . . . . . . . . . . . . . . 52
Глава 3. Лабораторные работы
изадачи . . . . . . . . . . . . . . . . . . . . . . . 55
3.1.Лабораторная работа 1.
Неявные методы решения ОДУ . . . . . . . . . . . 55
3.2.Лабораторная работа 2.
Системы нелинейных уравнений . . . . . . . . . . 58
3.3.Моделирование физических процессов . . . . . . . 61 3.3.1 Теплопроводность . . . . . . . . . . . . . . . 61
3.3.2 Разложение первого порядка . . . . . . . . 61
3.3.3Рассеяние в центральном поле . . . . . . . . 62
3.3.4Точка остановки . . . . . . . . . . . . . . . . 62
Список литературы . . . . . . . . . . . . . . . . . . . . . 63
4
Глава 1
Язык С
1.1.Общие сведения
Язык С(Си) — императивный язык программирования общего назначения, поддерживающий структурное программирование, имеющий статическую систему типов, разделяющий области видимости переменных и допускающий рекурсию. «Императивность» означает, что программа, написанная на этом языке, представляет собой набор инструкций, изменяющих ее состояние. В противоположность императивным языкам, в декларативных языках не приводятся команды, которые необходимо выполнить, а описывается ожидаемый результат, без спецификации конкретного алгоритма. Примерами декларативных языков могут служить языки SQL, HTML, язык описания make-файлов и т.п. Понятие «структурного программирования» предполагает, что программа может быть разделена на процедуры и блоки кода, использовать циклы for и while вместо меток перехода и конструкций goto.
Язык С — компилируемый язык. Программа, написанная на языке С, должна быть сначала преобразована в исполняемый код при помощи компилятора, в отличие от интерпретируемых языков, в которых код выполняется интерпретатором непосредственно, без предварительного перевода всей программы на машинный язык. В общих чертах процесс преобразования исход-
5
ного кода в исполняемый файл выглядит так. На первом этапе исходный код каждого файла обрабатывается препроцессором, выполняющим свои директивы, которые в С начинаются со знака #, например #ifdef, #define, #include и т.п. После этого компилятор в несколько проходов преобразовывает исходный код в объектный файл. Код объектного файла содержит исполняемый код метки процедур, определенных в других модулях. На следующем этапе компоновщик (англ. linker) собирает все объектные файлы вместе, подставляя объектный код процедур, находящихся в подключаемых библиотеках. Получившийся код преобразуется в исполняемый код машинных команд. В случае использования динамически подключаемых библиотек программный код содержащихся в них процедур не разворачивается в исполняемом файле, а становится доступным уже на этапе выполнения программы.
Таким образом, процесс сборки программы состоит из компиляции и компоновки скомпилированных файлов в исполняемый код. Например, если код расположен в файлах f1.c, f1.h, main.c и используется библиотека libfoo.so, то для сборки с помощью компилятора gcc можно воспользоваться командой
gcc f1.c f1.h main.c -lfoo -o run
При этом получится исполняемый файл run. Компоновщик задействуется автоматически. Можно сделать это явно, в начале получив объектные файлы f1.o, main.o с помощью
gcc -c f1.c f1.h
gcc -c main.c
а затем собрав все вместе командой
gcc f1.o main.o -lfoo -o run
Преимущество второго способа в том, что если, например, изменения были только в файле main.c, его можно заново не компилировать, а воспользоваться старым объектным файлом. В больших проектах затраты на компиляцию могут быть очень существенны.
6
Первая версия С была разработана Кеном Томпсоном (Ken Thompson) и Деннисом Ричи (Dennis Ritchie) в 1970–1980 гг. В задачу разработчиков входило создание комплируемого языка, подходящего для написания ядра операционной системы Unix. Поэтому конструкции языка должны были быть построены таким образом, чтобы их можно было максимально прямо преобразовать в ассемблерный код. Этим объясняется некоторый минимализм С и, в частности, обильное использование указателей. В дальнейшем язык был усовершенствован при участии Брайна Керригана (Brian Kernighan); получившийся вариант описан в ставшей классической книге [1]. На момент написания настоящего пособия последней версией стандарта С является С11, вышедший в 2011 г. Официальное описание стандарта доступно в виде «черновиков» (draft), свободно распространяемых в Интернете. Хотя стандарт представляет собой довольно формальный документ, ознакомление с ним желательно любому стремящемуся к профессиональному использованию языка.
Хотя изначально областью применения С было, в основном, программирование операционных систем, со временем использование языка приобрело универсальный характер. Это связано с исключительной логичностью синтаксических конструкций, качеством ассемблерного кода, переносимостью программ, написанных на С, на различные платформы. Существуют даже библиотеки, позволяющие разрабатывать на С приложения с графическим интерфейсом, например gtk. Язык С получил распространение и как язык для численных расчетов и компьютерного моделирования. На него портированы многие библиотеки для численных расчетов, изначально написанные на языке Фортран, например библиотека LAPACK. Существует также свободно распространяемая библиотека GSL (Gnu Scientific Library), целиком написанная на С и содержащая большой набор процедур, необходимых для численных расчетов. Реализация большого количества численных алгоритмов на С приведена в широко известной книге [2]. Некоторые материалы этой книги используются в этом пособии.
7
1.2.О стиле написания кода
Нужно понимать, что исходный код программы пишется для читающих его людей, а не для компьютера. В процессе разработки программы большая часть времени уходит именно на чтение написанного кода в поисках ошибок или усовершенствований. Компилятору все равно, насколько читаем код с точки зрения человека, имеет значение только его формальная правильность. Одна из особенностей С состоит в том, что на нем можно писать формально правильный, но совершенно не понятный без специальных усилий код. Существует даже ежегодный конкурс программистов IOCCC (The International Obfuscated C Code Contest), посвященный написанию именно таких программ. В качестве примера такого творчества приведем такую программу, работающую в Unix и выводящую на экран время в момент компиляции:
main(_){_^448&&main(-~_);putchar(--_%64?32|-~7
[__TIME__-_/8%8][">’txiZ^(~z?"-48]>>";;;====~$
::199"[_*2&8|_/64]/(_&2?1:8)%8&1:10);}
К сомнительным практикам можно отнести использование «венгерской нотации». Такой стиль предполагает, что перед именами переменных добавляют префикс, обозначающий тип. Например, ii — это переменная i целого типа. Что в таком случае означает dx? Может быть, это шаг по оси x, а может быть, это значение координаты типа double.
В целом, создание легко поддерживаемого и нормально работающего кода является достаточно сложной задачей. Этому посвящено большое количество литературы по программированию. Приведем только некоторые рекомендации, взятые из документа «Linux kernel coding style», распространяемого вместе с исходным кодом ядра Linux.
1. Используете знаки табуляции длиной в восемь пробелов. Смысл отступов в коде состоит в ясном определении того, где начинается и заканчивается блок. Если в коде попадаются более трех отступов подряд, это повод пересмотреть код.
8
2.Никогда не помещайте несколько выражений в одну строчку. Такой код трудно читать, кроме того, его неудобно отлаживать, так как посередине строки нельзя ставить точку остановки.
3.Всегда лучше помещать открывающуюся скобку в конце строки:
i f ( x i s tru e ) { we do y
}
Исключением являются функции:
int f u n c t i o n ( int |
x ) |
{ |
|
body o f |
f u n c t i o n |
} |
|
Закрывающуюся скобку лучше оставлять одну на строке, кроме оператора while и вложенных if-else:
do {
|
|
body o f do−loop |
} |
while ( c o n d i t i o n ) ; |
|
i f |
( x == y ) { |
|
|
|
. . |
} |
else |
i f ( x > y ) { |
|
|
. . . |
} |
else |
{ |
} |
|
. . . . |
|
|
|
4.Используйте пробелы после ключевых слов. Однако не стоит добавлять пробелов у скобок. Окружайте пробелами операторы =, +, - и др. Не добавляйте пробелы после унарных операторов &, *, +, - и др., также не нужны пробелы вокруг операторов доступа к полям структур ->, «.».
5.При обозначении имен переменных не надо смешивать заглавные и строчные буквы, как это принято в исходном коде Microsoft Windows. Такие имена очень сложно воспринимать: ThisVariableIsATemporaryCounter. Лучше просто назвать эту переменную tmp. Если необходимы глобальные переменные или
9
функции, то им следует давать развернутые названия. Например, глобальная функция, считающая число активных пользователей, может называться count_active_users(), но никак не cntusr(). С другой стороны, имена локальных переменных можно выбирать короткими. Счетчик цикла лучше обозначить просто i, чем loop_counter. Если чувствуете, что можете запутаться из-за коротких имен локальных переменных, код необходимо пересмотреть и разделить его на более мелкие функции.
6.В комментариях никогда не объясняйте, как работает код, а только что он делает. Если код написан хорошо, то алгоритм его работы должен быть и так понятен. Рекомендуется использовать «/*...*/» стиль вместо «//». Всегда комментируйте данные.
7.При объявлении макросов всегда используйте заглавные буквы в названиях констант:
#define CONSTANT 0 x12345
Лучше не определять макросов-функций. Вместо этого лучше определить функцию inline. Однако не стоит просить компилятор делать inline функции длиннее трех строк кода.
Принципиальным вопросом является правильное разделение кода на функции и файлы. В упомянутых рекомендациях, как и в большом количестве литературы, рекомендуется делать функции достаточно короткими и предназначенными для выполнения только одной задачи. Длина функции не должна превышать одного-двух экранов кода. Уровень отступов может являться одним из критериев сложности функции. Если приходится делать слишком много вложений, то функцию лучше разделить на более короткие части. При этом не стоит бояться потери производительности кода из-за выделения кусков в отдельные функции, даже если их тело используется только один раз. Еще одним критерием сложности является количество локальных переменных. Их не должно быть больше 5–10 шт. Если для переменных становится трудно выбирать названия, то это означает, что функции стали слишком длинными!
В файле между функциями следует оставлять одну пустую строку.
10
