Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву
Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Строки как одномерные массивы данных типа charСодержание книги
Похожие статьи вашей тематики
Поиск на нашем сайте В языке Си отдельного типа данных «строка символов» нет. Работа со строками реализована путем использования одномерных массивов типа char, т.е. строка символов – это одномерный массив символов, заканчивающийся нулевым байтом. Нулевой байт – это байт, каждый бит которого равен нулю, при этом для нулевого байта определена символьная константа ´\0´ (признак окончания строки, или «нуль-символ»). Поэтому если строка должна содержать k символов, то в описании массива размер должен быть k +1. По положению нуль-символа определяется фактическая длина строки. Например, char s [7]; – означает, что строка может содержать не более шести символов, а последний байт отводится под нуль-символ. Отсутствие нуль-символа и выход указателя при просмотре строки за ее пределы – распространенная ошибка при работе со строками. Строку можно инициализировать строковой константой (строковым литералом), которая представляет собой набор символов, заключенных в двойные кавычки. Например: сhar S[ ] = “Работа со строками”; для данной строки выделено и заполнено 19 байт – 18 на символы и 19-й на нуль-символ. В конце строковой константы явно указывать символ ´\0´ не нужно. Компилятор добавит его автоматически. Символ ´\0´ нужно использовать явно тогда, когда символьный массив при декларации инициализируется списком начальных значений, например, следующим образом: char str[10] ={‘V’, ‘a’, ‘s’, ‘j’, ‘а’, ‘\0’}; или когда строка формируется посимвольно в коде программы. Пример такого формирования приведен в конце этого раздела. При работе со строками можно пользоваться указателями, например: char *x; x = "БГУИР"; x = (i>0)? "положительное": (i<0)? "отрицательное": "нулевое"; Такая декларация строки – единственный случай, когда в коде программы можно использовать операцию присваивания явно. Операция char *str = "БГУИР" создает не строковую переменную, а указатель на строковую константу, изменить которую невозможно, причем это касается не только адреса ОП, но и его размера. Знак равенства перед строковым литералом означает инициализацию, а не присваивание. Операция присваивания одной строки другой в языке Си не определена (поскольку строка является массивом) и может обрабатываться при помощи оператора цикла (с использованием стандартной библиотечной функций). Процесс копирования строки s 1 в строку s 2 имеет вид char s1[25], s2[25]; for (int i = 0; i <= strlen(s1); i++) s2[i] = s1[i]; Длина строки определяется с помощью стандартной функции strlen, которая вычисляет длину, выполняя поиск нуль-символа (прототип функции приведен ниже). Таким образом, строка фактически просматривается дважды. А вот следующие действия будут ошибочными: сhar s1[51]; s1 = ”Minsk”; Это связано с тем, что s 1 – константный указатель и не может использоваться в левой части операции присваивания. Большинство действий со строковыми объектами в Си выполняются при помощи стандартных библиотечных функций, так, для правильного выполнения операции присваивания в последнем примере необходимо использовать стандартную функцию strcpy (s1, ”Minsk”); Напомним, что для ввода строк, как и для других объектов программы, обычно используются две стандартные функции: Функция scanf вводит значения для строковых переменных при помощи формата (спецификатора ввода) % s до появления первого символа “пробел” (символ «&» перед ID строковых данных указывать не надо); Функция gets осуществляет ввод строки, которая может содержать пробелы. Завершается ввод нажатием клавиши Enter. Обе функции автоматически ставят в конец строки нулевой байт. Вывод строк производится функциями printf или puts до нулевого байта. Функция printf не переводит курсор после вывода на начало новой строки, а puts автоматически переводит курсор после вывода строковой информации в начало новой строки. Например: char Str[30]; printf(“ Введите строку без пробелов: \n”); scanf(“%s”, Str); или puts(“ Введите строку ”); gets(Str); Остальные операции над строками, как уже отмечалось ранее, выполняются с использованием стандартных библиотечных функций, декларация прототипов которых находятся в файле string.h. Приведем наиболее часто используемые стандартные строковые функции. Функция strlen (S) возвращает длину строки (количество символов в строке), при этом завершающий нулевой байт не учитывается, например: char *S1 = ”Минск!\0”, S2[] = ”БГУИР–Ура!”; printf(“ %d, %d.”, strlen(S1), strlen(S2)); Результат выполнения данного участка программы: 6, 10. Функция strcpy (S 1, S 2) – копирует содержимое строки S 2 в строку S 1. Функция strcat (S 1, S 2) – присоединяет строку S 2 к строке S 1 и помещает ее в массив, где находилась строка S 1, при этом строка S 2 не изменяется. Нулевой байт, который завершал строку S 1, заменяется первым символом строки S2. Функция int strcmp (S 1, S 2) сравнивает строки S 1 и S 2 и возвращает значение <0, если S 1< S 2; >0, если S 1> S 2; =0, если строки равны, т.е. содержат одно и то же число одинаковых символов. Функции преобразования строковых объектов в числовые описаны в библиотеке stdlib. h. Рассмотрим некоторые из них. Преобразование строки S в число: – целое: int atoi (S); – длинное целое: long atol (S); – действительное: double atof (S); при возникновении ошибки данные функции возвращают значение 0. Функции преобразования числа V в строку S: – целое: itoa (V, S, kod); – длинное целое: ltoa (V, S, kod); 2 £ kod £ 36, для десятичных чисел со знаком kod = 10. Пример участка кода программы, в котором из строки s удаляется символ, значение которого содержится в переменной с каждый раз, когда он встречается char s[81], c; ... for(i = j = 0; s[i]!= '\0'; i++) if(s[i]!= c) s[j++] = s[i]; s[j]='\0'; ... __________________________________________________________________ В режиме консольных приложений в среде Visual C++ 6.0 вывод символов русского языка сопряжен с определенными неудобствами. Разрешение данной проблемы рассматривается в разд. 16.3. __________________________________________________________________ Указатели на указатели Указатели, как и переменные любого другого типа, могут объединяться в массивы. Объявление массива указателей на целые числа имеет вид int *a[10], y; Теперь каждому из элементов массива указателей a можно присвоить адрес целочисленной переменной y, например: a [1]=& y; Чтобы теперь найти значение переменной y через данный элемент массива а, необходимо записать * a [1]. В языке Си можно описать переменную типа «указатель на указатель». Это ячейка оперативной памяти (переменная), в которой будет храниться адрес указателя на некоторую переменную. Признак такого типа данных – повторение символа «*» перед идентификатором переменной. Количество символов «*» определяет уровень вложенности указателей друг в друга. При объявлении указателей на указатели возможна их одновременная инициализация. Например: int a=5; int *p1=&a; int **pp1=&p1; int ***ppp1=&pp1; Если присвоить переменной а новое значение, например 10, то одинаковые результаты будут получены в следующих операциях: a=10; *p1=10; **pp1=10; ***ppp1=10; Для доступа к области ОП, отведенной под переменную а, можно использовать и индексы. Эквивалентны следующие выражения: *p1 ~ p1[0]; **pp1 ~ pp1[0][0]; ***ppp1 ~ ppp1[0][0][0]. Фактически, используя указатели на указатели, мы имеем дело с многомерными массивами.
Многомерные массивы Декларация многомерного массива имеет следующий формат: тип ID [ размер 1][ размер 2]…[ размерN ] = { { список начальных значений }, { список начальных значений }, … }; Списки начальных значений – атрибут необязательный. Наиболее быстро изменяется последний индекс элементов массива, поскольку многомерные массивы в языке Си размещаются в памяти компьютера построчно друг за другом (см. следующую тему «Адресная функция»). Рассмотрим особенности работы с многомерными массивами на конкретном примере двухмерного массива. Например, пусть приведена следующая декларация двухмерного массива: int m [3][4]; Идентификатор двухмерного массива – это указатель на массив указателей (переменная типа указатель на указатель: int ** m;). Поэтому двухмерный массив m [3][4]; компилятор рассматривает как массив трех указателей, каждый из которых указывает на начало массива со значениями размером по четыре элемента каждый. В ОП данный массив будет расположен следующим образом:
(А) (В) Рис. 10.1. Схема размещения элементов массива m размером 3×4
Причем в данном случае указатель m [1] будет иметь адрес m [0]+4* sizeof (int), т.е. каждый первый элемент следующей строки располагается за последним элементом предыдущей строки. Приведем пример программы конструирования массива массивов: #include <stdio.h> void main() { int x0[4] = { 1, 2, 3,4}; // Декларация и инициализация int x1[4] = {11,12,13,14}; // одномерных массивов int x2[4] = {21,22,23,24}; int *m[3] = {x0, x1, x2,}; // Создание массива указателей int i,j; for (i=0; i<3; i++) { printf("\n Cтрока %d) ", i+1); for (j=0; j<4; j++) printf("%3d", m[ i ] [ j ]); } }
Результаты работы программы: Cтрока 1) 1 2 3 4 Cтрока 2) 11 12 13 14 Cтрока 3) 21 22 23 24
Такие же результаты будут получены и в следующей программе: #include <stdio.h> void main() { int i, j; int m[3][4] = { { 1, 2, 3, 4}, {11,12,13,14}, {21,22,23,24} }; for (i=0; i<3; i++) { printf("\n %2d)", i+1); for (j=0; j<4; j++) printf(" %3d",m[ i ] [ j ]); } } В последней программе массив указателей на соответствующие массивы элементов создается компилятором автоматически, т.е. данные массива располагаются в памяти последовательно по строкам, что является основанием для декларации массива m в виде int m[3][4] = {1, 2, 3, 4, 11, 12, 13, 14, 21, 22, 23, 24}; Замена скобочного выражения m [3][4] на m [12] здесь не допускается, так как массив указателей не будет создан. Таким образом, использование многомерных массивов в языке Си связано с расходами памяти на создание массивов указателей. Очевидна и схема размещения такого массива в памяти – последовательное (друг за другом) размещение «строк» – одномерных массивов со значениями (векторная организация памяти). Обращению к элементам массива при помощи операции индексации m [ i ][ j ] соответствует эквивалентное выражение, использующее адресную арифметику – *(*(m + i)+ j). Аналогичным образом можно установить соответствие между указателями и массивами с произвольным числом измерений.
Адресная функция Векторная память поддерживается почти всеми языками высокого уровня и предназначена для хранения массивов различной размерности и различных размеров. Каждому массиву выделяется непрерывный участок памяти указанного размера. При этом элементы, например, двухмерного массива X размерностью n 1´ n 2 размещаются в ОП в следующей последовательности: Х (0,0), Х (0,1), Х (0,2),... Х (0, n 2–1),..., Х (1,0), Х (1,1), Х (1,2),... Х (1, n 2–1),..., Х (n 1–1,0), Х (n 1–1,1), Х (n 1–1,2),..., Х (n 1–1, n 2–1). Адресация элементов массива определяется некоторой адресной функцией, связывающей адрес и индексы элемента. Пример адресной функции для массива Х: K (i, j) = n 2* i + j; где i = 0,1,2,...,(n 1–1); j = 0,1,2,...,(n 2–1); j – изменяется в первую очередь. Адресная функция двухмерного массива A (n, m) будет выглядеть так: N 1 = K (i, j) = m * i + j, i =0,1,..., n –1; j =0,1,..., m –1. Тогда справедливо следующее: A (i, j) «B (K (i, j)) = B (N 1), B – одномерный массив с размером N 1 = n * m. Например, для двухмерного массива A (2,3) имеем:
Проведем расчеты: i = 0, j = 0 N 1 = 3*0+0 = 0 B (0) i = 0, j = 1 N 1 = 3*0+1 = 1 B (1) i = 0, j = 2 N 1 = 3*0+2 = 2 B (2) i = 1, j = 0 N 1 = 3*1+0 = 3 B (3) i = 1, j = 1 N 1 = 3*1+1 = 4 B (4) i = 1, j = 2 N 1 = 3*1+2 = 5 B (5)
Аналогично получаем адресную функцию для трехмерного массива Х (n 1, n 2, n 3): K (i, j, k) = n 3* n 2* i + n 2* j + k, где i = 0,1,2,...,(n 1–1); j = 0,1,2,...,(n 2–1);); k = 0,1,2,...,(n 3–1); значение k – изменяется в первую очередь. Для размещения такого массива потребуется участок ОП размером (n 1* n 2* n 3)* sizeof (type). Рассматривая такую область как одномерный массив Y (0,1,..., n 1* n 2* n 3), можно установить соответствие между элементом трехмерного массива X и элементом одномерного массива Y: X (i, j, k) «Y (K (i, j, k)). Необходимость введения адресных функций возникает лишь в случаях, когда требуется изменить способ отображения с учетом особенностей конкретной задачи.
|
||||||||||||||||||||||||||||||||||
|
Последнее изменение этой страницы: 2016-04-19; просмотров: 1348; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.217.39 (0.012 с.) |