
- •Конструкция else if
- •Рассмотрим интересные условные выражения с использование логических операторов.
- •Циклы for, while и do while в языке c
- •Функции в языке программирования c
- •Указатели в c
- •Что такое указатели и зачем они нужны?
- •Объявление указателя, получение адреса переменной
- •Выделение памяти в Си (функция malloc)
- •Динамическое выделение памяти в c
- •Выделение памяти в Си (функция malloc)
- •Структуры в языке с
- •Массивы в c (часть 1)
Массивы в c (часть 1)
Массивы — очень полезные сущности, особенно в тех случаях, когда необходимо под одним именем объединить группы переменных одного и того же типа, обращаться к таким переменным можно через целочисленный индекс. Например, всем известная игра крестики нолики может быть представлена в виде массива. Размер поля — 3*3 клетки (левая верхняя ячейка может быть нулевым элементом, а правая нижняя клетка 8-м элементом массива). На самом деле, массивы — это самый эффективный способ для хранения большого количества однотипной информации под одним и тем же названием. Массивы могут иметь абсолютно любой тип данных, включая структуры и классы.
Массив можно представить как набор ячеек:
1 |
[ ][ ][ ][ ][ ][ ][ ][ ][ ][ ]
Каждая пара квадратных скобочек — это элемент массива, в каждом элементе массива можно хранить отдельное значение. Пора приступить к изучению синтаксиса объявления массива:
Мы только что объявили целочисленный массив размером 10 ячеек (мест, в которых значения массива хранятся). Чтобы обратиться к конкретному элементу массива, нужно просто указать имя массива и в квадратных скобках — порядковый номер. Единственно, вам следует знать, что на самом деле первый элемент массива имеет индекс — 0, тогда индекс последнего элемента равен размеру массива минус один. Например, индексы для массива из 10 элементов будут соответствовать этому диапазону: от 0 до 9. Будьте осторожны, никогда не выходите за пределы массива, это может привести к непредсказуемым ошибкам. Но как вы можете применить только что полученные знания? Допустим, вам нужно сохранить строку, но язык Cи не имеет встроенный типа данных для строк, в таком случае мы можем воспользоваться массивом. Например:
Этот код позволит нам объявить массив символов из 10 элементов. И мы сможем сохранить в этот массив данные, которые введет пользователь. Первый входной символ сохраниться в нулевую ячейку, второй символ — в 1-ю ячейку и т. д. Таким образом работать со строками — сравнительно легко, потому что это позволяет работать со строками любой длинны и вся строка хранится в одной переменной. А теперь представьте ситуацию, когда пришлось бы хранить строку не в массиве, а в десятках переменных — это не очень удобно. Кроме того, с массивами очень легко работать в цикле, поочереди перебирая каждый элемент массива:
Давайте посмотрим, что в этой программе происходит. Функция scanf() считывает вводимую пользователем строку. Первый её параметр — %s говорит о том, что необходимо считать строку. Во втором параметре мы не используем амперсанд, так как второй параметр это указатель на первый элемент массива string. То есть имя массива — это указатель на первый элемент массива, запомните это. Так что мы просто передаем массив непосредственно в scanf() без использования амперсанда и это работает прекрасно. Кроме того, обратите внимание, что для доступа к элементу массива, мы просто используем квадратные скобки, внутри которых ставим значение, которое нас интересует. В данном случае, мы перебираем элементы от 0 до 9 и проверяем каждый элемент на равенство символу — a. Обратите внимание, что в данном примере есть один не маловажный момент, если пользователь введет строку размером — более 10 символов, то вся строка не поместится в массив и часть строки будет отброшена. Но вы пока на это не обращайте внимание, более подробно работу строк мы рассмотрим в следующем уроке.
Нарушение границ массива
«Почему вывод программы содержит неправильные значения?» Элементы массива индексируются, начиная с 0, а значит, общая длинна на 1 меньше. Например, если у вас есть десять элементов массива, первый элемент находится в положении нуля, а последний элемент в позиции 9.
В этом коде границы массива уже не нарушаются. # Массивы в C (часть 2): многомерные массивы До этого момента, мы рассматривали только одномерные массивы, то есть, к элементу массива мы обращались через один индекс. Однако, массивы могут быть и двумерными и трехмерными и, даже, n-мерными. Многомерные массивы — это массивы, у которых есть более одного индекса. Вместо одной строки элементов, многомерные массивы можно рассматривать как совокупность элементов, которые распределены по двум или более измерениям. Вот так, например, можно визуализировать двумерный массив:
В этом примере изображен двумерный массив размером 3*5, 3 — строки и 5 столбцов. Объявление двумерного массива почти ничем не отличается от объявления одномерного, за исключением того, что при объявлении двумерного массива, нужно указывать размер каждого измерения в квадратных скобочках. Например, давайте объявим двумерный массив размером 8*8, это размер поля для стандартных шашек — 8 строк и 8 столбцов:
То есть, двумерный массив хорошо подходит для хранения информации на шашечном поле. Также двумерный массив можно легко использовать для хранения информации о любой другой игре — шахматы, крестики нолики, сапер и т. д. Чтобы получить доступ к любому элементу такого массива, нужно воспользоваться двумя значениями — индексами, первый индекс — это номер строки, а второй — номер столбца. Все выше сказанное относится и к n-мерным массивам. Хотя, уже 4-х мерные массивы сложновато визуализировать. Присваивать значения элементам массива очень просто, вот пример:
Пример присваивания значения элемента двумерного массива:
В этом примере мы присвоили значение 10 элементу двумерного массива myArray, который находится во второй строке и в 4-м столбце. Визуально это выглядит так:
Как видите, все просто, главное помните, что нумерация строк и столбцов всегда начинается с 0. То есть, я еще раз хочу вам напомнить, что вы никогда не должны пытаться записать данные после последнего элемента массива, например, когда у вас есть массив размером — 10 элементов и вы пытаетесь присвоить значение элементу с индексом [10]. Память для массива была выделена только для десяти элементов, (индексы от 0 до 9), поэтому элемента с индексом 10 просто не существует. В таком случае, запись в оперативной памяти может привести к непредсказуемым последствиям — например, вы можете в конечном итоге испортить работу параллельно запущенной программы. Однако, как правило, операционная система не позволит такого рода безрассудное поведение и приведет к краху программы, если та попытается получить доступ к нераспределенной памяти. Давайте рассмотрим практический пример использования массивов в программах:
#include <stdio.h>
int main() { int i, j; int myArray[8][8]; // объявляем массив размером 8*8 элементов
for ( i = 0; i < 8; i++ ) { for ( j = 0; j < 8; j++ ) myArray[i][j] = i * j; // каждому элементу присваиваем значение произведения текущих индексов элемента массива }
printf( "Вот такой массив у нас получился:\n" ); for ( i = 0; i < 8; i++ ) { for ( j = 0; j < 8; j++ ) { printf( "[%d][%d]=%d ", i, j, myArray[i][j] ); } printf( "\n" ); } getchar(); }
Сразу смотрим результат работы программы: CppStudio.com Вот такой массив у нас получился: [0][0]=0 [0][1]=0 [0][2]=0 [0][3]=0 [0][4]=0 [0][5]=0 [0][6]=0 [0][7]=0 [1][0]=0 [1][1]=1 [1][2]=2 [1][3]=3 [1][4]=4 [1][5]=5 [1][6]=6 [1][7]=7 [2][0]=0 [2][1]=2 [2][2]=4 [2][3]=6 [2][4]=8 [2][5]=10 [2][6]=12 [2][7]=14 [3][0]=0 [3][1]=3 [3][2]=6 [3][3]=9 [3][4]=12 [3][5]=15 [3][6]=18 [3][7]=21 [4][0]=0 [4][1]=4 [4][2]=8 [4][3]=12 [4][4]=16 [4][5]=20 [4][6]=24 [4][7]=28 [5][0]=0 [5][1]=5 [5][2]=10 [5][3]=15 [5][4]=20 [5][5]=25 [5][6]=30 [5][7]=35 [6][0]=0 [6][1]=6 [6][2]=12 [6][3]=18 [6][4]=24 [6][5]=30 [6][6]=36 [6][7]=42 [7][0]=0 [7][1]=7 [7][2]=14 [7][3]=21 [7][4]=28 [7][5]=35 [7][6]=42 [7][7]=49 В этом примере, мы сначала заполняем двумерный массив произведением его индексов, строки 8 — 11. А потом выводим на экран его содержимое, строки 13 — 20. Если вы хотите объявить указатель на массив, то вы не должны использовать операцию взятия адреса — &, вот пример: 1 char *ptrArray;
2 char myString[10];
3 ptrArray = myString; // указателю присваиваем адрес первого элемента массива myString без использования &
В то время как с обычными переменными, этого сделать нельзя, пример:
На первый взгляд, тот факт, что массивы работают как указатели, может запутать вас, но это специфика языка программирования, это нужно просто запомнить.
# Си-строки На самом деле, си-строки — это всего лишь массивы символов но, со своей спецификой, таким образом, мы всегда знаем, где конец строки. В этой статье мы рассмотрим несколько функций для работы со строками, например, вы — копирование, конкатенация, получить длину строки. Что такое строки? Отметим, что наряду со строками в стиле С, которые, по сути, являются простыми массивами, есть также строковые литералы, такие как этот "literal". В действительности, что строки, что литералы — это просто наборы символов, расположенных рядом в памяти компьютера. Но между массивами и литералами все таки есть разница, литералы нельзя изменять и строки — можно. Любая функция, которая принимает строку в стиле С, также может принимать в качестве параметра — литерал. В си также есть некоторые сущности, которые могут выглядеть как строки, хотя, на самом деле, они таковыми не являются. Я сейчас говорю о символах, они заключены в одинарные кавычки, вот пример — 'а', как видите, это не строка. Символ можно, в определенном месте, присвоить строке, но символы не могут быть обработаны в виде строки. Если вы помните, массивы работают как указатели, поэтому, если вы передаете один символ в строку, это будет считаться ошибкой. Из всего выше сказанного вы должны были понять, что строки — это массивы символов, а строковые литералы — слова, окруженные двойными кавычками. Вот еще один пример литерала:
Вы еще не забыли про специфику строк, которая упоминалась немного выше? Так вот, Си-строки всегда должны завершаться нулевым символом, буквально — '\0'. Поэтому, чтобы объявить строку, состоящую из 49 букв, необходимо зарезервировать дополнительную ячейку под нулевой символ:
ак видно из примера, длинна массива — 50 символов, 49 из которых займет строка и один, последний займет нулевой символ. Важно помнить, что в конце си-строк всегда должен быть нуль-символ, точно так же как и в конце каждого предложения есть точка. Хотя нуль символ не отображается при выводе строки, он все-равно занимает место в памяти. Поэтому, технически, в массиве из пятидесяти элементов вы смогли бы сохранить только 49 букв, потому что, последний символ нужен для завершения строки. Кроме того, указатели также могут быть использованы в качестве строки. Если вы читали статью про выделение памяти в Си, вы можете сделать нечто подобное:
В этом примере мы выделили 64 ячейки в памяти для массива myString. Для высвобождения памяти воспользуйтесь функцией free().
Использование строк Строки полезно использовать тогда, когда вам необходимо выполнять различные операции с текстовой информацией. Например, если вы хотите, чтобы пользователь вводил имя в программу, вы должны использовать строку. Использование функции scanf() для ввода строки — работает, но это может привести к переполнению буфера. Ведь входная строка может оказаться больше, чем размер строки-буфера. Есть несколько способов для решения этой проблемы, но самый простой способ — это использовать fgets() функцию, которая объявлена в заголовочном файле <stdio.h>. Когда fgets() считывает входные данные от пользователя, она будет читать все символы, кроме последнего. После этого в конец считанной строки, fgets() поместит нулевой терминатор. Функция fgets() будет cчитывать символы до тех пор, пока пользователь не нажмет Enter. Давайте посмотрим пример использования fgets():
Первым параметром для fgets() является строка, второй параметр — размер строки и третий параметр — это указатель на входной поток данных. Результат работы программы: CppStudio.com Введите длинную строку: Судьба оставляет свой отпечаток Вы ввели следующую строку: Судьба оставляет свой отпечаток
Для закрытия данного окна нажмите <ВВОД>... Как видите, из вывода программы, во входную строку попал символ новой строки — '\n'. Так случилось из-за того, чтоfgets() считала в строку myString нажатие кнопки Enter и завершила работу. Это означает, что вам может понадобиться вручную удалить символ новой строки. Один из способов сделать это, посимвольный перебор. Давайте доработаем программу и удалим символ новой строки:
Обратите внимание, что если входная строка содержит меньше 100 символов, то в строку попадет и символ новой строки. Поэтому мы можем удалить этот символ, используя простой перебор. В программу мы добавили цикл, в котором перебираем символы строки, строки 12-19. И когда нам встречается символ новой строки, мы его заменяем нулевым символом, строка 16. Результат работы программы: CppStudio.com Введите длинную строку: Судьба оставляет свой отпечаток Вы ввели следующую строку: Судьба оставляет свой отпечаток Для закрытия данного окна нажмите <ВВОД>... На этом пока все. В следующей статье я расскажу вам о специальных функциях для работы со строками.
|