Масиви символів можна створювати двома способами: з використанням масивів і з використанням вказівників:
char с1[ ] = "now is the time";
char * с2 = "now is the time";
В цьому визначенні с1 – це масив з кількістю елементів достатньою для розміщення символів рядка ініціалізації і нульового символу. Символи масиву можуть змінюватися, с1 є константним вказівником. с2 – це вказівник, який ініціалізований таким чином, щоб посилатися на рядкову константу. Значення вказівника можна змінювати, елементи символьного рядка – ні. Розглянемо програму, яка буде копіювати один рядок в інший.
#include <iostream>
using namespace std;
int main() {
char c1[ 80 ] = "Good old string";
char * c2 = "Nice new string (pointer version)";
char c3[ 80 ] = "Nice new string (array version)";
char * t1, *t2;
int i;
for ( t1 = c1, t2 = c2, i = 0; t2[ i ] != '\0'; i++ )
t1[ i ] = t2[ i ];
cout << c1 << endl;
for ( t1=c1, t2=c3, i=0; ( t1[ i ] = t2[ i ] ) != 0; i++ );
cout << c1 << endl;
t1 = c1;
t2 = c2;
while ( ( * t1++ = * t2++ ) != '\0' );
cout << c1 << endl;
t1 = c1;
t2 = c3;
while ( * t1++ = * t2++ );
cout << c1 << endl;
}
Операцію звертання за адресою можна використовувати разом з префіксним інкрементом (декрементом). Ці операції мають однаковий пріоритет і асоціативність справа наліво, тобто спочатку буде збільшене (зменшене) значення вказівника, після цього – виконане звертання за новою адресою. Знайдемо кількість елементів рядка за допомогою вказівників.
#include <iostream>
using namespace std;
int main() {
char c[ 80 ] = "A string";
char * t;
t = c;
while (* ++t);
cout << t - c << endl;
}
Поширеним видом багатовимірних масивів є масиви символьних рядків, тобто масиви, кожен елемент яких – окремий символьний рядок. Наприклад, масив повідомлень про помилки можна оголосити таким чином:
char Error[ 3 ][ 20 ] = {
"File Write Error",
"File Read Error",
"File Open Error"
};
Масив Error в пам'яті буде розташований таким чином (рис. 8.1):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| F
| i
| l
| e
| | W
| r
| i
| t
| e
| | E
| r
| r
| o
| r
| \0
| | | |
| F
| i
| l
| e
| | R
| e
| a
| d
| | E
| r
| r
| o
| r
| \0
| | | | |
| F
| i
| l
| e
| | O
| p
| e
| n
| | E
| r
| r
| o
| r
| \0
| | | | |
Рис. 8.1. Розташування елементів масиву рядків в пам'яті
Правила звертання до елементів символьних рядків такі ж, як і до звичайних багатовимірних масивів. Наприклад, до першого рядка символів і до першого символу другого рядка можна звернутися таким чином:
#include <iostream>
using namespace std;
int main() {
char Error[ 3 ][ 20 ] = {
"File Write Error",
"File Read Error",
"File Open Error"
};
cout << Error[ 0 ]; // Перший рядок символів
cout << Error[ 1 ][ 0 ]; // Перший символ другого рядка
}
Як і для інших масивів, старшу розмірність можна не указувати, вона буде визначена по кількості ініціалізаторів. В наступній програмі виконується перетворення чисел у двійково-десятковий вид.
#include <iostream>
#include <ctime>
using namespace std;
int main() {
srand( unsigned( time ( NULL ) ) );
char code[ ][ 5 ] = {
"0000", "0001", "0010", "0011", "0100",
"0101", "0110", "0111", "1000", "1001"
};
for ( int i = 0, t; i < 20; i++ ) {
t = rand( ) % 100;
cout << t << "\t" << code[ t / 10 ] << " "
<< code[ t % 10 ] << endl;
}
}
В цьому прикладі всі рядки в масиві мають однакову довжину – 5 символів (4 символи для цифр і один символ для нуль – символу), тому всі елементи цього двовимірного масиву будуть зайняті корисною інформацією. В попередньому прикладі для збереження кожного повідомлення про помилку мі виділяли 20 символів, по розміру найбільш довгого повідомлення. Через це частина елементів декількох з рядків не використовувалась.
Для уникнення цього недоліку можна використовувати масиви вказівників на перші символи набору рядків, кожен з яких зберігається в оперативній пам'яті окремо. Наприклад:
#include <iostream>
using namespace std;
int main() {
char * month[ ] = {
"Wrong month number",
"January", "February", "March", "April", "May",
"June", "July", "August", "September", "October",
"November", "December"
};
int t;
cin >> t;
cout << ( 1 <= t && t <= 12 ? month[ t ] : month[ 0 ] );
}
тут month – це масив вказівників на char, кількість елементів масиву визначається кількістю ініціалізаторів, їх 13, кожен елемент масиву – вказівник на перший елемент відповідного рядка.
Напишемо програму, яка буде визначати день року для зазначеної дати і навпаки. В цьому прикладі змінна leap має значення 0, якщо рік не високосний і 1 – якщо високосний.
#include <iostream>
using namespace std;
int main() {
char * month_name[ ] = {
"Wrong month number",
"January", "February", "March" , "April", "May",
"June", "July", "August", "September", "October",
"November", "Deсember"
};
char ndays[ 2 ][ 13 ] = {
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
{ 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
int year = 2011, month = 3, day = 16, i, j, n;
// Номер дня для заданої дати
int leap= year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
for ( i = 1, n = day; i < month; i++ )
n += ndays[ leap ][ i ];
cout << n << endl;
// Дата для номера дня і року
n = 75;
for ( i = 1; ndays[ leap ][ i ] <= n; i++ )
n -= ndays[ leap ][ i ];
cout << month_name[ i ] << " " << n << endl;
// Всі номера днів
for ( i = 1, n = 1; i <= 12; i++ )
for ( j = 1; j <= ndays[ leap ][ i ]; j++, n++ ) {
cout << n << "\t";
cout << month_name[ i ] << "\t";
cout << j << endl;
}
}