Литмир - Электронная Библиотека
A
A

Эта рекомендация относится как к языку C, так и к языку C++.

Функция

strchr()
из языка C порождает аналогичную, но более трудноуловимую проблему. Рассмотрим пример.

char* strchr(const char* s,int c); /* найти c в константной строке s

                                      (
не C++) */

const char aa[] = "asdf";  /* aa — массив констант */

char* q = strchr(aa,'d');  /* находит символ 'd' */

*q = 'x';                  /* изменяет символ 'd' в строке aa на 'x' */

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 Опять-таки, этот код является недопустимым ни в языке С, ни в языке С++, но компиляторы языка C не могут найти ошибку. Иногда это явление называют трансмутацией (transmutation): функция превращает константы в не константы, нарушая разумные предположения о коде.

В языке C++ эта проблема решается с помощью немного измененного объявления стандартной библиотечной функции

strchr()
.

char const* strchr(const char* s, int c); // найти символ c

                                          // в константной строке s

char* strchr(char* s, int c);             // найти символ c в строке s

Аналогично объявляется функция

strstr()
.

27.5.2. Операции над байтами

В далеком средневековье (в начале 1980-х годов), еще до изобретения указателя

void*
, программисты, работавшие на языках C (и C++), для манипуляции байтами использовали строки. В настоящее время основные стандартные библиотечные функции для работы с памятью имеют параметры типа
void*
и возвращают указатели типа
void*
, чтобы предупредить пользователей о непосредственной работе с памятью без контроля типов.

/* копирует n байтов из строки s2 в строку s1 (как функция strcpy): */

void* memcpy(void* s1, const void* s2, size_t n);

/* копирует n байтов из строки s2 в строку s1

   (диапазон [s1:s1+n] может перекрываться с диапазоном [s2:s2+n]): */

void* memmove(void* s1, const void* s2, size_t n);

/* сравнивает n байтов из строки s2 в строку s1

   (как функция strcmp): */

int memcmp(const void* s1, const void* s2, size_t n);

/* находит символ c (преобразованный в тип unsigned char)

   среди первых n байтов строки s: */

void* memchr(const void* s, int c, size_t n);

/* копирует символ c (преобразованный в тип unsigned char)

   в каждый из n байтов строки, на который ссылается указатель s: */

void* memset(void* s, int c, size_t n);

Не используйте эти функции в программах на языке C++. В частности, функция

memset()
обычно влияет на гарантии, выданные конструкторами.

27.5.3. Пример: функция strcpy()

Определение функции

strcpy()
представляет собой печально известный пример лаконичного стиля, который допускает язык C (и C++) .

char* strcpy(char* p, const char* q)

{

  while (*p++ = *q++);

  return p;

}

Объяснение, почему этот код на самом деле копирует С-строку

q
в С-строку
p
, мы оставляем читателям в качестве упражнения.

ПОПРОБУЙТЕ

Является ли корректной реализация функции

strcpy()
? Объясните почему.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _001.png
 Если вы не можете аргументировать свой ответ, то не вправе считать себя программистом, работающим на языке C (однако вы можете быть компетентным в других языках программирования). Каждый язык имеет свои собственные идиомы, это относится и к языку C.

27.5.4. Вопросы стиля

Мы потихоньку втягиваемся в длинные и часто яростно оспариваемые вопросы стиля, которые, впрочем, часто не имеют большого значения. Мы объявляем указатель следующим образом:

char* p; // p — указатель на переменную типа char

Мы не принимаем стиль, продемонстрированный ниже.

char *p; /* p — нечто, что можно разыменовать, чтобы получить символ */

Пробел совершенно игнорируется компилятором, но для программиста он имеет значение. Наш стиль (общепринятый среди программистов на языке С++) подчеркивает тип объявляемой переменной, в то время как альтернативный стиль (общепринятый среди программистов на языке С) делает упор на использовании переменной. Мы не рекомендуем объявлять несколько переменных в одной строке.

char c, *p, a[177], *f(); /* разрешено, но может ввести в заблуждение */

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

char c = 'a'; /* символ завершения ввода для функции f() */

char* p = 0;  /* последний символ, считанный функцией f() */

char a[177];  /* буфер ввода */

char* f();    /* считывает данные в буфер a;

                 возвращает указатель
 на первый считанный символ */

Кроме того, выбирайте осмысленные имена.

27.6. Ввод-вывод: заголовок stdio

 В языке С нет потоков ввода-вывода

iostream
, поэтому мы используем стандартный механизм ввода-вывода языка С, определенный в заголовочном файле
<stdio.h>
. Эквивалентами потоков ввода и вывода
cin
и
cout
из языка С++ в языке С являются потоки
stdin
и
stdout
. Стандартные средства ввода-вывода языка С и потоки
iostream
могут одновременно использоваться в одной и той же программе (для одних и тех же потоков ввода-вывода), но мы не рекомендуем это делать. Если вам необходимо совместно использовать эти механизмы, хорошенько разберитесь в них (обратите особое внимание на функцию
ios_base::sync_with_stdio()
), используя хороший учебник. См. также раздел Б.10.

394
{"b":"847443","o":1}