forked from ivan-igorevich/basic-c
minor arrays fixes
This commit is contained in:
parent
910ca2a953
commit
11de104fc4
BIN
build/main.pdf
BIN
build/main.pdf
Binary file not shown.
138
main.tex
138
main.tex
|
@ -38,7 +38,6 @@
|
|||
#define false 0
|
||||
\end{lstlisting}
|
||||
Но нам пока что достаточно умения создать глобальную именованную константу. Код ниже демонстрирует, что директивы не обязательно группировать именно в начале файла, а можно использовать там, где это удобно и уместно, так мы можем объявить константу с длиной массива в начале файла, а можем прямо внутри функции \code{int main (int argc, char *argv[])}.
|
||||
\begin{figure}[h!]
|
||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||
int main(int argc, char* argv[]) {
|
||||
#define ARRAY_LENGTH 50
|
||||
|
@ -47,7 +46,12 @@
|
|||
return 0;
|
||||
}
|
||||
\end{lstlisting}
|
||||
\end{figure}
|
||||
|
||||
Результатом работы этой функции будет ожидаемое:
|
||||
\begin{verbatim}
|
||||
$ ./program
|
||||
a = 50
|
||||
\end{verbatim}
|
||||
\subsection{Массивы}
|
||||
Вступление про директивы препроцессора напрямую не связано с темой массивов, но директива \code{\#define} для объявления размера массива применяется чрезвычайно часто. Рассмотрим природу этого явления чуть позже.
|
||||
\frm{Массив – это множество данных одного типа, расположенных в памяти подряд.}
|
||||
|
@ -74,7 +78,7 @@
|
|||
\begin{figure}[h!]
|
||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||
int nArr[100]; // An array for 100 int's;
|
||||
float fArr[5]; // An array for 1 float's;
|
||||
float fArr[5]; // An array for 5 float's;
|
||||
char cArr[2]; // An array for 2 char's;
|
||||
int varElem;
|
||||
int nArr[varElem]; // Compile error! Number of elements must be constant;
|
||||
|
@ -95,6 +99,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int arr[5];
|
||||
int i;
|
||||
printf("Your array is: ");
|
||||
for (i = 0; i < 5; i++) {
|
||||
|
@ -103,7 +108,14 @@ int main(int argc, char *argv[]) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
\end{lstlisting}\end{figure}
|
||||
\end{lstlisting}
|
||||
\end{figure}
|
||||
Такая несложная программа даст нам следующий результат (Обратите внимание, что при такой инициализации, а точнее её отсутствии, значения внутри массива не гарантируются. В результате запуска программы на компьютере автора первые четыре индекса оказались равными нулю, а пятый принял странное отрицательное целочисленное значение):
|
||||
\begin{verbatim}
|
||||
$ ./program
|
||||
Your array is 0 0 0 0 -497067408
|
||||
\end{verbatim}
|
||||
|
||||
Мы научились создавать, инициализировать массивы и обращаться к его элементам. Теперь решим задачу посложнее: напишем программу, которая проверит насколько статистически хорош описанный в стандартной библиотеке (языка С) генератор псевдо-случайных чисел (функция \code{rand();}). Для такой статистической проверки нам понадобится сформировать так называемый \textit{частотный массив}, массив, в котором будет содержаться информация о том, сколько раз то или иное число появилось во множестве значений, полученном при помощи генератора псевдослучайных чисел, частота вхождения значений. Сама генерация псевдослучайных чисел происходит при помощий функции \code{rand();} которая создаёт целое число типа \code{int}. Но, поскольку целое число в таком диапазоне нам не нужно, мы его сократим при помощи оператора получения остатка от деления.
|
||||
\begin{figure}[h!]
|
||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||
|
@ -111,7 +123,7 @@ int main(int argc, char *argv[]) {
|
|||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#define ARRAY_LENGTH 10
|
||||
#define NUMBERS_AMOUNT 1000000;
|
||||
#define NUMBERS_AMOUNT 1000000
|
||||
|
||||
int main( int argc, char *argv[]){
|
||||
srand(time(NULL)); // initialize PRNG
|
||||
|
@ -133,7 +145,24 @@ int main( int argc, char *argv[]){
|
|||
}
|
||||
\end{lstlisting}
|
||||
\end{figure}
|
||||
Обратите внимание на 14ю строку: для сгенерированного на 13й строке числа \code{0} увеличим значение в 0-й ячейке массива, для числа \code{1} - в 1-й, и т.д. Данная программа наглядно демонстрирует не только работу с массивами, но и то, что генератор псевдослучайных чисел в языке С генерирует статистически верную последовательность случайных чисел.
|
||||
Обратите внимание на 14ю строку: для сгенерированного на 13й строке числа \code{0} увеличим значение в 0-й ячейке массива, для числа \code{1} - в 1-й, и т.д. Данная программа наглядно демонстрирует не только работу с массивами, но и то, что генератор псевдослучайных чисел в языке С генерирует статистически верную последовательность случайных чисел. Инициализация генератора псевдослучайных чисел на восьмой строке \code{srand(time(NULL));} происходит значением текущего времени системы, то есть мы гарантируем, что начальное значение генератора будет отличаться от запуска к запуску, а значит наше исследование будет чуть более достоверным.
|
||||
|
||||
\begin{figure}[h!]
|
||||
\begin{verbatim}
|
||||
$ ./program
|
||||
Number 0 generated 99955 (10.00%) times
|
||||
Number 1 generated 99977 (10.00%) times
|
||||
Number 2 generated 100156 (10.02%) times
|
||||
Number 3 generated 99952 (10.00%) times
|
||||
Number 4 generated 100212 (10.02%) times
|
||||
Number 5 generated 100713 (10.07%) times
|
||||
Number 6 generated 99418 ( 9.94%) times
|
||||
Number 7 generated 99768 ( 9.98%) times
|
||||
Number 8 generated 99918 ( 9.99%) times
|
||||
Number 9 generated 99931 ( 9.99%) times
|
||||
\end{verbatim}
|
||||
\end{figure}
|
||||
Запуск программы (даже несколько запусков) показывает, что всех значений получилось около десяти процентов, что говорит о том, что последовательность псевдослучайных чисел статистически верна.
|
||||
|
||||
\subsection{Идентификатор массива}
|
||||
Это будет непросто, но мы поговорим о том, что из себя представляет идентификатор массива, чем чреват выход за пределы массива, затронем тему арифметики указателей и научимся передавать указатели на массивы в функции. Как упоминалось ранее, массив - это ссылочный тип данных. То есть в идентификаторе хранится адрес, ссылка на первый байт первого элемента массива, дальнейший доступ к элементам осуществляется посредством \textit{смещения относительно этого байта}. Таким образом запись вида \code{array[0]} говорит нам о том, что нужно взять адрес массива и сместить указатель на \code{0} элементов того типа, из которых состоит массив. Отсюда становится ясно, почему \textbf{индексирование массивов начинается с нуля}.
|
||||
|
@ -188,6 +217,7 @@ int main( int argc, char *argv[]){
|
|||
Помним, что индексация массива начинается с нуля, поэтому длина массива всегда на единицу больше последнего индекса. Выведем в консоль надпись <<введите значение>>, при помощи функции \code{scanf();} считаем его и сразу привычным образом, оператором индексного доступа, положим в массив.
|
||||
\begin{figure}[h!]
|
||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||
int arr[ARRAY_LENGTH];
|
||||
int i = 0;
|
||||
float result = 0;
|
||||
while (i < ARRAY_LENGTH) {
|
||||
|
@ -207,10 +237,25 @@ for (i = 0; i < ARRAY_LENGTH; i++)
|
|||
printf("\nAnd the average is: ");
|
||||
for (i = 0; i < ARRAY_LENGTH; i++)
|
||||
result += *(arr + i);
|
||||
printf("%f\n", result / ARRAY_LENGTH);
|
||||
\end{lstlisting}
|
||||
\end{figure}
|
||||
Как вы видите, некоторые подсчеты программа выполняет за нас - мы прибавляем к указателю единицу, двойку, тройку и т.д, а программа понимает, что надо взять не следующий по счёту байт, а следующий указатель. Так как в данном примере мы используем массив в котором хранятся значения типа \code{int}, а как вы помните \code{int} в подавляющем большинстве случаев - это четыре байта, то при увеличении указателя на единицу, мы обратимся к области памяти находящейся на четыре байта дальше идентификатора, при увеличении на двойку на восемь байт и так далее. Подсчитать среднее арифметическое не составит труда, этот алгоритм нам знаком со средней школы. Далее при помощи функции \code{printf();} выведем в консоль среднее арифметическое. Запустим, повводим цифры и убедимся что все работает.
|
||||
|
||||
\begin{verbatim}
|
||||
$ ./program
|
||||
Enter value 0: 1
|
||||
Enter value 1: 2
|
||||
Enter value 2: 3
|
||||
Enter value 3: 4
|
||||
Enter value 4: 5
|
||||
Enter value 5: 6
|
||||
Enter value 6: 7
|
||||
Enter value 7: 8
|
||||
Enter value 8: 9
|
||||
Enter value 9: 10
|
||||
Your array is: 1 2 3 4 5 6 7 8 9 10
|
||||
And the average is: 5.500000
|
||||
\end{verbatim}
|
||||
Внимательный читатель мог заметить, что мы применяем операцию \textit{разыменования}. Что происходит, когда мы таким образом обращаемся к массиву? Операция разыменования получает доступ к значению, находящемуся по адресу. Адресс массива - это адрес его первого элемента, поэтому конструкция \code{*arr} вернёт значение нулевого элемента массива. А прибавление значений к этому указателю будет смещать его также, как это делает оператор квадратных скобок.
|
||||
|
||||
Как уже упоминалось, идентификатор массива - это не обычный указатель. Обычный указатель хранит в себе адрес какой-то другой переменной, и сам где-то хранится. Указатель на начало массива хранит в себе адрес массива, то есть адрес его нулевого элемента, и сам этот указатель находится в этом самом месте. На первый взгляд сложновато? Но пусть Вас это не сбивает с толку, на деле всё не так жутко. На деле это означает, что при передаче массива (читай идентификатора массива) в функцию в качестве аргумента, мы не должны использовать оператор взятия адреса, поскольку идентификатор массива сам по себе является указателем на собственное начало. Это открывает для нас широкие возможности по написанию функций, работающих с массивами данных. В только что написанной нами программе оформим вывод массива на экран и поиск среднего арифметического в виде функции. Опишем функции \code{printArray()} и \code{average()} в которые передадим указатель на массив и его длину, т.к. в массиве не содержится сведений о его размере.
|
||||
|
@ -239,6 +284,7 @@ float average(int* array, int length) {
|
|||
\lstinputlisting[language=C,style=CCodeStyle]{../sources/arrayaverage.c}
|
||||
\label{code:arrayaverage}
|
||||
\end{figure}
|
||||
\newpage
|
||||
|
||||
\subsection{Многомерные массивы}
|
||||
Массив в языке С может иметь сколько угодно измерений. Все массивы, с которыми мы имели дело до этого момента - одномерные, их легко визуализировать в виде простого перечисления элементов, возможно, как строки или как таблицы, состоящей из одной строки. Самые распространённые многомерные массивы - это двумерные и трёхмерные, которые легко себе представить в виде таблицы или куба соответственно. Итак, массив это структура, содержащая элементы. Двумерный массив - это массив из массивов, содержащих элементы. Трёхмерный - это массив из массивов, содержащих массивы, которые содержат элементы. И так далее. В массиве могут находиться любые типы данных, мы, для удобства, будем рассматривать работу массивов с числами.
|
||||
|
@ -251,6 +297,23 @@ float average(int* array, int length) {
|
|||
int twoDimensional[5][5];
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{||c|c|c|c|c||}
|
||||
\hline
|
||||
\hline
|
||||
0,0 & 0,1 & 0,2 & 0,3 & 0,4 \\
|
||||
\hline
|
||||
1,0 & 1,1 & 1,2 & 1,3 & 1,4 \\
|
||||
\hline
|
||||
2,0 & 2,1 & 2,2 & 2,3 & 2,4 \\
|
||||
\hline
|
||||
3,0 & 3,1 & 3,2 & 3,3 & 3,4 \\
|
||||
\hline
|
||||
4,0 & 4,1 & 4,2 & 4,3 & 4,4 \\
|
||||
\hline
|
||||
\hline
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
\columnbreak
|
||||
\begin{lstlisting}[language=C,style=CCodeStyle]
|
||||
int threeDimensional[3][3][3];
|
||||
|
@ -258,44 +321,39 @@ int threeDimensional[3][3][3];
|
|||
|
||||
\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};
|
||||
\node at (0.5,2.5) {2,0,0};
|
||||
\node at (1.5,2.5) {1,0,0};
|
||||
\node at (2.5,2.5) {0,0,0};
|
||||
\node at (0.5,1.5) {2,1,0};
|
||||
\node at (1.5,1.5) {1,1,0};
|
||||
\node at (2.5,1.5) {0,1,0};
|
||||
\node at (0.5,0.5) {2,2,0};
|
||||
\node at (1.5,0.5) {1,2,0};
|
||||
\node at (2.5,0.5) {0,2,0};
|
||||
\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};
|
||||
\node at (3.5,-0.5) {0,0,0};
|
||||
\node at (4.5,-0.5) {0,0,1};
|
||||
\node at (5.5,-0.5) {0,0,2};
|
||||
\node at (3.5,-1.5) {0,1,0};
|
||||
\node at (4.5,-1.5) {0,1,1};
|
||||
\node at (5.5,-1.5) {0,1,2};
|
||||
\node at (3.5,-2.5) {0,2,0};
|
||||
\node at (4.5,-2.5) {0,2,1};
|
||||
\node at (5.5,-2.5) {0,2,2};
|
||||
\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};
|
||||
\begin{scope}[every node/.append style={yslant=0.5,xslant=-1},yslant=0.5,xslant=-1]
|
||||
\node at (3.5,2.5) {2,0,0};
|
||||
\node at (3.5,1.5) {1,0,0};
|
||||
\node at (3.5,0.5) {0,0,0};
|
||||
\node at (4.5,2.5) {2,0,1};
|
||||
\node at (4.5,1.5) {1,0,1};
|
||||
\node at (4.5,0.5) {0,0,1};
|
||||
\node at (5.5,2.5) {2,1,2};
|
||||
\node at (5.5,1.5) {1,1,2};
|
||||
\node at (5.5,0.5) {0,1,2};
|
||||
\draw (3,0) grid (6,3);
|
||||
\end{scope}
|
||||
\end{tikzpicture}
|
||||
|
|
|
@ -18,11 +18,12 @@ float average(int* array, int length) {
|
|||
|
||||
int main(int argc, const char** argv) {
|
||||
#define ARRAY_LENGTH 10
|
||||
int arr[ARRAY_LENGTH];
|
||||
int i = 0;
|
||||
float result = 0;
|
||||
while (i < ARRAY_LENGTH) {
|
||||
printf("Enter value %d:", i);
|
||||
scanf("%d", arr[i]);
|
||||
scanf("%d", &arr[i]);
|
||||
i++;
|
||||
}
|
||||
printf("Our array is: ");
|
||||
|
|
Loading…
Reference in New Issue