нужны. Однако теперь мы должны разрешить основную загадку этого проекта: где находятся данные в элементах списка? Как реализовать простой список имен, представленных в виде С-строк. Рассмотрим следующий пример:
struct Name {
struct Link lnk; /* структура Link нужна для выполнения ее операций */
char* p; /* строка имен */
};
До сих пор все было хорошо, хотя остается загадкой, как мы можем использовать этот член Link? Но поскольку мы знаем, что структура List хранит узлы Link в свободной памяти, то написали функцию, создающую объекты структуры Name в свободной памяти.
struct Name* make_name(char* n)
{
struct Name* p = (struct Name*)malloc(sizeof(struct Name));
p–>p = n;
return p;
}
Эту ситуацию можно проиллюстрировать следующим образом:
Попробуем использовать эти структуры.
int main()
{
int count = 0;
struct List names; /* создает список */
struct List* curr;
init(&names);
/* создаем несколько объектов Names и добавляем их в список: */
push_back(&names,(struct Link*)make_name("Norah"));
push_back(&names,(struct Link*)make_name("Annemarie"));
push_back(&names,(struct Link*)make_name("Kris"));
/* удаляем второе имя (с индексом 1): */
erase(&names,advance(names.first,1));
curr = names.first; /* выписываем все имена */
for (; curr!=0; curr=curr–>suc) {
count++;
printf("element %d: %sn", count, ((struct Name*)curr)–>p);
}
}
Итак, мы смошенничали. Мы использовали приведение типа, чтобы работать с указателем типа Name* как с указателем типа Link*. Благодаря этому пользователь знает о библиотечной структуре Link. Тем не менее библиотека не знает о прикладном типе Name. Это допустимо? Да, допустимо: в языке C (и C++) можно интерпретировать указатель на структуру как указатель на ее первый элемент, и наоборот.
Очевидно, что этот пример можно также скомпилировать с помощью компилятора языка С++.
ПОПРОБУЙТЕ
Программисты, работающие на языке C++, разговаривая с программистами, работающими на языке C, рефреном повторяют: “Все, что делаешь ты, я могу сделать лучше!” Итак, перепишите пример интрузивного контейнера List на языке C++, продемонстрировав, что это можно сделать короче и проще без замедления программы или увеличения объектов.
Задание
1. Напишите программу “Hello World!” на языке C, скомпилируйте ее и выполните.
2. Определите две переменные, хранящие строки “Hello” и “World!” соответственно; конкатенируйте их с пробелом между ними и выведите в виде строки Hello World!.
3. Определите функцию на языке C, получающую параметр p типа char* и параметр x типа int, и выведите на печать их значения в следующем формате: p is "foo" and x is 7. Вызовите эту функцию для нескольких пар аргументов.
Контрольные вопросы
В следующих вопросах предполагается выполнение стандарта ISO C89.
1. Является ли язык C++ подмножеством языка C?
2. Кто изобрел язык C?
3. Назовите высокоавторитетный учебник по языку С.
4. В какой организации были изобретены языки C и C++?
5. Почему язык С++ (почти) совместим с языком C?
6. Почему язык C++ только почти совместим с языком C?
7. Перечислите десять особенностей языка C++, отсутствующих в языке C.
8. Какой организации “принадлежат” языки C и C++?
9. Перечислите шесть компонентов стандартной библиотеки языка C++, которые не используются в языке C.
10. Какие компоненты стандартной библиотеки языка C можно использовать в языке C++?
11. Как обеспечить проверку типов аргументов функций в языке C?
12. Какие свойства языка C++, связанные с функциями, отсутствуют в языке C? Назовите по крайней мере три из них. Приведите примеры.
13. Как вызвать функцию, написанную на языке C, в программе, написанной на языке C++?
14. Как вызвать функцию, написанную на языке C++, в программе, написанной на языке C?
15. Какие типы совместимы в языках C и C++? Приведите примеры.
16. Что такое дескриптор структуры?
17. Перечислите двадцать ключевых слов языка C++, которые не являются ключевыми словами языка C.
18. Является ли инструкция int x; определением в языке C++? А в языке C?
19. В чем заключается приведение в стиле языка С и чем оно опасно?
20. Что собой представляет тип void* и чем он отличается в языках C и C++?
21. Чем отличаются перечисления в языках C и C++?
22. Что надо сделать в программе на языке C, чтобы избежать проблем, связанных с совпадением широко распространенных имен?
23. Назовите три наиболее широко используемые функции для работы со свободной памятью в языке C.
24. Как выглядит определение в стиле языка С?
25. Чем отличаются оператор == и функция strcmp() для С-строк?
26. Как скопировать С-строки?
27. Как определить длину С-строки?
28. Как скопировать большой массив целых чисел типа int?
29. Назовите преимущества и недостатки функции printf().
30. Почему никогда не следует использовать функцию gets()? Что следует использовать вместо нее?
31. Как открыть файл для чтения в программе на языке C?
32. В чем заключается разница между константами (const) в языке C и C++?
33. Почему мы не любим макросы?
34. Как обычно используются макросы?
35. Что такое “страж включения”?
Термины
Упражнения
Для этих упражнений может оказаться полезным скомпилировать все программы с помощью компиляторов и языка C, и языка C++. Если использовать только компилятор языка C++, можно случайно использовать свойства, которых нет в языке C. Если вы используете только компилятор языка C, то ошибки, связанные с типами, могут остаться незамеченными
1. Реализуйте варианты функций strlen(), strcmp() и strcpy().
2. Завершите пример с интрузивным контейнером List из раздела 27.9 и протестируйте каждую его функцию.
3. Усовершенствуйте пример с интрузивным контейнером List из раздела 27.9 по своему усмотрению. Предусмотрите перехват и обработку как можно большего количества ошибок. При этом можно изменять детали определений структур, использовать макросы и т.д.
4. Если вы еще на переписали пример с интрузивным контейнером List из раздела 27.9 на языке C++, сделайте это и протестируйте каждую функцию.
5. Сравните результаты упр. 3 и 4.
6. Измените представление структур Link и List из раздела 27.9 без изменения интерфейса пользователя, обеспеченного функциями. Разместите узлы в массивах и предусмотрите члены first, last, pre, и suc типа int