typo fixes 07,08, more arrays work

This commit is contained in:
ivan-igorevich 2021-09-27 08:48:12 +03:00
parent f787ea3539
commit 910ca2a953
5 changed files with 61 additions and 9 deletions

Binary file not shown.

View File

@ -13,6 +13,7 @@
\usepackage{titlesec}
\usepackage{hyperref}
\usepackage{tikz}
\usetikzlibrary{positioning}
\usepackage[lmargin=1.5cm,rmargin=2.5cm,tmargin=2.5cm,bmargin=2.5cm,paperheight=240mm,paperwidth=170mm]{geometry}
\definecolor{codekeywords}{rgb}{0.1,0.3,0.3}
@ -41,7 +42,6 @@
showtabs=false,
tabsize=4
}
\author{Иван Овчинников}
\date{\today}
\title{Очередное введение в\\язык программирования C}

View File

@ -30,8 +30,8 @@
В этом разделе нас с вами ждут массивы. Много массивов. И ещё пара слов о директивах компилятору, иногда также называемых директивами препроцессора. С них и начнём.
\subsection{Директива \code{\#define}}
Помимо уже хорошо знакомой вам директивы \code{\#include}, частично описанной в разделе \hyperref[text:directive]{\ref{text:directive}}, естественно, существуют и другие. Некоторые из них ограничивают импорт описанных в заголовочном файле функций, некоторые <<\textbf{описывают}>> какие-то константы и даже действия. Вот, директиву \textbf{описать} мы и рассмотрим подробнее. Она не зря называется директивой препроцессора, поскольку даёт указание не процессору во время выполнения программы выделить память, присвоить значения, а непосредственно компилятору: заменить в тексте программы одни слова на другие. Таким образом можно задавать константы проекта, и даже делать сокращённые записи целых действий. Например, написав \code{\#define ARRAY\_LENGTH 50} мы предпишем компилятору, перед запуском трансляции нашего кода заменить все слова \code{ARRAY\_LENGTH} на цифру 50. В такой записи, слово \code{ARRAY\_LENGTH} будет называться \textit{макроконстантой}.
\frm{Обратите внимание, что директива пишется немного не так, как обычный оператор языка, хоть и может находиться в любом месте кода. В конце диерктивы не ставится точка с запятой. Это важно именно потому что директивы работают с текстом программы, то есть если точка с запятой всё же будет поставлена, текст программы будет всегда содержать вместо макроконстанты число и точку с запятой, что может в корне изменить смысл программы.}
Весьма удобно, но этим можно не ограничиваться, мы можем попросить компилятор заменить вызовы функций и операторы на короткие, удобные нам слова. Важно помнить, что директивы препроцессора работают с текстом программы, поэтому не осуществляют никаких дополнительных проверок. Это сложный и мощный инструмент, который чаще всего используется для решения нетривиальных задач, например, выбор кода, который попадёт в компиляцию в зависимости от операционной системы. Иногда в программах можно встретить описание недостающего но такого привычного булева типа при помощи директив препроцессора:
\frm{Обратите внимание, что директива пишется немного не так, как обычный оператор языка, хоть и может находиться в любом месте кода. В конце директивы не ставится точка с запятой. Это важно именно потому что директивы работают с текстом программы, то есть если точка с запятой всё же будет поставлена, текст программы будет всегда содержать вместо макроконстанты число и точку с запятой, что может в корне изменить смысл программы.}
Весьма удобно, но этим можно не ограничиваться, мы можем попросить компилятор заменить вызовы функций и операторы на короткие, удобные нам слова. Важно помнить, что директивы препроцессора работают с текстом программы, поэтому не осуществляют никаких дополнительных проверок. Это сложный и мощный инструмент, который чаще всего используется для решения нетривиальных задач, например, выбор кода, который попадёт в компиляцию в зависимости от операционной системы. Иногда в программах можно встретить описание недостающего, но такого привычного булева типа при помощи директив препроцессора:
\begin{lstlisting}[language=C,style=CCodeStyle]
#define bool int
#define true 1
@ -104,7 +104,7 @@ int main(int argc, char *argv[]) {
}
\end{lstlisting}\end{figure}
Мы научились создавать, инициализировать массивы и обращаться к его элементам. Теперь решим задачу посложнее: напишем программу, которая проверит насколько статистически хорош описанный в стандартной библиотеке (языка С) генератор псевдо-случайных чисел (функция \code{rand();}). Для такой статистической проверки нам понадобится сформировать так называемый \textit{частотный массив}, массив, в котором будет содержаться информация о том, сколько раз то или иное число появилось во множестве значений, полученном при помощи генератора псевдослучайных чисел, частота вхождения значений. Сама генерация псевдослучайных чисел происходит при помощий функции \code{rand();} которая создаёт целое число типа \code{int}.
Мы научились создавать, инициализировать массивы и обращаться к его элементам. Теперь решим задачу посложнее: напишем программу, которая проверит насколько статистически хорош описанный в стандартной библиотеке (языка С) генератор псевдо-случайных чисел (функция \code{rand();}). Для такой статистической проверки нам понадобится сформировать так называемый \textit{частотный массив}, массив, в котором будет содержаться информация о том, сколько раз то или иное число появилось во множестве значений, полученном при помощи генератора псевдослучайных чисел, частота вхождения значений. Сама генерация псевдослучайных чисел происходит при помощий функции \code{rand();} которая создаёт целое число типа \code{int}. Но, поскольку целое число в таком диапазоне нам не нужно, мы его сократим при помощи оператора получения остатка от деления.
\begin{figure}[h!]
\begin{lstlisting}[language=C,style=CCodeStyle]
#include <stdio.h>
@ -245,13 +245,65 @@ float average(int* array, int length) {
Попробуем визуализировать двумерный массив. Создадим двумерный массив в коде, например, 5х5 элементов. Массив 5х5 это 5 столбцов и 5 строчек. Соответственно, \textit{каждая строчка это будет у нас младший индекс, а каждый столбец старший индекс}. Трехмерный массив может быть, например, 3х3х3 его можно визулизировать как всем известный кубик Рубика то есть, это три стоящих друг за другом таблицы 3х3. Также опишем его в коде ниже. Получается, что мы к таблице (ширине и высоте) добавили третье \textbf{измерение}, поэтому и массив получается \textbf{многомерным}, в данном случае, \textbf{трёхмерным}. Массивы б\'{о}льших размерностей тоже можно встретить в программах, но значительно реже, только лишь потому, что их действительно немного сложнее представить себе.
\begin{figure}[h!]
\begin{lstlisting}[language=C,style=CCodeStyle]
\begin{multicols}{2}
\begin{lstlisting}[language=C,style=CCodeStyle]
int twoDimensional[5][5];
\end{lstlisting}
\columnbreak
\begin{lstlisting}[language=C,style=CCodeStyle]
int threeDimensional[3][3][3];
\end{lstlisting}
\end{lstlisting}
\begin{tikzpicture}[every node/.style={minimum size=1cm},on grid]
\begin{scope}[every node/.append style={yslant=-0.5},yslant=-0.5]
\shade[right color=gray!10, left color=black!50] (0,0) rectangle +(3,3);
\node at (0.5,2.5) {9};
\node at (1.5,2.5) {7};
\node at (2.5,2.5) {1};
\node at (0.5,1.5) {2};
\node at (1.5,1.5) {4};
\node at (2.5,1.5) {8};
\node at (0.5,0.5) {5};
\node at (1.5,0.5) {3};
\node at (2.5,0.5) {6};
\draw (0,0) grid (3,3);
\end{scope}
\begin{scope}[every node/.append style={yslant=0.5},yslant=0.5]
\shade[right color=gray!70,left color=gray!10] (3,-3) rectangle +(3,3);
\node at (3.5,-0.5) {3};
\node at (4.5,-0.5) {9};
\node at (5.5,-0.5) {7};
\node at (3.5,-1.5) {6};
\node at (4.5,-1.5) {1};
\node at (5.5,-1.5) {5};
\node at (3.5,-2.5) {8};
\node at (4.5,-2.5) {2};
\node at (5.5,-2.5) {4};
\draw (3,-3) grid (6,0);
\end{scope}
\begin{scope}[every node/.append style={
yslant=0.5,xslant=-1},yslant=0.5,xslant=-1
]
\shade[bottom color=gray!10, top color=black!80] (6,3) rectangle +(-3,-3);
\node at (3.5,2.5) {1};
\node at (3.5,1.5) {4};
\node at (3.5,0.5) {7};
\node at (4.5,2.5) {5};
\node at (4.5,1.5) {6};
\node at (4.5,0.5) {8};
\node at (5.5,2.5) {2};
\node at (5.5,1.5) {3};
\node at (5.5,0.5) {9};
\draw (3,0) grid (6,3);
\end{scope}
\end{tikzpicture}
\end{multicols}
\end{figure}
Как работать с многомерными массивами мы рассмотрим на примере двумерного массива. Поставим для себя задачу - сформировать таблицу Пифагора (раньше такие на тетрадях в клетку печатали на обратной стороне). Таблица подразумевает наличие двух измерений - строк и колонок. Для этого объявим константы rows и cols и присвоим им значения 10, rows это количество строк, а cols - соответственно столбцов Создадим двумерный массив, table[rows][cols]. Итак, мы создали массив размером rows, в каждом элементе которого содержится ссылка на массив размером cols - т.е. массив массивов содержащих непосредственные значения.
Как работать с многомерными массивами мы рассмотрим на примере двумерного массива. Поставим для себя задачу - сформировать таблицу Пифагора (раньше такие на тетрадях в клетку печатали на обратной стороне). Таблица подразумевает наличие двух измерений - строк и колонок. Для этого объявим константы rows и cols и присвоим им значения 10, rows это количество строк, а cols - соответственно столбцов. Создадим двумерный массив, table[rows][cols]. Итак, мы создали массив размером rows, в каждом элементе которого содержится ссылка на массив размером cols - т.е. массив массивов содержащих непосредственные значения.
% Таблица Пифагора представляет собой таблицу, где строки и столбцы озаглавлены множителями, а в ячейках таблицы находится их произведение. Вот это самое произведение мы и будем выводить.
% Заполнение таких массивов значениями ничем не отличается от заполнения одномерных массивов. Заполним нашу матрицу поэлементно: напишем двойной цикл который будет заполнять нашу таблицу.
% Объявим переменные итераторы. И с помощью внешнего цикла фор пройдемся по всем строкам массива, а с помощью вложенного по всем столбцам массива, при этом будем записывать в каждый элемент массива результат умножения. Формула (r + 1) * (c + 1) позволяет исключить 0 из нашей таблицы. Давайте разберем более подробно работу такой конструкции. Внешний цикл при каждой итерации перемещает нас на одну строчку вниз. Вложенный, при каждой итерации, перемещает нас на одно значение вправо. Важно понять, что на одну итерацию внешнего цикла приходится cols итераций вложенного. Т.е. с помощью такой конструкции мы поочередно перебираем все элементы массива.

View File

@ -141,7 +141,7 @@ int main(int argc, char *argv[]) {
\end{lstlisting}
\end{figure}
Теперь мы можем написать программы любой сложности, содержащие функции \code{isPrime()} или \code{sum()}. О том, что мы работаем с консолью, в нашем случае должна знать только функция \code{int main (int argc, char *argv[])}, поэтому ввод значений и вывод на экран мы оставим в ней, а подсчёты, проверки или другие важные действия и алгоритмы положим в функции. Именно это абстрагирование является сильной стороной использования функций, так, например, у нас нет необходимости каждый раз вставлять в программу код взаимодействия с консолью при выводе каждой строки, а можно ограничиться вызовом функции \code{printf();}
Теперь мы можем написать программы любой сложности, содержащие функции \code{isPrime()} или \code{sum()}. О том, что мы работаем с консолью, в нашем случае должна знать только функция \code{int main (int argc, char *argv[])}, поэтому ввод значений и вывод на экран мы оставим в ней, а подсчёты, проверки или другие важные действия и алгоритмы положим в функции. Именно это абстрагирование является сильной стороной использования функций, так, например, у нас нет необходимости каждый раз вставлять в программу код взаимодействия с консолью при выводе каждой строки, а можно ограничиться вызовом функции \code{printf();}.
\subsection{Прототип функции, заголовочные файлы}
Зачастую возникают ситуации, когда функция не описана до точки входа в программу, или вовсе лежит в другом файле, возможно, даже написанном не нами. В этом случае мы должны сообщить компилятору, что такую функцию придётся дополнительно поискать. Для этого необходимо указать всю информацию о функции, кроме её тела. Такое объявление называется \textbf{прототип или определение функции} (англ. function definition).
\frm{С определением функции тесно связано понятие \textit{сигнатуры} функции. Сигнатура функции для разных языков программирования представляется немного разным составом сведений, так, например, в языке С сигнатура - это тип возвращаемого значения, название функции и порядок типов параметров, например, для функции суммирования чисел, описанной выше, это будет \code{int sum(int, int)}.}

View File

@ -57,4 +57,4 @@ value of 'pointer' is 000000000061FE1C
\columnbreak
\lstinputlisting[language=C,style=CCodeStyle]{../sources/swapprog.c}
\end{multicols}
Применение такого подхода открывает перед нами широкие возможности. Ванжо, на схеме со стр. \pageref{fig:dereference}, что указатель - это тоже переменная, поэтому мы можем создавать указатели на указатели, и так далее любой сложности.
Применение такого подхода открывает перед нами широкие возможности. Важно, на схеме со стр. \pageref{fig:dereference}, что указатель - это тоже переменная, поэтому мы можем создавать указатели на указатели, и так далее, указатели любой сложности, тем самым увеличивая уровень абстракции программы.