Лекции (1171139), страница 16
Текст из файла (страница 16)
Поэтому для символьного массива c1 будет выделено 9 байтов, а для символьногомассива str1 – 5 байтов. Обратите внимание на то, что массив letter занимает в памяти 2байта, тогда как символьная переменная symbol – 1 байт. Так как строка представленаодномерным массивом символов, то доступ к каждому ее символу осуществляется самымобычным способом:c1[3] – четвертый символ в массиве c1 (т.е. буква 'D')str1[0] – первый символ в массиве str1 (т.е. цифра '1')Значение любого символа в строковой "переменной" можно изменить во время работыпрограммы:str1[3]='L';Запись за пределы строки может непредсказуемым образом повлиять на последующуюработу программы:str1[4]=5; //Байт с признаком конца строки испорченstr1[5]=6; //Испорчен байт с какими-то даннымиСписок форматных указателей функции scanf предусматривает возможность вводазначений односимвольных (%c) и многосимвольных (%s) переменных:#include <stdio.h>void main(){ char ch1,ch2;char str1[10];scanf("%c %c",&ch1,&ch2);scanf("%s",str1);....................Обратите внимание на то, что для ввода данных в скалярные переменные ch1 и ch2 всписке ввода необходимо указывать их адреса (&ch1, &ch2), а при вводе в массив str1 –достаточно написать его имя.
Дело в том, что имя массива одновременно выполняет рольадреса своего первого элемента. Поэтому str1 и &str1[0] указывают на один и тот жеадрес. Ввод значений символьных переменных ch1 и ch2 можно организовать одним издвух способов. Во-первых, в строке ввода можно набрать два требуемых символа либослитно, либо разделяя их хотя бы одним пробелом и нажать клавишу Enter. Во-вторых,можно набрать первый символ, предназначенный для переменной ch1, и нажать клавишуEnter.
Затем повторить аналогичным образом ввод следующего символа.Некоторые функции работы со строками: для обработки строк, представленныходномерными символьными массивами, в библиотеке системных функций предусмотренодовольно много различных операций. Прототипы этих функций сгруппированы в57заголовочном файле string.h и большинство их названий начинается с префикса str (отstring). Условимся о некоторых обозначениях аргументов и их типах, чтобы не повторятьих в приведенной таблице:••••S, S1,S2 – указатель на символьный массив (как правило, имя массива);CS – указатель типа const char * (т.е.
неизменяемый массив или строковая константа – источникданных);ch – код символа, обычно числовое значение типа int;k – количество символов.strlen(CS)strcpy(S1,CS2)strncpy(S1,CS2,k)strcat(S1,CS2)strncat(S1,CS2,k)strcmp(CS1,CS2)strncmp(CS1,CS2,k)stricmp(CS1,CS2)strchr(CS,ch)strrchr(CS,ch)strstr(CS1,CS2)Возвращает количество символов в строке SКопирует содержимое CS2 в S1, возвращает указатель на S1Копирует первые k символов из CS2 в S1, возвращает указатель наS1Приписывает содержимое CS2 в конец S1, возвращает указатель наS1 (длина массива S1 должна предусматривать такое расширение)Присоединяет первые k символов CS2 к содержимому S1,возвращает указатель на S1Возвращаемое значение равно 0, если CS1=CS2, больше 0, еслиCS1>CS2, и меньше 0, если CS1<CS2Сравниваются только первые k символов строк CS1 и CS2При сравнении игнорируется разница между кодами больших ималых буквСтрока CS сканируется слева направо до обнаружения символа ch.Если он найден, возвращаемый указатель "смотрит" на этот символв строке CS, если такого символа нет, то возвращаемый указательравен null (т.е.
0)Аналогичный поиск с конца строки CS.Поиск первого вхождения строки CS2 в строку CS1. Если поискзавершен успешно, возвращается указатель на первый символнайденной подстроки. В противном случае возвращается nullРабота с памятьюВ традиционных языках программирования, таких как Си, Фортран, Паскаль,существуют три вида памяти: статическая, стековая и динамическая. Конечно, сфизической точки зрения никаких различных видов памяти нет: оперативная память - этомассив байтов, каждый байт имеет адрес, начиная с нуля. Когда говорится о видах памяти,имеются в виду способы организации работы с ней, включая выделение и освобождениепамяти, а также методы доступа.Статическая памятьСтатическая память выделяется еще до начала работы программы, на стадии компиляциии сборки.
Статические переменные имеют фиксированный адрес, известный до запускапрограммы и не изменяющийся в процессе ее работы. Статические переменные создаютсяи инициализируются до входа в функцию main, с которой начинается выполнениепрограммы.Существует два типа статических переменных:глобальные переменные - это переменные, определенные вне функций, в описании которых отсутствуетслово static. Обычно описания глобальных переменных, включающие слово extern, выносятся взаголовочные файлы (h-файлы).
Слово extern означает, что переменная описывается, но не создается вданной точке программы. Определения глобальных переменных, т.е. описания без слова extern, помещаютсяв файлы реализации (c-файлы или cpp-файлы).статические переменные - это переменные, в описании которых присутствует слово static. Как правило,статические переменные описываются вне функций. Такие статические переменные во всем подобныглобальным, с одним исключением: область видимости статической переменной ограничена одним файлом,внутри которого она определена, - и, более того, ее можно использовать только после ее описания, т.е. нижепо тексту. По этой причине описания статических переменных обычно выносятся в начало файла.
В отличие58от глобальных переменных, статические переменные никогда не описываются в h-файлах (модификаторыextern и static конфликтуют между собой).Стековая, или локальная, памятьЛокальные, или стековые, переменные - это переменные, описанные внутрифункции. Память для таких переменных выделяется в аппаратном стеке.
Памятьвыделяется в момент входа в функцию или блок и освобождается в момент выхода изфункции или блока. При этом захват и освобождение памяти происходят практическимгновенно, т.к. компьютер только изменяет регистр, содержащий адрес вершины стека.Локальные переменные можно использовать при рекурсии, поскольку при повторномвходе в функцию в стеке создается новый набор локальных переменных, а предыдущийнабор не разрушается. Всегда следует избегать использования глобальных и статическихпеременных, если можно обойтись локальными.Недостатки локальных переменных являются продожением их достоинств.Локальные переменные создаются при входе в функцию и исчезают после выхода из нее,поэтому их нельзя использовать в качестве данных, разделяемых между несколькимифункциями.
К тому же, размер аппаратного стека не бесконечен, стек может в одинпрекрасный момент переполниться (например, при глубокой рекурсии), что приведет ккатастрофическому завершению программы. Поэтому локальные переменные не должныиметь большого размера. В частности, нельзя использовать большие массивы в качествелокальных переменных.Динамическая память, или куча.Помимо статической и стековой памяти, существует еще практическинеограниченный ресурс памяти, которая называется динамическая, или куча (heap).Программа может захватывать участки динамической памяти нужного размера. Послеиспользования ранее захваченный участок динамической памяти следует освободить.Под динамическую память отводится пространство виртуальной памяти процесса междустатической памятью и стеком.
Обычно стек располагается в старших адресахвиртуальной памяти и растет в сторону уменьшения адресов. Программа и константныеданные размещаются в младших адресах, выше располагаются статические переменные.Структура динамической памяти автоматически поддерживается исполняющей системойязыка Си или C++. Динамическая память состоит из захваченных и свободных сегментов,каждому из которых предшествует описатель сегмента.
При выполнении запроса назахват памяти исполняющая система производит поиск свободного сегмента достаточногоразмера и захватывает в нем отрезок требуемой длины. При освобождении сегментапамяти он помечается как свободный, при необходимости несколько подряд идущихсвободных сегментов объединяются.В языке Си для захвата и освобождения динамической памяти применяютсястандартные функции malloc и free, описания их прототипов содержатся в стандартномзаголовочном файле "stdlib.h". (Имя malloc является сокращением от memory allocate "захват памяти".) Прототипы этих функций выглядят следующим образом:void *malloc(size_t n); // Захватить участок памяти// размером в n байтvoid free(void *p); // Освободить участок// памяти с адресом pЗдесь n - это размер захватываемого участка в байтах, size_t - имя одного изцелочисленных типов, определяющих максимальный размер захватываемого участка.Функция malloc возвращает адрес захваченного участка памяти или ноль в случае неудачи(когда нет свободного участка достаточно большого размера).
Функция free освобождаетучасток памяти с заданным адресом. Для задания адреса используется указатель общеготипа void*. После вызова функции malloc его необходимо привести к указателю на59конкретный тип, используя операцию приведения типа. Например, в следующемпримере захватывается участок динамической памяти размером в 4000 байтов, его адресприсваивается указателю на массив из 1000 целых чисел:int *a;// Указатель на массив целых чисел.
. .a = (int *) malloc(1000 * sizeof(int));Выражение в аргументе функции malloc равно 4000, поскольку размер целого числаsizeof(int) равен четырем байтам. Для преобразования указателя используется операцияприведения типа (int *) от указателя обобщенного типа к указателю на целое число.Пример: печать n первых простых чисел.Рассмотрим пример, использующий захват динамической памяти. Требуется ввести целоецисло n и напечатать n первых простых чисел.
(Простое число - это число, у которого нетнетривиальных делителей.) Используем следующий алгоритм: последовательнопроверяем все нечетные числа, начиная с тройки (двойку рассматриваем отдельно). Делимочередное число на все простые числа, найденные на предыдущих шагах алгоритма и непревосходящие квадратного корня из проверяемого числа. Если оно не делится ни на одноиз этих простых чисел, то само является простым; оно печатается и добавляется в массивнайденных простых.Поскольку требуемое количество простых чисел n до начала работы программынеизвестно, невозможно создать массив для их хранения в статической памяти.