Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции за I семестр конспект Гуревича для C++ B....doc
Скачиваний:
12
Добавлен:
21.11.2018
Размер:
1.3 Mб
Скачать

11.7. Кодирование программы.

Программа в машинных кодах (исполняемый код) – это последовательность команд (инструкций), которые помещаются в памяти и выполняются процессором в указанном порядке. Исполняемый код обычно сохраняется на диске в файлах с определенными расширениями ( .exe, .dll, .com и др.)

Команда размещается в комбинированной ячейке следующим образом. Первый байт содержит код операции (КОП) (например + или – или *), которую необходимо выполнить над содержимым ячеек памяти. Далее в одной, двух или трех ячейках (операндах команды) по 2 или 4 байта содержатся адреса ячеек (А1, А2, А3), над которыми нужно выполнить указанную операцию. Номер первого байта команды называется ее адресом (рис. 11.1).

Рис. 11.1

11.8. Регистры

Заметим, что кроме собственно ОЗУ, в компьютере имеются ячейки памяти, размещенные непосредственно в процессоре. Такие ячейки памяти называются регистрами. В процессоре обычно имеется лишь несколько регистров, каждый из которых содержит один или несколько байт; их точное количество зависит от типа процессора. Регистры не имеют адресов. Регистры обеспечивают наиболее быстрый доступ к данным, так как: 1) обращение к регистру не требует затрат времени на передачу информации из процессора в ОЗУ или наоборот; 2) машинные команды, использующие регистр, вместо длинного адреса ячейки памяти содержат краткое указание соответствующего регистра.

Программист на языке С обычно не может непосредственно обращаться к регистрам. Вместо этого компилятор размещает в регистрах промежуточные выражения, вычисляемые при выполнении различных операторов программы на языке С. Но компилятор может также размещать в регистрах наиболее часто используемые переменные. Такое поведение - один из случаев оптимизирующей компиляции. Настройка параметров оптимизации будет рассмотрена позже.

12. Строки в языке С++

В языке С++ имеются следующие типы данных для работы со строками:

  1. одномерный массив char;

  2. специальный тип string, который в свою очередь существует в разных вариантах, соответствующих различным стандартам:

  1. AnsiString ;

  2. 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 за конец подстроки

}