Символьные строки и символьные массивы.
В С++ нет строковых переменных, но так как строка представляет собой последовательность нескольких символов, то символьный массив удобно использовать для размещения строк. Например:
char name [ ]=”МИША ИВАНОВ”;
Такой символьный массив располагается в памяти следующим образом:
name[0] | М | В конце каждой строки имеется null символ (ограничитель строки), на который специально |
name[1] | И | резервируется память. Содержимое строки |
name[2] | Ш | можно вывести на экран, используя оператор |
name[3] | А | cout<<name; |
name[4] | И | заметим, что имя массива указывается без |
name[5] | В | квадратных скобок. Если инициализировать |
name[6] | А | char cat[]=”Lucy”; |
name[7] | Н | то С++ сам посчитает размерность массива и |
name[8] | О | учтёт двоичный нуль. |
name[9] | В | Без двоичного нуля эта конструкция не будет. |
name[10] | \0 | интерпретирована компилятором как строка |
Фундаментальное правило С++: строки хранятся в символьных массивах, но не все символьные массивы содержат строки.
char carat1[4]={‘a’,’b’,’c’,’d’};
char carat2[ ]=”Good”;
Массив carat1 является символьным, поскольку не содержит двоичного нуля в конце, а массив carat2 является строкой, так как компилятор сам определяет по кавычкам, что это строка и ставит в конце двоичный нуль. При расчёте длины строки двоичный нуль никогда не учитывается, хотя в памяти для него место резервируется.
Инициализация символьного массива.
Библиотечная функция strcpy() позволяет копировать строковые константы в массив.
Инструкция strcpy(petname,”Bumsi”); копирует строку Bumsi в массив petname. Для использования этой функции необходимо включать в программу заголовочный файл string.h. Далее приведена программа, иллюстрирующая способы инициализации символьного массива.
#include <iostream.h>
#include <string.h>
void main()
{
char name1[ ]=”Иванов”;
char name2[6];
char name3[3];
strcpy(name2,”Петров”);
name3[0]=’k’;
name3[1]=’i’;
name3[2]=’m’;
name3[3]=’\0’;
cout<<name1<<endl<< name2<<endl<< name3;
return 0
}
Указатели.
Указатели – это переменные, которые содержат в памяти адрес данных. Можно сказать, что переменная типа указатель содержит местоположение значения. В С++ указатели есть на все существующие типы данных. Разница между обычными переменными и указателями заключается в их содержимом. Указатели содержат не само значение, а его адрес. С++ имеет два оператора, относящихся к указателям.
& - оператор «адрес значения»
* - оператор «значение по адресу».
Оператор & возвращает адрес переменной, расположенной после него. Оператор *, используемый вместе с указателем, позволяет узнать значение, которое находится в памяти ЭВМ по данному адресу.
Объявление указателей.
int age =30;// объявление переменной
int *p_age; // объявление указателя на целую переменную
Знак * перед переменной при объявлении означает, что эта переменная указатель. При объявлении резервируется место для переменной с именем p_age.
С++ не инициализирует указатели при их объявлении, для того, чтобы указатель содержал адрес переменной, на которую он указывает следует выполнить:
p_age= & age; // присваивание адреса переменной указателю
То же самое можно сделать при объявлении:
int age =30;
int *p_age= & age;
Чтобы распечатать переменные:
cout<<age; // печать значения переменной age
cout<<*p_age; // печать значения переменной age через указатель
cout<<p_age; // печать адреса переменной age.
Указатели и массивы.
В С++ имя массива – это указатель, который содержит адрес первого элемента массива. Если есть:
int abba[5];
cout <<*abba;// печать первого элемента массива abba[0];
cout <<*(abba+2); // печать третьего элемента массива.
abba[i]=*(abba+i)
abba=&abba[0] abba+1=&abba[1] abba+i=&abba[i]
Пример 1.
Существуют две переменные с плавающей точкой, вначале указатель содержит адрес первой переменной, затем второй.
#include <iostream>
using namespace std;
int main()
{ float v1=34.25;
float v2=17.9
float *ptr_v;
ptr_v=&v1;
cout<<”значение первого числа “<<*ptr_v<<”\n”;
ptr_v=&v2;
cout<<”значение второго числа “<<*ptr_v<<”\n”;
}
Пример 2.
С массивами можно работать, используя нотацию указателей.
#include <iostream>
using namespace std;
int main()
{ int ctr;
int iara[5]={10,20,30,40,50};
int *iptr; // объявление указателя на переменную целого типа
iptr=iara; // инициализация указателя
cout<<”вывод массива через индексы ”<<endl;
for(ctr=0; ctr<5; ctr++)
cout<<iara[ctr]<<”\t”<<iptr[ctr]<<endl;
cout<<”вывод массива через указатели ”<<endl;
for(ctr=0; ctr<5; ctr++)
cout<<*(iara+ctr)<<”\t”<<*(iptr+ctr)<<endl;
}
Объявление массива указателей.
int*iptr[10]; // резервирование массива на 10 указателей целого типа
char*cpoint[20];// резервирование массива на 20 указателей символьного типа.
Каждый элемент после инициализации будет содержать адрес, который указывает на другое значение в памяти. Массивы указателей позволяют более эффективно проводить обработку групповых данных.
Двумерный массив может быть представлен как одномерный массив указателей:
data-type *array[expression 1];
например, мы хотим описать двумерный массив х[10][20]. Это будет иметь следующий вид int *x[10];, где х – одномерный массив указателей.
При этом x[0] указывает на начало первого ряда массива [10][20]
При этом x[1] указывает на начало второго ряда массива х[10][20]
![]() | ||||||||
![]() | ![]() | |||||||
![]() | ||||||||
![]() | ||||||||
Чтобы получить доступ к элементу х[1][5]- надо использовать следующую запись: *(х[1][5])., где х[1] – это адрес второго ряда,(х[2]+5)-это адрес четвёртого элемента в этом ряду, *(х[2]+5) – это значение по адресу.
Рассмотрим пример: Программа вычисляет сумму элементов двух таблиц и записывает в третий массив. Каждый двумерный массив представлен как одномерный массив указателей. Каждый элемент одномерного массива указателей соответствует одному ряду двумерного массива.
Для объявления массивов и резервирования памяти воспользуемся функцией malloc(), снабжённой одним аргументом: необходимым числом байтов памяти. Эта функция возвращает адрес первого байта блока. Эта функция может применяться для возвращения указателей на массивы, структуры. Например:
double *ptd;
ptd=(double*) malloc(30*sizeof(double));
Этот код запрашивает память для хранения 30 величин типа double.
Обратите внимание, что ptd объявлении как указатель для отдельного типа double, а не для блока из 30 величин, имеющих тип double.
Так как имя массива есть адрес первого элемента массива, то ptd можно использовать для получения доступа к первому элементу блока.
#include<stdlib.h> // содержит функцию malloc()
#include<iostream.h>
#define MAXROWS 20
void main()
{ int row, nrows, ncols;
int *a[MAXROWS],*b[MAXROWS],*c[MAXROWS];//определение массивов указателей
//прототипы функций
void readinput(int *a[MAXROWS],int nrows, int ncols);
void computesums(int *a[MAXROWS], int *b[MAXROWS], int *c[MAXROWS], int nrows, int ncols);
void writeoutput(int *c[MAXROWS], int nrows, int ncols);
cout<<"Сколько рядов"<<endl;
cin>>nrows;
cout<<"Сколько колонок"<<endl;
cin>>ncols;
//первоначальное распределение памяти
for(row=0; row<=nrows; row++)
{
a[row]=(int*)malloc(ncols*sizeof(int));
b[row]=(int*)malloc(ncols*sizeof(int));
c[row]=(int*)malloc(ncols*sizeof(int));
}
cout<<"первая таблица"<<endl;
readinput(a,nrows,ncols);
cout<<"вторая таблица"<<endl;
readinput(b,nrows,ncols);
computesums(a,b,c,nrows,ncols);
cout<<"сумма элементов"<<endl;
writeoutput(c,nrows,ncols);
}
//return;
void readinput(int *a[MAXROWS],int m, int n)
{
int row,col;
for (row=0; row<m;++row)
{
cout<<"введите данные по"<<row<<" ряду"<<endl;
for(col=0; col<n;++col)
cin>>*(a[row]+col);
}
return;
}
void computesums(int *a[MAXROWS], int *b[MAXROWS], int *c[MAXROWS], int m, int n)
{
int row,col;
for (row=0; row<m;++row)
for (col=0; col<n;++col)
*(c[row]+col)=*(a[row]+col)+*(b[row]+col);
return;
}
void writeoutput(int *a[MAXROWS], int m, int n)
{
int row,col;
for (row=0; row<m;++row)
{ for (col=0; col<n;++col)
cout<<*(a[row]+col);
cout<<endl;
}
return;
}