refactored preambles

This commit is contained in:
ivan-igorevich 2022-02-02 00:08:39 +03:00
parent 6f639f55cd
commit 50b4bbbdfa
12 changed files with 324 additions and 160 deletions

29
book-preamble.tex Normal file
View File

@ -0,0 +1,29 @@
% \usepackage[printwatermark]{xwatermark}
\usepackage{ascii}
\usepackage{textcomp}
\usepackage{eurosym}
\usepackage{cclicenses}
\input{fancy-listings-preamble}
\usetikzlibrary{positioning}
\geometry{
paperheight=24cm,
paperwidth=17cm,
lmargin=1.5cm,
rmargin=2.5cm,
tmargin=2.5cm,
bmargin=2.5cm,
}
\author{Иван Овчинников}
\date{\today}
\title{Очередное введение в\\язык программирования C}
% \newwatermark[allpages,color=red!50,angle=70,scale=7,xpos=-36,ypos=14]{DRAFT}
\addto{\captionsrussian}{\renewcommand{\figurename}{}}
\newcommand{\sectionbreak}{\clearpage}

Binary file not shown.

85
common-preamble.tex Executable file
View File

@ -0,0 +1,85 @@
\usepackage{tikz}
\usepackage{import}
\usepackage{xcolor}
\usepackage{bookmark}
\usepackage{multicol,multirow,colortbl}
\usepackage{longtable}
\usepackage{setspace}
\usepackage{titlesec}
\usepackage{indentfirst}
\usepackage{amsmath,amsfonts,amssymb,amsthm,mathtools}
\usepackage{layout,lscape}
\usepackage{hyperref}
\usepackage{geometry}
\usepackage[russian]{babel}
\usepackage{nomencl}
\usepackage{makeidx}
\usepackage{fancyhdr}
\usepackage{tabularx,adjustbox}
\usepackage{float,makecell}
\usepackage{anyfontsize,tabto}
\usepackage{tocloft}
\makeindex
\makenomenclature
\babelfont{rm}{Times New Roman}
\babelfont{sf}{Liberation Serif}
\babelfont{tt}{Courier New}
\onehalfspacing
\hypersetup{
colorlinks=false,
linktoc=all
}
\fancypagestyle{plain}{ % для автосгенерированых
\fancyhf{}
\renewcommand{\headrulewidth}{0pt}
}
\fancypagestyle{titlepage}{
\fancyhf{}
\cfoot{\small{\textbf{Москва, \the\year{}г.}}}
}
\fancyhf{}
\renewcommand{\headrulewidth}{0pt}
\newcommand{\numerationTop}{
\fancyhead[C]{\thepage}
}
\newcommand{\numerationBottom}{
\fancyfoot[C]{\thepage}
}
\newcommand{\code}[1]{\texttt{#1}}
\renewcommand{\nomname}{ }
\newcommand*{\nom}[2]{#1\nomenclature{#1}{#2}}
\newcommand\blankpage{%
\null
\thispagestyle{empty}%
\addtocounter{page}{-1}%
\newpage
}
\newcommand{\frm}[1]{\newline%
\newline%
\indent\fbox{%
\parbox{0.9\textwidth}{%
#1}%
}%
\newline%
\newline%
}%
\makeatletter
\newcommand{\setword}[2]{%
\phantomsection
#1\def\@currentlabel{\unexpanded{#1}}\label{#2}%
}
\makeatother
\newcommand\lh[2]{\texttt{\textcolor{#1}{#2}}}
\newcommand\hrf[1]{\hyperref[#1]{\ref{#1}}}
\newcommand*\circled[1]{\tikz[baseline=(char.base)]{\node[shape=circle,draw,inner sep=2pt] (char) {#1};}}
\renewcommand{\cftsecleader}{\cftdotfill{\cftdotsep}}

104
fancy-listings-preamble.tex Normal file
View File

@ -0,0 +1,104 @@
\usepackage{listings}
\definecolor{codekeywords}{rgb}{0.1,0.4,0.4}
\definecolor{codecomments}{rgb}{0,0.6,0}
\definecolor{codenumbers}{rgb}{0.4,0.4,0.4}
\definecolor{codestring}{rgb}{0.85,0.2,0.1}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\definecolor{codefine}{rgb}{0.7,0.5,0.3}
\definecolor{dkgreen}{rgb}{0,0.6,0}
\definecolor{gray}{rgb}{0.5,0.5,0.5}
\definecolor{mauve}{rgb}{0.58,0,0.82}
\lstdefinestyle{JCodeStyle}{
frame=single,
language=Java,
aboveskip=3mm,
belowskip=3mm,
showstringspaces=false,
columns=flexible,
basicstyle=\footnotesize\ttfamily,
numbers=left,
numberstyle=\tiny\color{gray},
keywordstyle=\color{blue},
commentstyle=\color{dkgreen},
stringstyle=\color{mauve},
breaklines=true,
breakatwhitespace=true,
tabsize=4,
escapeinside={<@}{@>}
}
\lstdefinestyle{PyCodeStyle}{
frame=single,
commentstyle=\color{codecomments},
numberstyle=\tiny\color{codenumbers},
stringstyle=\color{codestring},
basicstyle=\ttfamily\footnotesize,
keywordstyle=\color{codekeywords},
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=4,
escapeinside={<@}{@>}
}
\lstdefinestyle{CCodeStyle}{
frame=single,
commentstyle=\color{codecomments},
morecomment=[l][\color{codefine}]{\#},
numberstyle=\tiny\color{codenumbers},
stringstyle=\color{codestring},
basicstyle=\ttfamily\footnotesize,
keywordstyle=\color{codekeywords},
emph={int,char,double,float,unsigned,void,bool},
emphstyle={\color{blue}},
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=4,
escapeinside={<@}{@>}
}
\lstdefinestyle{ASMStyle}{
frame=single,
numberstyle=\tiny\color{codenumbers},
commentstyle=\color{codecomments},
keywordstyle=\color{codekeywords},
morecomment=[l]{//}, % l is for line comment
morecomment=[s]{/*}{*/}, % s is for start and end delimiter
basicstyle={\ttfamily\footnotesize},
morekeywords={
add,addi,and,andi,
bge,beq,bne,br,
cmpeqi,cmpgei,cmplti,cmpnei,
ldhu,ldw,ldwio,
mov,movi,movhi,muli,
nop,nor,
ret,
slli,srai,srli,stw,stwio},
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showtabs=false,
tabsize=4,
escapeinside={<@}{@>}
}
\lstset{escapeinside={<@}{@>}}

View File

@ -1,75 +0,0 @@
% \usepackage[printwatermark]{xwatermark}
\usepackage{import}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{bookmark}
\usepackage{multicol}
\usepackage{setspace}
\usepackage{titlesec}
\usepackage{indentfirst}
\usepackage{amsmath,amsfonts,amssymb,amsthm,mathtools}
\usepackage{icomma}
\usepackage{layout}
\usepackage{titlesec}
\usepackage{hyperref}
\usepackage{tikz}
\usepackage{ascii}
\usepackage{textcomp}
\usepackage{eurosym}
\usepackage{cclicenses}
\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}
\definecolor{codecomments}{rgb}{0,0.4,0}
\definecolor{codenumbers}{rgb}{0.4,0.4,0.4}
\definecolor{codestring}{rgb}{0.85,0.2,0.1}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\definecolor{codefine}{rgb}{0.7,0.5,0.3}
\lstdefinestyle{CCodeStyle}{
commentstyle=\color{codecomments},
morecomment=[l][\color{codefine}]{\#},
numberstyle=\tiny\color{codenumbers},
stringstyle=\color{codestring},
basicstyle=\ttfamily\footnotesize,
keywordstyle=\color{codekeywords},
emph={int,char,double,float,unsigned,void,bool},
emphstyle={\color{blue}},
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=4
}
\author{Иван Овчинников}
\date{\today}
\title{Очередное введение в\\язык программирования C}
\babelfont{rm}{Times New Roman}
\babelfont{sf}{Microsoft Sans Serif}
\babelfont{tt}{Courier New}
% \newwatermark[allpages,color=red!50,angle=70,scale=7,xpos=-36,ypos=14]{DRAFT}
\onehalfspacing
\addto{\captionsrussian}{\renewcommand{\figurename}{}}
\hypersetup{
colorlinks=false,
linktoc=all
}
\newcommand{\code}[1]{\texttt{#1}}
\newcommand{\sectionbreak}{\clearpage}
\newcommand{\frm}[1]{\newline%
\newline%
\indent\fbox{%
\parbox{0.9\textwidth}{%
#1}%
}%
\newline%
\newline%
}%

View File

@ -1,6 +1,6 @@
\documentclass[a4paper]{article} \documentclass[fontsize=14bp]{article}
\usepackage[russian]{babel} \input{common-preamble}
\include{formatting} \input{book-preamble}
\begin{document} \begin{document}
\maketitle \maketitle
@ -35,7 +35,7 @@
\import{sections/}{12-files} \import{sections/}{12-files}
\section{Распределение памяти} \section{Распределение памяти}
Этот раздел находится в конце книги, но не по важности. Сильная сторона языка С (и, как следствие, С++) не только в возможности работать с указателями, но и в возможности самостоятельно управлять выделяемой памятью внутри программы. В языках высокого уровня данная возможность зачастую скрыта от программиста, чтобы по случайности программа не привела к зависанию среды виртуализации, по неосторожности не попыталась воспользоваться всей возможной оперативной памятью или не сломала операционную систему. Итак, как мы уже знаем, все переменные всех типов как-то хранятся в памяти, и до этого момента нас устраивало, как операционная система нам эту память выделяет. Но, пришло время взять бразды правления в свои руки. Процесс \textbf{выделения памяти} для программы называется \textbf{memory allocation}, отсюда и название функции, которая выделяет память и пишет в предложенный идентификатор указатель на начало этой области. Этот раздел находится в конце документа, но не по важности. Сильная сторона языка С (и, как следствие, С++) не только в возможности работать с указателями, но и в возможности самостоятельно управлять выделяемой памятью внутри программы. В языках высокого уровня данная возможность зачастую скрыта от программиста, чтобы по случайности программа не привела к зависанию среды виртуализации, по неосторожности не попыталась воспользоваться всей возможной оперативной памятью или не сломала операционную систему. Итак, как мы уже знаем, все переменные всех типов как-то хранятся в памяти, и до этого момента нас устраивало, как операционная система нам эту память выделяет. Но, пришло время взять бразды правления в свои руки. Процесс \textbf{выделения памяти} для программы называется \textbf{memory allocation}, отсюда и название функции, которая выделяет память и пишет в предложенный идентификатор указатель на начало этой области.
\subsection{\code{void* malloc(size);}} \subsection{\code{void* malloc(size);}}
Функция \code{void* malloc(size);} принимает в качестве аргумента размер выделяемой памяти. Как видим, функция возвращает довольно необычный на первый взгляд тип: \code{void*}, то есть область памяти будет зафиксирована, но не размечена. То есть это будет просто некоторая пустая область из \code{size} байт. Функция \code{void* malloc(size);} принимает в качестве аргумента размер выделяемой памяти. Как видим, функция возвращает довольно необычный на первый взгляд тип: \code{void*}, то есть область памяти будет зафиксирована, но не размечена. То есть это будет просто некоторая пустая область из \code{size} байт.

View File

@ -4,19 +4,19 @@
Переменные делятся на целочисленные, символьные, указатели и числа с плавающей точкой (англ. floating point, дробное число). Все, кроме указателей и символьных переменных бывают как знаковыми так и беззнаковыми. То есть в знаковых самый старший бит в двоичной записи этих переменных отводится под определение, является ли число отрицательным, или положительным, в беззнаковых все биты используются для записи числа, что увеличивает его диапазон возможных значений, но позволяет записать только положительные числа. В классическом С нет булевого типа, вместо него используется целое число и значения нуля для \textbf{лжи} и \textit{любое} другое число для \textbf{истины}, обычно это единица. Об указателях и булевой алгебре мы будем подробно говорить в одном из последующих разделов. Переменные делятся на целочисленные, символьные, указатели и числа с плавающей точкой (англ. floating point, дробное число). Все, кроме указателей и символьных переменных бывают как знаковыми так и беззнаковыми. То есть в знаковых самый старший бит в двоичной записи этих переменных отводится под определение, является ли число отрицательным, или положительным, в беззнаковых все биты используются для записи числа, что увеличивает его диапазон возможных значений, но позволяет записать только положительные числа. В классическом С нет булевого типа, вместо него используется целое число и значения нуля для \textbf{лжи} и \textit{любое} другое число для \textbf{истины}, обычно это единица. Об указателях и булевой алгебре мы будем подробно говорить в одном из последующих разделов.
\begin{figure}[h!] \begin{figure}[h!]
\centering \centering
\begin{tabular}{|p{1.5cm}|p{6.6cm}|p{2.4cm}|} \begin{tabular}{|p{15mm}|p{65mm}|p{25mm}|}
\hline \hline
Тип & Пояснение & Спецификатор формата \\ Тип & Пояснение & Спецификатор формата \\
\hline \hline
char & Целочисленный, самый маленький из адресуемых типов, диапазон: [\textminus128, +127] & \%c \\ char & Целочисленный, самый маленький из адресуемых типов, диапазон: [\textminus128, +127] & \%c \\
\hline \hline
short\newline short int & Тип короткого целого числа со знаком, диапазон: [\textminus32 768, +32 767] & \%hi \\ short\newline short int & Тип короткого целого числа со знаком, диапазон:\newline[\textminus32 768, +32 767] & \%hi \\
\hline \hline
int & Основной тип целого числа со знаком, диапазон: [\textminus2 147 483 648, +2 147 483 647] & \%i или \%d \\ int & Основной тип целого числа со знаком, диапазон:\newline[\textminus2 147 483 648, +2 147 483 647] & \%i или \%d \\
\hline \hline
long\newline long int & Тип длинного целого числа со знаком, диапазон: [\textminus2 147 483 648, +2 147 483 647] & \%li или \%ld \\ long\newline long int & Тип длинного целого числа со знаком, диапазон:\newline[\textminus2 147 483 648, +2 147 483 647] & \%li или \%ld \\
\hline \hline
long long\newline long long int & Тип двойного длинного целого числа со знаком, диапазон: [\textminus9 223 372 036 854 775 808, +9 223 372 036 854 775 807] & \%lli \\ long long\newline long long int & Тип двойного длинного целого числа со знаком, диапазон:\newline[\textminus9 223 372 036 854 775 808, +9 223 372 036 854 775 807] & \%lli \\
\hline \hline
float & Тип вещественного числа с плавающей запятой (одинарной точности) & \%f (автоматически преобразуется в double для printf()) \\ float & Тип вещественного числа с плавающей запятой (одинарной точности) & \%f (автоматически преобразуется в double для printf()) \\
\hline \hline
@ -363,7 +363,7 @@ a = a ^ b; //00001111
\end{figure} \end{figure}
Применять бинарную алгебру можно и в больших проектах, работающих со сложными высокоуровневыми абстракциями. Помимо этого важно помнить, что поддержка бинарных операций есть в подавляющем числе языков программирования. Используя бинарную алгебру можно создавать оптимальные протоколы передачи данных и/или алгоритмы хранения и обработки. Применять бинарную алгебру можно и в больших проектах, работающих со сложными высокоуровневыми абстракциями. Помимо этого важно помнить, что поддержка бинарных операций есть в подавляющем числе языков программирования. Используя бинарную алгебру можно создавать оптимальные протоколы передачи данных и/или алгоритмы хранения и обработки.
\begin{figure}[h!] \begin{figure}[H]
\begin{verbatim} \begin{verbatim}
$ ./program $ ./program
a = 15 a = 15

View File

@ -84,10 +84,13 @@ $
Теперь мы без проблем можем оформить уже существующие у нас программы в виде функций. Например, оформим в виде функции программу проверки простоты числа. Для этого опишем функцию которая возвращает целое число, назовем ее \code{isPrime()}, в качестве параметра она будет принимать целое число, назовем его \code{number}. Найдем в предыдущих разделах (стр. \hyperref[code:isPrime]{\pageref{code:isPrime}}) программу определения простоты числа и скопируем в тело функции. Внесем небольшие правки, уберем вывод так как это будет, можно сказать, классическая проверяющая функция, вывод оставим для функции \code{int main (int argc, char *argv[])}, пусть о наличии у нас терминала <<знает>> только она. Теперь мы без проблем можем оформить уже существующие у нас программы в виде функций. Например, оформим в виде функции программу проверки простоты числа. Для этого опишем функцию которая возвращает целое число, назовем ее \code{isPrime()}, в качестве параметра она будет принимать целое число, назовем его \code{number}. Найдем в предыдущих разделах (стр. \hyperref[code:isPrime]{\pageref{code:isPrime}}) программу определения простоты числа и скопируем в тело функции. Внесем небольшие правки, уберем вывод так как это будет, можно сказать, классическая проверяющая функция, вывод оставим для функции \code{int main (int argc, char *argv[])}, пусть о наличии у нас терминала <<знает>> только она.
\frm{Такой процесс, перенос участков кода между функциями, выделение участков кода в функции, синтаксические, стилистические и другие улучшения, называетя \textbf{рефакторингом}. Обычно, рефакторингом занимаются сами разработчики в свободное от основной деятельности времени, в периоды код ревью или по необходимости улучшить читаемость/повторяемость собственного кода.} \frm{Такой процесс, перенос участков кода между функциями, выделение участков кода в функции, синтаксические, стилистические и другие улучшения, называетя \textbf{рефакторингом}. Обычно, рефакторингом занимаются сами разработчики в свободное от основной деятельности времени, в периоды код ревью или по необходимости улучшить читаемость/повторяемость собственного кода.}
Следовательно, допишем условия: если делителей два, то число простое, возвращаем \code{ИСТИНУ}, то есть любое ненулевое значение, в нашем примере - единицу. Если же делителей больше число не простое, возвращаем \code{ЛОЖЬ}, в нашем случае, это ноль. Такой вывод можно записать и другим способом, \code{return (dividers == 2)} это выражение в случае истины вернет единицу в случае лжи ноль. Или можно воспользоваться тернарным оператором, то есть, написать \code{return (dividers == 2) ? 1 : 0}: если условие в скобках истинно вернется единица, ложно ноль. Также важно, что выйти из функции мы можем на любом этапе ее выполнения, например если делителей уже три, то нам нужно не завершать цикл, а вернуть \code{ЛОЖЬ} из функции. Следовательно, допишем условия: если делителей два, то число простое, возвращаем \code{ИСТИНУ}, то есть любое ненулевое значение, в нашем примере - единицу. Если же делителей больше число не простое, возвращаем \code{ЛОЖЬ}, в нашем случае, это ноль. Такой вывод можно записать и другим способом, \code{return (dividers == 2)} это выражение в случае истины вернет единицу в случае лжи ноль. Или можно воспользоваться тернарным оператором, то есть, написать \code{return (dividers == 2) ? 1 : 0}: если условие в скобках истинно вернется единица, ложно ноль. Также важно, что выйти из функции мы можем на любом этапе ее выполнения, например если делителей уже три, то нам нужно не завершать цикл, а вернуть \code{ЛОЖЬ} из функции.
\begin{figure}[H]
\setlength{\columnsep}{22pt}
\begin{multicols}{2} \begin{multicols}{2}
\begin{lstlisting}[language=C,style=CCodeStyle] \begin{lstlisting}[language=C,style=CCodeStyle]
int isPrime(int number){ int isPrime(int number){
int dividers = 0, i = 1; int dividers = 0, i = 1;
@ -126,6 +129,8 @@ int main(int argc, char *argv[]) {
} }
\end{lstlisting} \end{lstlisting}
\end{multicols} \end{multicols}
\end{figure}
%\setlength{\columnsep}{10pt}
Немного подправив вывод, внесем в него вызов функции \code{isPrime()} и объявим переменную \code{int num}, которую будем передавать в качестве аргумента в функцию \code{isPrime()}. Запустим нашу программу и убедимся что все работает число 71 действительно является простым. Немного подправив вывод, внесем в него вызов функции \code{isPrime()} и объявим переменную \code{int num}, которую будем передавать в качестве аргумента в функцию \code{isPrime()}. Запустим нашу программу и убедимся что все работает число 71 действительно является простым.
\begin{figure}[h!] \begin{figure}[h!]

View File

@ -52,9 +52,13 @@ value of 'pointer' is 000000000061FE1C
\frm{Для языка С также справедливо выражение <<передача по ссылке>>, поскольку в языке С нет отдельной операции передачи по ссылке. Так, например, в языке С++ передача по ссылке и передача по указателю - это разные операции.} \frm{Для языка С также справедливо выражение <<передача по ссылке>>, поскольку в языке С нет отдельной операции передачи по ссылке. Так, например, в языке С++ передача по ссылке и передача по указателю - это разные операции.}
То есть функция будет ссылаться на переменные, на которые мы укажем и оперировать их значениями. Давайте немного модифицируем нашу программу обмена значениями внутри двух переменных (\hyperref[code:programswap]{\ref{code:programswap}}): опишем её в виде функции, принимающей в качестве параметров два указателя на целые числа типа \code{char}, и передадим адреса созданных в \code{int main (int argc, char *argv[])} переменных. Внутри функции, при её вызове, у нас будут создаваться не переменные, а указатели на переменные, то есть мы будем ссылаться на те самые переменные, созданные вне функции, и будем менять именно их (тех переменных) значения. Таким образом, нам не нужно ничего возвращать, потому что в функции ничего не создавалось, и типом возвращаемого значения функции должен быть \code{void}. То есть функция будет ссылаться на переменные, на которые мы укажем и оперировать их значениями. Давайте немного модифицируем нашу программу обмена значениями внутри двух переменных (\hyperref[code:programswap]{\ref{code:programswap}}): опишем её в виде функции, принимающей в качестве параметров два указателя на целые числа типа \code{char}, и передадим адреса созданных в \code{int main (int argc, char *argv[])} переменных. Внутри функции, при её вызове, у нас будут создаваться не переменные, а указатели на переменные, то есть мы будем ссылаться на те самые переменные, созданные вне функции, и будем менять именно их (тех переменных) значения. Таким образом, нам не нужно ничего возвращать, потому что в функции ничего не создавалось, и типом возвращаемого значения функции должен быть \code{void}.
\begin{figure}[H]
\setlength{\columnsep}{30pt}
\begin{multicols}{2} \begin{multicols}{2}
\lstinputlisting[language=C,style=CCodeStyle]{../sources/swapfunc.c} \lstinputlisting[language=C,style=CCodeStyle]{../sources/swapfunc.c}
\columnbreak \columnbreak
\lstinputlisting[language=C,style=CCodeStyle]{../sources/swapprog.c} \lstinputlisting[language=C,style=CCodeStyle]{../sources/swapprog.c}
\end{multicols} \end{multicols}
\end{figure}
Применение такого подхода открывает перед нами широкие возможности. Важно, на схеме со стр. \pageref{fig:dereference}, что указатель - это тоже переменная, поэтому мы можем создавать указатели на указатели, и так далее, указатели любой сложности, тем самым увеличивая уровень абстракции программы. Применение такого подхода открывает перед нами широкие возможности. Важно, на схеме со стр. \pageref{fig:dereference}, что указатель - это тоже переменная, поэтому мы можем создавать указатели на указатели, и так далее, указатели любой сложности, тем самым увеличивая уровень абстракции программы.

View File

@ -5,11 +5,14 @@
Помимо уже хорошо знакомой вам директивы \code{\#include}, частично описанной в разделе \hyperref[text:directive]{\ref{text:directive}}, естественно, существуют и другие. Некоторые из них ограничивают импорт описанных в заголовочном файле функций, некоторые <<\textbf{описывают}>> какие-то константы и даже действия. Вот, директиву \textbf{описать} мы и рассмотрим подробнее. Она не зря называется директивой препроцессора, поскольку даёт указание не процессору во время выполнения программы выделить память, присвоить значения, а непосредственно компилятору: заменить в тексте программы одни слова на другие. Таким образом можно задавать константы проекта, и даже делать сокращённые записи целых действий. Например, написав \code{\#define ARRAY\_LENGTH 50} мы предпишем компилятору, перед запуском трансляции нашего кода заменить все слова \code{ARRAY\_LENGTH} на цифру 50. В такой записи, слово \code{ARRAY\_LENGTH} будет называться \textit{макроконстантой}. Помимо уже хорошо знакомой вам директивы \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{figure}[H]
\begin{lstlisting}[language=C,style=CCodeStyle] \begin{lstlisting}[language=C,style=CCodeStyle]
#define bool int #define bool int
#define true 1 #define true 1
#define false 0 #define false 0
\end{lstlisting} \end{lstlisting}
\end{figure}
Но нам пока что достаточно умения создать глобальную именованную константу. Код ниже демонстрирует, что директивы не обязательно группировать именно в начале файла, а можно использовать там, где это удобно и уместно, так мы можем объявить константу с длиной массива в начале файла, а можем прямо внутри функции \code{int main (int argc, char *argv[])}. Но нам пока что достаточно умения создать глобальную именованную константу. Код ниже демонстрирует, что директивы не обязательно группировать именно в начале файла, а можно использовать там, где это удобно и уместно, так мы можем объявить константу с длиной массива в начале файла, а можем прямо внутри функции \code{int main (int argc, char *argv[])}.
\begin{lstlisting}[language=C,style=CCodeStyle] \begin{lstlisting}[language=C,style=CCodeStyle]
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
@ -261,7 +264,7 @@ float average(int* array, int length) {
Попробуем визуализировать двумерный массив. Создадим двумерный массив в коде, например, 5х5 элементов. Массив 5х5 это 5 столбцов и 5 строчек. Соответственно, \textit{каждая строчка это будет у нас младший индекс, а каждый столбец старший индекс}. Трехмерный массив может быть, например, 3х3х3 его можно визулизировать как всем известный кубик Рубика то есть, это три стоящих друг за другом таблицы 3х3. Также опишем его в коде ниже. Получается, что мы к таблице (ширине и высоте) добавили третье \textbf{измерение}, поэтому и массив получается \textbf{многомерным}, в данном случае, \textbf{трёхмерным}. Массивы б\'{о}льших размерностей тоже можно встретить в программах, но значительно реже, только лишь потому, что их действительно немного сложнее представить себе. Попробуем визуализировать двумерный массив. Создадим двумерный массив в коде, например, 5х5 элементов. Массив 5х5 это 5 столбцов и 5 строчек. Соответственно, \textit{каждая строчка это будет у нас младший индекс, а каждый столбец старший индекс}. Трехмерный массив может быть, например, 3х3х3 его можно визулизировать как всем известный кубик Рубика то есть, это три стоящих друг за другом таблицы 3х3. Также опишем его в коде ниже. Получается, что мы к таблице (ширине и высоте) добавили третье \textbf{измерение}, поэтому и массив получается \textbf{многомерным}, в данном случае, \textbf{трёхмерным}. Массивы б\'{о}льших размерностей тоже можно встретить в программах, но значительно реже, только лишь потому, что их действительно немного сложнее представить себе.
\begin{figure}[h!] \begin{figure}[h!]
\setlength{\columnsep}{30pt}
\begin{multicols}{2} \begin{multicols}{2}
\begin{lstlisting}[language=C,style=CCodeStyle] \begin{lstlisting}[language=C,style=CCodeStyle]
int twoDimensional[5][5]; int twoDimensional[5][5];
@ -288,7 +291,7 @@ int twoDimensional[5][5];
\begin{lstlisting}[language=C,style=CCodeStyle] \begin{lstlisting}[language=C,style=CCodeStyle]
int threeDimensional[3][3][3]; int threeDimensional[3][3][3];
\end{lstlisting} \end{lstlisting}
\centering
\begin{tikzpicture}[every node/.style={minimum size=1cm},on grid] \begin{tikzpicture}[every node/.style={minimum size=1cm},on grid]
\begin{scope}[every node/.append style={yslant=-0.5},yslant=-0.5] \begin{scope}[every node/.append style={yslant=-0.5},yslant=-0.5]
\node at (0.5,2.5) {2,0,0}; \node at (0.5,2.5) {2,0,0};
@ -321,9 +324,9 @@ int threeDimensional[3][3][3];
\node at (4.5,2.5) {2,0,1}; \node at (4.5,2.5) {2,0,1};
\node at (4.5,1.5) {1,0,1}; \node at (4.5,1.5) {1,0,1};
\node at (4.5,0.5) {0,0,1}; \node at (4.5,0.5) {0,0,1};
\node at (5.5,2.5) {2,1,2}; \node at (5.5,2.5) {2,0,2};
\node at (5.5,1.5) {1,1,2}; \node at (5.5,1.5) {1,0,2};
\node at (5.5,0.5) {0,1,2}; \node at (5.5,0.5) {0,0,2};
\draw (3,0) grid (6,3); \draw (3,0) grid (6,3);
\end{scope} \end{scope}
\end{tikzpicture} \end{tikzpicture}
@ -367,10 +370,13 @@ for (r = 0; r < rows; r++) {
Запустив нашу программу с формированием таблицы умножения увидим, что все отлично работает. Полный листинг программы приводить не целесообразно, поскольку это цикл с заполнением и цикл с выводом, полностью привед§нные выше. К тому же, такой код носит исключительно академический характер, и в случае действительной необходимости формирования таблицы Пифагора на экране промежуточное заполнение массива будет излишним, результат умножения целесообразнее сразу выводить на экран. Запустив нашу программу с формированием таблицы умножения увидим, что все отлично работает. Полный листинг программы приводить не целесообразно, поскольку это цикл с заполнением и цикл с выводом, полностью привед§нные выше. К тому же, такой код носит исключительно академический характер, и в случае действительной необходимости формирования таблицы Пифагора на экране промежуточное заполнение массива будет излишним, результат умножения целесообразнее сразу выводить на экран.
Как уже говорилось, все массивы могут содержать данные любых типов, в том числе и указатели. Именно это позволяет массиву хранить другие массивы, строки и прочие ссылочные типы данных. Используя массивы указателей, мы можем создать, например, массив строк. Как уже говорилось, все массивы могут содержать данные любых типов, в том числе и указатели. Именно это позволяет массиву хранить другие массивы, строки и прочие ссылочные типы данных. Используя массивы указателей, мы можем создать, например, массив строк.
\begin{figure}[H]
\begin{lstlisting}[language=C,style=CCodeStyle] \begin{lstlisting}[language=C,style=CCodeStyle]
char* stringArray[3] = {"Hello", "C", "World"}; char* stringArray[3] = {"Hello", "C", "World"};
int r; int r;
for (r = 0; r < 3; r++) for (r = 0; r < 3; r++)
printf("%s ", stringArray[r]); printf("%s ", stringArray[r]);
\end{lstlisting} \end{lstlisting}
\end{figure}
Это указатели на строки, а точнее, на строковые литералы. Такой тип данных (строковый литерал) является указателем. И мы можем создать из этих указателей массив. Используя массивы указателей, мы можем создать, например, двумерный массив, где каждый элемент не обязан быть того же размера, что и остальные (но обязан быть того же типа, как мы помним). Но строки и сложно составленные указатели - это темы, которые очень сильно выходят за рамки Основ языка, хотя, конечно, это не помешает нам немного подробнее разобраться со строками в следующем разделе. Это указатели на строки, а точнее, на строковые литералы. Такой тип данных (строковый литерал) является указателем. И мы можем создать из этих указателей массив. Используя массивы указателей, мы можем создать, например, двумерный массив, где каждый элемент не обязан быть того же размера, что и остальные (но обязан быть того же типа, как мы помним). Но строки и сложно составленные указатели - это темы, которые очень сильно выходят за рамки Основ языка, хотя, конечно, это не помешает нам немного подробнее разобраться со строками в следующем разделе.

View File

@ -153,6 +153,8 @@ int main(int argc, const char* argv[]) {
\frm{В некоторых случаях может показаться, что никакой проблемы нет, поскольку написанная таким образом программа благополучно поприветствует пользователя, но такое поведение не гарантируется ни одним компилятором и ни одной операционной системой, поскольку возвращаемый таким образом указатель может быть переписан абсолютно любой следующей инструкцией кода. Такое \textit{исчезающее} значение называется \textbf{xvalue}.} \frm{В некоторых случаях может показаться, что никакой проблемы нет, поскольку написанная таким образом программа благополучно поприветствует пользователя, но такое поведение не гарантируется ни одним компилятором и ни одной операционной системой, поскольку возвращаемый таким образом указатель может быть переписан абсолютно любой следующей инструкцией кода. Такое \textit{исчезающее} значение называется \textbf{xvalue}.}
Выход очень простой: раз указатель не идёт в \code{int main (int argc, char *argv[])}, надо чтобы \code{int main (int argc, char *argv[])} дал нам указатель. Добавим в параметры функции указатель на выходную строку, и напишем что для начала сложить строки и положить в локальный массив \code{strcat(welcome, name)}. Добавим в основную функцию массив \code{char result[]}, который будет хранить результат и передадим в функцию \code{helloFunction} аргументы \code{name} и \code{result}. А раз функция больше ничего не возвращает, вполне легально сделать её \code{void}. Выход очень простой: раз указатель не идёт в \code{int main (int argc, char *argv[])}, надо чтобы \code{int main (int argc, char *argv[])} дал нам указатель. Добавим в параметры функции указатель на выходную строку, и напишем что для начала сложить строки и положить в локальный массив \code{strcat(welcome, name)}. Добавим в основную функцию массив \code{char result[]}, который будет хранить результат и передадим в функцию \code{helloFunction} аргументы \code{name} и \code{result}. А раз функция больше ничего не возвращает, вполне легально сделать её \code{void}.
\begin{figure}[H]
\begin{lstlisting}[language=C,style=CCodeStyle] \begin{lstlisting}[language=C,style=CCodeStyle]
void helloFunction(char* name, char* out) { void helloFunction(char* name, char* out) {
char welcome[255] = "Hello, "; char welcome[255] = "Hello, ";
@ -170,6 +172,7 @@ int main(int argc, const char* argv[]) {
return 0; return 0;
} }
\end{lstlisting} \end{lstlisting}
\end{figure}
Запускаем, и \textbf{снова не работает}, да ещё и как интересно, смотрите! Предупреждение, ладно, понятно, мы о нём говорили, но дальше, когда мы вводим имя на выходе получается какая-то совсем уж непонятная строчка, совсем не похожая на приветствие. Запускаем, и \textbf{снова не работает}, да ещё и как интересно, смотрите! Предупреждение, ладно, понятно, мы о нём говорили, но дальше, когда мы вводим имя на выходе получается какая-то совсем уж непонятная строчка, совсем не похожая на приветствие.
\begin{verbatim} \begin{verbatim}

View File

@ -24,6 +24,8 @@ struct fraction {
}; };
\end{lstlisting} \end{lstlisting}
Для сокращения записи опишем новый тип данных, назовём его \textbf{дробь}. Далее будут приведены два равнозначных способа сокращения записи структур: первый способ создаёт псевдоним для уже существующей структуры, а второй создаёт псевдоним для структуры прямо в момент её описания, поэтому и саму структуру можно не называть, а обращаться к ней только через псевдоним: Для сокращения записи опишем новый тип данных, назовём его \textbf{дробь}. Далее будут приведены два равнозначных способа сокращения записи структур: первый способ создаёт псевдоним для уже существующей структуры, а второй создаёт псевдоним для структуры прямо в момент её описания, поэтому и саму структуру можно не называть, а обращаться к ней только через псевдоним:
\begin{figure}[H]
\begin{lstlisting}[language=C,style=CCodeStyle] \begin{lstlisting}[language=C,style=CCodeStyle]
// alias for existing struct // alias for existing struct
typedef struct fraction Fraction; typedef struct fraction Fraction;
@ -35,6 +37,7 @@ typedef struct {
int den; // denominator int den; // denominator
} Fraction; } Fraction;
\end{lstlisting} \end{lstlisting}
\end{figure}
Обычно доступ к переменным внутри структуры осуществляется привычным для высокоуровневых языков способом - через точку. Есть одно исключение, но об этом чуть позже. Создадим три переменных для хранения двух дробей, с которыми будем совершать операции, и одну для хранения результата. Инициализируем переменные какими-нибудь значениями. Опишем целочисленные значения, опишем делимое для обеих дробей и опишем делитель для обеих дробей. Для простоты будем использовать простые дроби: \( 1\frac{1}{5} \) и \( -1\frac{1}{5} \). Обычно доступ к переменным внутри структуры осуществляется привычным для высокоуровневых языков способом - через точку. Есть одно исключение, но об этом чуть позже. Создадим три переменных для хранения двух дробей, с которыми будем совершать операции, и одну для хранения результата. Инициализируем переменные какими-нибудь значениями. Опишем целочисленные значения, опишем делимое для обеих дробей и опишем делитель для обеих дробей. Для простоты будем использовать простые дроби: \( 1\frac{1}{5} \) и \( -1\frac{1}{5} \).