- •Структура программы на языке Си. Этапы выполнения программы
- •1.1. Алфавит языка Си
- •1.2. Лексемы
- •1.3. Идентификаторы и ключевые слова
- •1.4. Знаки операций
- •1.5. Литералы (константы)
- •1.6. Комментарии
- •1.7. Общая структура программы на языке Си.
- •1.8. Функциональная и модульная декомпозиции
- •1.9. Этапы обработки программы.
- •1.10. Роль препроцессора.
- •1.11. Ошибки
- •2. Переменные и константы. Типы данных
- •2.1. Основные типы данных
- •2.2. Декларация (объявление) объектов
- •2.3. Константы в программах
- •2.4. Целочисленные константы
- •2.5. Константы вещественного типа
- •2.6. Символьные константы
- •2.7. Строковые константы
- •3. Обзор операций
- •3.1. Операции, выражения
- •3.2. Арифметические операции
- •3.3. Операции сравнения
- •3.4. Логические операции
- •4. Операции (продолжение).
- •4.1. Операция присваивания
- •Примеры недопустимых выражений:
- •4.2. Сокращенная запись операции присваивания
- •4.3. Преобразование типов операндов бинарных операций
- •4.4. Преобразование типов при присваивании.
- •4.5. Операция явного приведения типа
- •4.6. Операция «,» (запятая)
- •4.7. Приоритеты операций (по убыванию, чертой разделены разные приоритеты)
- •5. Стандартная библиотека языка Си
- •5.1. Стандартные математические функции
- •5.2. Потоковый ввод-вывод
- •5.3. Консольные функции вывода данных на экран
- •5.4. Консольные функции ввода информации
- •5.5. Ввод-вывод в оконных приложениях.
- •Советы по программированию
- •6. Операторы языка с.
- •7. Составление разветвляющихся алгоритмов
- •7.1. Условные операторы
- •If (выражение) оператор;
- •If (выражение) оператор 1 ;
- •If (выражение 1) оператор 1;
- •If (выражение 2) оператор 2;
- •If (выражение 3) оператор 3;
- •7.2. Оператор выбора альтернатив (переключатель)
- •7.3. Условная операция «? :»
- •8. Составление циклических алгоритмов
- •8.1. Оператор с предусловием while
- •8.2. Оператор цикла с постусловием do – while
- •8.3. Оператор цикла с предусловием и коррекцией for
- •8.4. Вложенные циклы.
- •9. Операторы передачи управления.
- •9.1. Оператор безусловного перехода goto
- •9.2. Операторы continue, break и return
- •10. Массивы
- •10.1. Одномерные массивы
- •10.2. Примеры алгоритмов, использующих одномерные массивы.
- •10.3. Многомерные массивы
- •10.4. Примеры алгоритмов, использующих двумерные массивы.
- •10.5. Компонента StringGrid
- •11. Размещение данных и программ в памяти пэвм
- •11.1. Общие понятия.
- •11.2. Кодирование целых чисел.
- •11.3. Кодирование вещественных чисел.
- •11.4. Кодирование символов.
- •Стандартная часть таблицы символов (ascii)
- •Дополнительная часть таблицы символов
- •11.5. Операция sizeof
- •11.6. Побитовые логические операции. Операции над битами
- •11.7. Кодирование программы.
- •11.8. Регистры
- •12.1. Строки как нуль-терминированные массивы char.
- •12.2. Русификация консольных приложений.
- •12.3. Строки как переменные типа AnsiString.
- •12.4. Преобразования строковых типов.
- •12.5. Тип String в консольных приложениях.
- •13. Функции пользователя и классы памяти.
- •13.1. Сущность и предназначение функций.
- •13.2. Определение и вызов функции.
- •13.3. Прототип функции.
- •13.4. Область видимости.
- •13.5. Классы памяти объектов в языке Cи.
- •13.6. Разбиение программы на модули.
- •15. Генерация псевдослучайных чисел.
- •17. Указатели
- •17.1. Определение указателей
- •17.2. Связь указателей и массивов.
- •17.3. Операции над указателями (косвенная адресация)
- •17.4. Сравнение указателей
- •17.5. Массивы указателей.
- •17.6. Указатели на указатели.
- •17.7 . Указатели как параметры функций.
- •Void f1(int, const double *);
- •17.9. Ссылка
- •17.10. Указатели на функции
- •Id_функции(список аргументов);
- •18. Работа с динамической памятью
- •18.1. Динамическое выделение и освобождение памяти.
- •18.2. Создание одномерного динамического массива.
- •18.3. Создание двуxмерного динамического массива.
- •19. Операция typedef
- •20. Отладка и пошаговое выполнение программы
11.7. Кодирование программы.
Программа в машинных кодах (исполняемый код) – это последовательность команд (инструкций), которые помещаются в памяти и выполняются процессором в указанном порядке. Исполняемый код обычно сохраняется на диске в файлах с определенными расширениями ( .exe, .dll, .com и др.)
Команда размещается в комбинированной ячейке следующим образом. Первый байт содержит код операции (КОП) (например + или – или *), которую необходимо выполнить над содержимым ячеек памяти. Далее в одной, двух или трех ячейках (операндах команды) по 2 или 4 байта содержатся адреса ячеек (А1, А2, А3), над которыми нужно выполнить указанную операцию. Номер первого байта команды называется ее адресом (рис. 11.1).
Рис. 11.1
11.8. Регистры
Заметим, что кроме собственно ОЗУ, в компьютере имеются ячейки памяти, размещенные непосредственно в процессоре. Такие ячейки памяти называются регистрами. В процессоре обычно имеется лишь несколько регистров, каждый из которых содержит один или несколько байт; их точное количество зависит от типа процессора. Регистры не имеют адресов. Регистры обеспечивают наиболее быстрый доступ к данным, так как: 1) обращение к регистру не требует затрат времени на передачу информации из процессора в ОЗУ или наоборот; 2) машинные команды, использующие регистр, вместо длинного адреса ячейки памяти содержат краткое указание соответствующего регистра.
Программист на языке С обычно не может непосредственно обращаться к регистрам. Вместо этого компилятор размещает в регистрах промежуточные выражения, вычисляемые при выполнении различных операторов программы на языке С. Но компилятор может также размещать в регистрах наиболее часто используемые переменные. Такое поведение - один из случаев оптимизирующей компиляции. Настройка параметров оптимизации будет рассмотрена позже.
12. Строки в языке С++
В языке С++ имеются следующие типы данных для работы со строками:
одномерный массив char;
специальный тип string, который в свою очередь существует в разных вариантах, соответствующих различным стандартам:
AnsiString ;
string из библиотеки STL.
Все перечисленные типы поддерживаются компилятором C++ Builder'а.
12.1. Строки как нуль-терминированные массивы char.
а) Основные понятия.
Один из способов организовать работу со строками - это использование одномерных массивов типа char. Тогда строка символов – это одномерный массив типа char, заканчивающийся нулевым байтом.
Нулевой байт – это байт, каждый бит которого равен нулю, при этом для нулевого байта определена символьная константа ´\0´ (признак окончания строки, или нуль-терминатор). По нулевому байту функции, работающие со строками, определяют место окончания строки. Если они читают строку, то воспринимают ее только до первого нуль-терминатора; если они создают строку, то записывают нуль-терминатор в ее конец.
Поэтому, если строка должна содержать максимум k символов, то в описании массива необходимо указать k+1 элемент. Например, char a[7]; - означает, что строка может содержать от 0 до 6 символов, а один байт будет занимать нуль-терминатор.
Строковая константа – это набор символов, заключенных в двойные кавычки, например: "Работа со строками". Такие константы хранятся именно как массивы типа char. В конце строковой константы явно указывать символ ´\0´ не нужно (он будет добавлен автоматически в ходе компиляции).
Строковые константы можно использовать при инициализации массивов:
сhar S[]="Работа со строками";
В этом примере размер массива не указан, поэтому он будет определен автоматически (19 элементов).
б) Ввод-вывод строк - массивов char.
Для ввода с консоли строк - массивов char обычно используются две стандартные функции:
scanf() (см. тему "Функции ввода-вывода"; спецификатор ввода %s; символ «&» перед именем массива типа char указывать не надо). Значения вводятся до появления первого символа “пробел”, а если его нет - до конца строки;
gets(char *S) (запись "char *S" означает, что на ее место нужно подставить массив типа char; можно также подставить указатель на char - см. тему "Указатели"). Значения вводятся до конца вводимой строки (т.е., в отличие от scanf(), может вводить строку с пробелами как единое целое).
Обе функции автоматически ставят в конец строки нулевой байт.
Вывод строк производится функциями printf() или puts() до первого нулевого байта (‘\0’):
printf() (см. тему "Функции ввода-вывода"; спецификатор вывода %s) не переводит курсор после вывода на начало новой строки;
puts(char *S) автоматически переводит курсор после вывода строковой информации в начало новой строки.
Например:
char Str[30];
printf("Введите строку без пробелов : \n");
scanf("%s",Str);
или
puts("Введите строку");
gets(Str);
В оконных приложениях C++ Builder'а изображаемый на экране текст, как правило, имеет тип String (или при необходимости автоматически преобразуется в String). Для вывода туда строки - массива char обычно достаточно просто подставить ее имя в функцию вывода:
Edit1->Text=Str;
Но для ввода оттуда такой строки необходимо использовать преобразование типов и копирование (объяснение см. ниже):
strcpy(Str, Edit1->Text.c_str());
в) Поэлементная работа со строками.
В языке С не допускается ни присваивание, ни сравнение массивов. Операции над строками могут быть выполнены либо непосредственно действиями над отдельными символами (как над элементами массива, имеющими тип char), либо с использованием стандартных библиотечных функций.
Пример поэлементной работы со строкой - массивом char :
Задача 1. В строке заменить все пробелы на символы подчеркивания.
#include<stdio.h>
#include<conio.h>
void main(){
char s[50];
int i;
puts("Vvedite stroku s probelami:");
gets(s);
for(i=0; s[i]; i++) // poka s[i] != nul-terminatoru
if (s[i]==' ') s[i]='_';
puts(s);
getch();
}
г) Копирование, сцепление (конкатенация), сравнение, поиск строк - массивов char.
Описание прототипов стандартных функций работы со строками - массивами char находится в файле string.h.
Рассмотрим наиболее часто используемые функции (перед именем функции указывается тип ее результата, в скобках - количество и типы параметров).
Функция int strlen(char *S) возвращает длину строки (количество символов в строке), при этом завершающий нулевой байт не учитывается.
Пример:
char S1[]="Минск!\0", S2[90]="БГУИР-Ура!";
printf("%d, %d .", strlen(S1), strlen(S2));
Результат выполнения данного участка программы:
6 , 10 .
Функция strcpy(char *S1, char *S2) - копирует содержимое строки S2 в строку S1. Она заменяет операцию присваивания, непригодную для массивов в языке С.
Пример:
char s[50], t[30];
strcpy(t, "Hello!");
strcpy(s,t);
Функция strcat(char *S1, char *S2) - присоединяет строку S2 к концу строки S1 и помещает ее в массив, где находилась строка S1, при этом строка S2 не изменяется. Нулевой байт, который завершал строку S1, заменяется первым символом строки S2. (Такая операция называется также конкатенацией или сцеплением строк).
Пример:
char a[20], b[10];
strcpy(a, "Hello,");
strcpy(b, "world!");
strcat(a,b);
puts(a);
Результат выполнения данного участка программы:
Hello,world!
Функция int strcmp(char *S1, char *S2) сравнивает строки S1 и S2 . Результат функции - целое число, меньшее 0, если значение S1 предшествует значению S2 в "алфавитном" порядке; большее 0, если S2 предшествует S1; равное 0, если строки равны, т.е. содержат одно и то же число одинаковых символов.
Под "алфавитным порядком" здесь понимается порядок возрастания кодов символов (см. тему "Кодирование символов", кодовые таблицы). Как можно видеть из этих таблиц, этот порядок совпадает с алфавитным для латинских букв одинакового регистра, а также для русских букв одинакового регистра (кроме буквы 'ё'). Если первые символы S1 и S2 совпадают, то учитываются следующие символы и т.д., по обычным алфавитным правилам.
Функция int stricmp(char *S1, char *S2) делает то же, что и strcmp, но нечувствительна к регистру букв (например, 'D' и 'd' для нее - одинаковые символы).
Пример:
char a[20], b[10];
gets(a);
gets(b);
if (!strcmp(a,b))
puts("a==b!");
else
if (strcmp(a,b)<0)
puts("a<b!");
else puts("b<a!");
Для a="Москва", b="Могилев" результат будет:
b<a!
Функции:
strncpy(char *S1, char *S2, int max);
strncat(char *S1, char *S2, int max);
int strncmp(char *S1, char *S2, int max);
int strnicmp(char *S1, char *S2, int max);
делают то же, что и соответствующие им функции strcpy, strcat, strcmp, stricmp, но, если длина строки S2 превышает max, то они используют только первые max ее символов. В этом случае нуль-терминатор в конце полученной строки может не быть поставлен автоматически.
Существует также функция для поиска заданной подстроки в заданной строке (подстрокой называется подряд идущая часть строки):
char * strstr(char *S1, char *S2);
Здесь S1 - строка, в которой будет проводиться поиск; S2 - искомая подстрока. Результатом функции является указатель (см. тему "Указатели") на первое (считая слева направо) местоположение подстроки S2 в строке S1, либо NULL, если S2 не встречается в S1.
д) Перевод строк - массивов char в числа и наоборот.
Функции преобразования строки S в число:
целое: int atoi(char *S);
длинное целое: long atol(char *S);
действительное: double atof(char *S);
При ошибке данные функции возвращают значение 0.
Пример:
char s[]="120"; int i;
i=atoi(s); // i=120
Функции преобразования числа V в строку S:
целое: itoa(int V, char *S, int kod);
длинное целое: ltoa(long V, char *S, int kod);
Здесь kod - основание системы счисления, в которой должно быть записано полученное число: 2 kod 36, для десятичных чисел kod=10.
Пример:
char s[10]; int i=120;
itoa(i, s, 10);
Вместо этих функций, для преобразования чисел в строку и строки в числа можно использовать также функции:
sscanf(char *S, …) ;
sprintf(char *S, …) ;
Они во всем подобны функциям scanf и printf, но после их открывающей скобки вначале указывается имя строки, из которой "читаются" (sscanf) или в которую "записываются" (sprintf) значения совершенно так же, как они вводятся с клавиатуры функцией scanf или выводятся на экран функцией printf.
В отличие от функции atoi, sscanf позволяет отличить ошибку ввода от нулевого результата (см. про scanf в теме "Функции ввода-вывода"), например:
char s[20]; int i;
...
if (! sscanf(s, "%d", &i)) puts("Nevernoe chislo!");
е) Примеры работы со строками - массивами char.
Задача 2. Удалить символ "с" из строки s каждый раз, когда он встречается.
int i,j;
for( i=j=0; s[i]; i++)
if( s[i]!=c)
s[j++]=s[i];
s[j]='\0';
Задача 3. Если строка начинается со слова "song" (неважно, в каком регистре), добавить в начало и конец ее восклицательные знаки.
char s[50],t[52];
int i;
...
if (!strnicmp(s, "song", 4)) {
strcpy(t,"!");
strcat(t, s);
strcat(t, "!");
strcpy(s,t);
}
Если s было равно "Song 1", то оно станет равно "!Song 1!".
Другой вариант решения этой же задачи:
if (!strnicmp(s, "song", 4)) {
sprintf(t,"!%s!",s);
strcpy(s,t);
}
Задача 4. Из строки выделить и вывести подстроку, содержащую запись вещественного числа без знака с фиксированной точкой. Если таких подстрок несколько, вывести их все.
n=strlen(s);
for (i=0; i<n; i++)
if (s[i]=='.'){
// Выделение целой части вещественного числа
for (j=i-1; j>=0; j--)
if (s[j]<'0' || s[j]>'9') break;
//(Вначале была проверка j>=0, т.к. иначе s[j] недопустимо)
// Выделение дробной части вещественного числа
for (k=i+1; s[k]>='0' && s[k]<='9'; k++);
//(Незачем проверять на k<n - при k=n s[k]=0, а 0 <'0')
if (k==i+1 && j==i-1) //Если ни целой, ни дробной части
continue; // то переходим к следующей итерации for(i)
for (j++, p=0; j<k; j++,p++)
t[p]=s[j]; // Копируем в t подстроку от j+1 до k-1
t[p]=0; // Ставим нуль-терминатор
puts(t);
i=k; // Смещаем i за конец подстроки
}