Строковые переменные

Встроенный тип данных «строка» в C/C++ отсутствует. С точки зрения компилятора и стандартной библиотеки, строка — это массив элементов типа char, последним элементом которого является двоичный ноль (то есть символ, код которого равен нулю). Допускается присваивание таким массивам так называемых строковых литералов — последовательностей символов, заключенных в двойные кавычки. В конец каждого такого литерала компилятор автоматически добавляет символ с кодом 0. Рассмотрим небольшой пример:

char str1[] = "text";
char str2[] = { 't', 'e', 'x', 't', '\0' };
char* str3 = "text";

Все три объявления в приведенном выше примере абсолютно равнозначны; заметим, что в случае «формальной» инициализации (переменная str2) требуется явное указание завершающего нулевого символа. В соответствии с тем, что компилятор C/C++ воспринимает имя массива как адрес его первого элемента, синтаксически допустимым и правильным является объявление переменной str3 как указателя. Именно эта форма и используется чаще всего; более того, все функции стандартной библиотеки, предназначенные для работы со строками, используют именно формальный тип «указатель на char».

Необходимо отметить, что компилятор от Microsoft помещает строковые литералы в область, доступную только для чтения, поэтому выполнение следующего примера завершается с ошибкой «access violation»:

void foo(void)
{
   char* str = "Money";
   str[0] = 'H';
}

Чтобы иметь возможность модифицировать содержимое строки, соответствующую переменную необходимо формально объявлять как массив:

void foo(void)
{
   char str[] = "Money";
   str[0] = 'H';
}

В этом случае, содержимое строкового литерала будет скопировано в стек и изменение этой копии не вызовет никаких нареканий со стороны операционной системы.

Управляющие последовательности

Для вставки в строку «непечатных» (с кодами меньше 32) или имеющих в C/C++ специальное значение символов, необходимо использовать так называемые управляющие последовательности, начинающиеся с символа «обратный слэш»:

\a — звонок;
\t — горизонтальная табуляция;
\r — возврат каретки;
\n — перевод строки;
\b — «забой» (backspace);
\' — апостроф;
\" — двойная кавычка;
\\ — обратный слэш;
\0 — символ с кодом 0.

Кроме того, в строку можно вставить любой символ, указав его восьмеричный или шестнадцатеричный код:

\ooo — символ с восьмеричным кодом ooo;
\xHH — символ с шестнадцатеричным кодом HH.

Следующие две строки эквивалентны:

char* str1 = "Hello,\nworld!";
char* str2 = "Hello,\x0Aworld!";

Функции обработки строк

Заголовочный файл #include <string.h>

Еще раз подчеркнем, что все функции обработки строк, предоставляемые стандартной библиотекой, считают признаком конца строки первый символ с кодом 0, который присутствует в этой строке. Таким образом, при выполнении любых действий над строковой переменной, объявленной как

char* str = "Sin\0City";

слово «City» будет игнорироваться. Рассмотрим основные функции обработки строк.

strcpy

char* strcpy(
   char* dest,
   const char* src
);

Копирует строку src в dest и возвращает dest. Пример использования:

char str[80];
strcpy(str, "Go Down");
// теперь в str содержится "Go Down"

strcat

char* strcat(
   char* dest,
   const char* src
);

«Дописывает» строку src в конец строки dest и возвращает dest. Пример использования:

char str[80];
strcpy(str, "Heat");
strcat(str, "seeker");
// теперь в str содержится "Heatseeker"

strlen

int strlen(
   const char* src
);

Возвращает длину строки src, то есть количество символов до завершающего нуля. Пример использования:

int n;
n = strlen("Overdose");
// n == 8
n = strlen("Dog\0Eat Dog");
// n == 3

strchr

char* strchr(
   const char* src,
   char chr
);

Ищет первое вхождение символа chr в строку src и возвращает указатель на часть строки, начинающуюся с искомого символа. Если символ chr отсутствует в строке src, функция возвращает NULL. Пример использования:

char str[80];
strcpy(str, strchr("Overdose", 'd'));
// теперь str содержит "dose"

strrchr

char* strrchr(
   const char* src,
   char chr
);

Ищет последнее вхождение символа chr в строку src и возвращает указатель на часть строки, начинающуюся с искомого символа. Если символ chr отсутствует в строке src, функция возвращает NULL. Пример использования:

char str[80];
strcpy(str, strrchr("Bad Boy Boogie", 'B'));
// теперь str содержит "Boogie"

strstr

char* strstr(
   const char* str,
   const char* substr
);

Ищет первое вхождение строки substr в строку str и возвращает указатель на часть строки, начинающуюся с искомой подстроки. Если строка substr отсутствует в строке str, функция возвращает NULL. Пример использования:

char str[80];
strcpy(str, strstr("Fly On The Wall", "The"));
// теперь str содержит "The Wall"

strcmp

int strcmp(
   const char* str1,
   const char* str2
);

Сравнивает строки str1 и str2. Если эти строки эквивалентны, функция возвращает 0; в противном случае, возвращается отрицательное значение, если строка str1 «меньше» строки str2, или положительное значение, если строка str1 «больше» строки str2. «Меньше» и «больше» в данном случае определяется разницей кодов первых несовпадающих символов. Пример использования:

int n = strcmp("Sink The Pink", "Stand Up");
// n < 0, так как 'i' < 't', так как 105 < 116

_stricmp

int _stricmp(
   const char* str1,
   const char* str2
);

Сравнивает строки str1 и str2 аналогично функции strcmp, но без учета регистра символов. Данная функция не входит в стандарт ANSI и относится к категории «Microsoft specific». Пример использования:

int n = _stricmp("Danger", "danGer");
// n == 0

strtok

char* strtok(
   char* src,
   const char* seps
);

Последовательно разбивает строку src на лексемы (токены), считая разделителями все символы строки seps. При каждом вызове возвращается указатель на очередную найденную лексему или NULL, если достигнут конец строки src. Отметим, что данная функция модифицирует исходную строку. Пример использования:

char cur_lex[80];

char str[] = "Send For\tThe\r\nMan";
char seps[] = "\t\r\n";

strcpy(cur_lex, strtok(str, seps));
// теперь в cur_lex содержится "Send For"

strcpy(cur_lex, strtok(NULL, seps));
// теперь в cur_lex содержится "The"

strcpy(cur_lex, strtok(NULL, seps));
// теперь в cur_lex содержится "Man"

Обратите внимание, что указатель на исходную строку передается только при первом вызове функции; при последующих вызовах для работы с этой же строкой необходимо в качестве ее адреса передавать значение NULL. Естественно, что в реальных случаях обработка лексем выполняется в цикле, завершающемся при достижении конца исходной строки:

char str[] = "Let There\tBe\r\nRock";
char seps[] = "\t\r\n";

char* cur_lex = strtok(str, seps);
while (cur_lex != NULL)
{
   ...  // делаем что-нибудь полезное с cur_lex
   cur_lex = strtok(NULL, seps);
}

Текстовый ввод/вывод

Заголовочный файл #include <stdio.h>

Поскольку нашей основной целью является создание Windows-приложений, взаимодействующих с пользователем посредством GUI, мы рассмотрим только две функции стандартной библиотеки, предназначенные для текстового ввода/вывода.

puts

int puts(
   const char* src
);

Выводит на экран строку src, переводит курсор в начало следующей строки экрана и возвращает количество выведенных символов.

gets

char* gets(
   char* dest
);

Записывает в dest введенную с клавиатуры строку и возвращает dest. Признаком конца ввода является символ перевода строки, генерируемый при нажатии пользователя на клавишу Enter. Заметим, что сам этот символ не копируется в dest.

Функции преобразования данных

Заголовочный файл #include <stdlib.h>

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

atoi

int atoi(
   const char* str
);

Преобразует строковое представление целого числа str в двоичное и возвращает его. Преобразование прекращается на первом недопустимом символе; если такой символ окажется самым первым в строке, функция вернет значение 0. Пример использования:

int x = atoi("28 bytes");
if (x != 28)
{
   puts("oops!");
}

atol

long atol(
   const char* str
);

Под Win32 эта функция полностью аналогична atoi.

strtol

При необходимости более гибко обрабатывать ошибки преобразования, можно воспользоваться функцией

long strtol(
   const char* str,
   char** end_ptr,
   int radix
);

которая преобразует строковое представление целого числа str в системе счисления с основанием radix в двоичное и возвращает его. При этом, в переменную по адресу end_ptr будет записан указатель на символ, который прервал обработку строки str. Пример использования:

char str[80];
char* end_ptr;

puts("Enter your age:");
long age = strtol(gets(str), &end_ptr, 10);
if (*end_ptr != 0)
{
   puts("oops!");
}

atof

double atof(
   const char* str
);

Преобразует строковое представление дробного числа str в двоичное и возвращает его. Преобразование прекращается на первом недопустимом символе; если такой символ окажется самым первым в строке, функция вернет значение 0.0. Заметим, что исходная строка может содержать как десятичное, так и экспоненциальное представление дробного числа.

strtod

При необходимости более гибко обрабатывать ошибки преобразования, можно воспользоваться функцией

double strtod(
   const char* str,
   char** end_ptr
);

которая преобразует строковое представление дробного числа str в двоичное и возвращает его, записывая по адресу end_ptr указатель на символ, который прервал обработку строки str. Использование этой функции аналогично strtol.

_itoa

char* _itoa(
   int number,
   char* dest,
   int radix
);

Записывает по адресу dest строковое представление целого числа number по основанию radix и возвращает dest. Пример использования:

char dec_str[80];
char hex_str[80];

_itoa(13, dec_str, 10);
// теперь в dec_str содержится "13"
_itoa(13, hex_str, 16);
// теперь в hex_str содержится "d"

_ltoa

char* _ltoa(
   long number,
   char* dest,
   int radix
);

Под Win32 эта функция полностью аналогична _itoa.

_ultoa

char* _ultoa(
   unsigned long number,
   char* dest,
   int radix
);

Аналогична функции _ltoa, но предназначена для преобразования беззнаковых целых чисел.

_gcvt

char* _gcvt(
   double number,
   int num_dig,
   char* dest
);

Записывает по адресу dest строковое представление дробного числа number и возвращает dest. Через параметр num_dig необходимо передать требуемое число знаков строкового представления. Заметим, что если данная функция не сможет представить исходное число в десятичной форме с требуемым количеством знаков, то будет выбрана экспоненциальная форма. При преобразовании в десятичную форму, функция отбрасывает незначащие нули.

_fcvt

char* _fcvt(
   double number,
   int num_dec,
   int* dec_pos,
   int* has_sign
);

Возвращает адрес буфера, содержащего строковое представление дробного числа number в десятичной форме; заметим, что этот буфер перезаписывается при каждом вызове функции. Через параметр num_dec необходимо передать требуемое количество десятичных знаков; при необходимости исходное число будет округлено или дополнено нулями. В переменную по адресу dec_pos будет записана требуемая позиция десятичной точки в возвращенной строке; при этом, если целая часть числа равна 0, то по этому адресу будет записано отрицательное или нулевое значение. В переменную по адресу has_sign записывается ненулевое значение для отрицательного исходного числа и 0 — в противном случае.

Таким образом, возвращаемая данной функцией строка не содержит ни знака, ни десятичной точки; ниже рассматривается несколько примеров:

char str[80];
int dec_pos;
int has_sign;

strcpy(_fcvt(3.85, 3, &dec_pos, &has_sign));
// теперь в str содержится "3850"
// dec_pos == 1
// has_sign == 0

strcpy(_fcvt(-21.6, 1, &dec_pos, &has_sign));
// теперь в str содержится "216"
// dec_pos == 2
// has_sign != 0

strcpy(_fcvt(-0.013, 3, &dec_pos, &has_sign));
// теперь в str содержится "013"
// dec_pos <= 0
// has_sign != 0

_ecvt

char* _ecvt(
   double number,
   int num_dec,
   int* dec_pos,
   int* has_sign
);

Данная функция полностью аналогична _fcvt, за исключением того, что исходное число представляется в экспоненциальной форме. Заметим, что эта функция использует тот же буфер, что и _fcvt.

sprintf

int sprintf(
   char* dest,
   const char* fmt,
   ...
);

Записывает в буфер по адресу dest строку, сформированную на основании форматирующей строки fmt и произвольного количества необязательных аргументов. Строка fmt, помимо обычных символов, может содержать так называемые форматирующие последовательности. Каждая такая последовательность соответствует одному необязательному аргументу; она начинается с символа «%» и имеет в общем случае форму

%fw.pst

Здесь t — это один символ, определяющий тип аргумента, строковое представление которого должно быть подставлено на место данной форматирующей последовательности, и вид выполняемого преобразования. Это обязательная составляющая форматирующей последовательности; допустимо использование следующих символов:

t ожидаемый
тип аргумента
вид преобразования
c char
d
i
int или long в десятичной системе
u unsigned int в десятичной системе
o unsigned int в восьмеричной системе
x unsigned int в шестнадцатеричной системе, буквы a…f строчные
X unsigned int в шестнадцатеричной системе, буквы A…F заглавные
e double в экспоненциальной форме, буква e строчная
E double в экспоненциальной форме, буква E заглавная
f double в десятичной форме
g double в наиболее компактной форме, буква e строчная
G double в наиболее компактной форме, буква E заглавная
p void* в шестнадцатеричной системе, буквы A…F заглавные
s char* параметр интерпретируется как строка C/C++

Необязательная часть f форматирующей последовательности определяет выравнивание преобразованного аргумента, необходимость отображения его знака, etc, и может состоять из одного или нескольких символов, перечисленных ниже:

символ значение
- преобразованный аргумент выравнивается по левому краю (по умолчанию — по правому)
+ знак отображается при любом значении аргумента (по умолчанию — только при отрицательном)
0 «лишние» позиции заполняются символом «0» (по умолчанию — пробелом)
пробел при положительном значении аргумента на месте знака выводится пробел
# o к преобразованному аргументу добавляется префикс «0»
x к преобразованному аргументу добавляется префикс «0x»
X к преобразованному аргументу добавляется префикс «0X»
e
E
f
преобразованный аргумент будет содержать десятичную точку даже при отсутствии дробной части
g
G
преобразованный аргумент будет содержать десятичную точку даже при отсутствии дробной части;
при необходимости дробная часть дополняется незначащими нулями

Необязательная составляющая w задает требуемую минимальную ширину преобразованного аргумента; заметим, что аргумент будет выведен полностью, даже если заданное значение окажется недостаточным.

Необязательная составляющая p определяет точность представления аргумента; ее интерпретация зависит от типа этого аргумента:

тип p
e
E
f
требуемое количество знаков после десятичной точки; при необходимости выполняется
округление аргумента или дополнение его дробной части незначащими нулями
g
G
максимальное количество значащих цифр
s максимальное количество символов аргумента, которое следует использовать

Необязательная составляющая s «уточняет» размер целочисленного аргумента и может быть одним из следующих символов:

символ размер аргумента
l long
h short

Если в формируемую строку необходимо вставить символ «%», то его следует написать два раза подряд. Ниже приведен пример использования функции sprintf:

char str[80];
sprintf(str, "My name is %s. I am %i years old.", "Elijah", 31);
// теперь в str содержится "My name is Elijah. I am 31 years old."
обновлено
29.03.2006
 
Проверка PR и ТИЦ