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

void f()

{

  char i = 0;

  char j = 0;

  char* p = &i;

  void* q = p;

  int* pp = q; /* небезопасно; разрешено в языке C,

                  ошибка в языке C++ */

  *pp = –1;    /* перезаписываем память, начиная с адреса &i */

В данном случае мы даже не уверены, какой фрагмент памяти будет перезаписан: переменная

j
или часть памяти, на которую ссылается указатель
p
? А может быть, память, использованная для управлении вызовом функции
f()
(стек функции
f
)? Какие бы данные ни были перезаписаны, вызов функции
f()
приведет к печальным последствиям.

Обратите внимание на то, что (обратное) преобразование указателя типа

T*
в указатель типа
void*
является совершенно безопасным, — вы не сможете придумать ужасные примеры, подобные предыдущему, — и они допускаются как в языке C, так и в языке C++.

К сожалению, неявное преобразование

void*
в
T*
широко распространено в языке C и, вероятно, является основной проблемой совместимости языков С и С++ в реальных программах (см. раздел 27.4).

27.3.6. Перечисление

В языке C можно присваивать целое число перечислению без приведения

int
в
enum
. Рассмотрим пример.

enum color { red, blue, green };

int x = green;      /* OK в языках C и C++ */

enum color col = 7; /* OK в языке C; ошибка в языке C++ */

Одним из следствий этого факта является то, что в программах на языке С мы можем применять операции инкрементации (

++
) и декрементации (
––
) к переменным, являющимся перечислениями. Это может быть удобным, но одновременно небезопасным.

enum color x = blue;

++x; /* переменная x становится равной значению green;

        ошибка в языке C++ */

++x; /* переменная x становится равной 3; ошибка в языке C++ */

Выход за пределы перечисления может входить в наши планы, а может быть неожиданным.

Обратите внимание на то, что, подобно дескрипторам структур, имена перечислений пребывают в своем собственном пространстве имен, поэтому каждый раз при указании имени перечисления перед ним следует ставить ключевое слово

enum
.

color c2 = blue;     /* ошибка в языке C: переменная color не находится

                        в пределах области видимости; OK в языке C++ */

enum color c3 = red; /* OK */

27.3.7. Пространства имен

В языке С нет пространств имен (в том смысле, как это принято в языке С++). Так что же можно сделать, чтобы избежать коллизий имен в больших программах, написанных на языке С? Как правило, для этого используются префиксы и суффиксы. Рассмотрим пример.

/* в bs.h: */

typedef struct bs_string { /* ... */ } bs_string; /* строка

                                                     Бьярне */

typedef int bs_bool; /* булев тип Бьярне */

/* in pete.h: */

typedef char* pete_string; /* строка Пита */

typedef char pete_bool;    /* булев тип Пита */

Этот прием настолько широко используется, что использовать одно- и двухбуквенные префиксы обычно уже недостаточно.

27.4. Свободная память

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

new
и
delete
, работающих с объектами. Для использования свободной памяти в нем используются функции, работающие с памятью. Наиболее важные функции определены в стандартном заголовочном файле общих утилит
<stdlib.h>
.

void* malloc(size_t sz); /* выделить sz байтов */

void free(void* p);      /* освободить область памяти, на которую

                            ссылается указатель p */

void* calloc(size_t n, size_t sz); /* выделить n*sz байтов,

                                      инициализировав их нулями */

void* realloc(void* p, size_t sz); /* вновь выделить sz байтов

                                      в памяти, на которую ссылается

                                      указатель p*/

Тип

typedef size_t
— это тип без знака, также определенный в заголовочном файле
<stdlib.h>
.

 

Программирование. Принципы и практика использования C++ Исправленное издание - _003.png
 Почему функция
malloc()
возвращает указатель
void*
? Потому что она не имеет информации о том, объект какого типа вы хотите разместить в памяти. Инициализация — это ваша проблема. Рассмотрим пример.

struct Pair {

  const char* p;

  int val;

};

struct Pair p2 = {"apple",78};

struct Pair* pp = (struct Pair*) malloc(sizeof(Pair)); /* выделить
память */

pp–>p = "pear"; /* инициализировать */

pp–>val = 42;

Теперь мы не можем написать инструкцию

*pp = {"pear", 42}; /* ошибка: не C и не C++98 */

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