Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Название:Язык программирования C. Лекции и упражнения (6-е изд.) 2015
- Автор:
- Жанр:
- Издательство:Вильямс
- Год:0101
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Стивен Прата - Язык программирования C. Лекции и упражнения (6-е изд.) 2015 краткое содержание
Язык программирования C. Лекции и упражнения (6-е изд.) 2015 - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Ответ — да, это возможно, но могут возникнуть проблемы, если вы не обдумаете все последствия. Взгляните на показанный ниже код:
struct names veep = {"Talia", "Summers"};
struct pnames treas = {"Brad", "Fallingjaw"};
printf("%s и %s\n", veep.hrst, treas.hrst);
588 Глава 14
Этот код допустим, и он работает, однако рассмотрим, где хранятся строки. В случае переменной veep типа struct names строки хранятся внутри структуры; для хранения двух имен структура выделяет всего 40 байтов. Тем не менее, в переменной treas типа struct pnames строки хранятся там, где компилятор сохраняет строковые константы. Все, что содержит данная структура — это два адреса, которые в нашей системе в целом занимают 16 байтов. В частности, структура struct pnames не выделяет память для хранения строк. Она может применяться только со строками, для которых память была выделена где-то в другом месте, такими как строковые константы или строки в массивах. Короче говоря, указатели в структуре struct pnames должны использоваться только для управления строками, которые были созданы с выделением под них памяти в другом месте программы.
Давайте посмотрим, когда это ограничение превращается в проблему. Взгляните на следующий код:
struct names accountant;
struct pnames attorney;
puts("Введите фамилию вашего бухгалтера );
scanf("%s", accountant.last);
puts("Введите фамилию вашего адвоката:");
scanf("%s", attorney.last); /* здесь скрыта опасность */
С точки зрения синтаксиса этот код допустим. Но куда сохраняются входные данные? Фамилия бухгалтера записывается в последний член переменной accountant; эта структура содержит массив для хранения строки. В случае фамилии адвоката функция scanf() получает указание поместить строку фамилии но адресу, заданному как attorney. last. Из-за того, что эта переменная не инициализирована, адрес может иметь произвольное значение, и программа может попытаться поместить фамилию куда угодно. Если повезет, то программа будет работать, по крайней мере, некоторое время, либо сразу же аварийно завершится. Однако если программа работает, то вам на самом деле не повезло, т.к. в ней присутствует катастрофическая ошибка, о которой вы не знаете.
Таким образом, если вам необходима структура для хранения строк, то проще применять члены типа символьных массивов. Использование указателей на char в отдельных случаях допускается, но потенциально сопряжено с серьезными проблемами.
Структура, указатели и malloc()
Применение в структуре указателя, поддерживающего строку, имеет смысл, когда с помощью функции malloc() для строки выделяется область памяти и указатель используется, чтобы сохранить адрес этой области. Преимущество такого подхода заключается в том, что malloc() позволяет выделить ровно столько памяти, сколько необходимо для строки. Вы можете запросить 4 байта для сохранения строки "Joe" и 18 байтов для строки с мадагаскарским именем "Rasolofomasoandro". Код в листинге 14.9 совсем нетрудно адаптировать под этот подход. Два основных изменения касаются определения структуры с целью применения указателей вместо массивов и предоставления новой версии функции getinfo().
Новое определение структуры будет выглядеть следующим образом:
struct namect {
char * fname; // использование указателей вместо массивов char * lname; int letters;
};
Структуры и другие формы данных 589
Новая версия getinfo() будет читать входные данные во временный массив, использовать malloc() для выделения пространства памяти и копировать в него строку. Она будет делать это для каждого имени:
void getinfo (struct namect * pst)
{
char temp[SLEN];
printf("Введите свое имя.\n");
s_gets(temp, SLEN);
// выделение памяти для хранения имени pst->fname = (char *) malloc(strlen(temp) + 1);
// копирование имени в выделенную память strcpy(pst->fname, temp); printf("Введите свою фамилию.\n"); s_gets(temp, SLEN);
pst->lname = (char *) malloc(strlen(temp) + 1); strcpy(pst->lname, temp);
}
Вы должны четко понимать, что эти две строки не хранятся в структуре. Они сохранены в области памяти, управляемой malloc(). Тем не менее, адреса двух строк хранятся в структуре, и именно с ними обычно имеют дело функции обработки строк. Следовательно, остальные функции в программе в изменениях не нуждаются.
Однако, согласно совету из главы 12, вы должны уравновешивать вызовы malloc() вызовами free() , поэтому в программу добавлена новая функция по имени cleanup(), которая освобождает память, когда программа завершает пользоваться ею. Вы найдете эту новую функцию и оставшуюся часть программы в листинге 14.10.
Листинг 14.10. Программа names3.c
590 Глава 14
Ниже показан пример вывода:
Введите свое имя.
Васисуалий
Введите свою фамилию.
Лоханкин
Васисуалий Лоханкин, ваше имя и фамилия содержат 18 букв.
Структуры и другие формы данных 591
Составные литералы и структуры (С99)
Средство составных литералов С99 доступно для структур, а также для массивов. Оно удобно, если требуется всего лишь временное значение структуры. Например, составные литералы можно применять для создания структуры, предназначенной для использования в качестве аргумента функции или для присваивания другой струк туре. Синтаксис составного литерала выглядит как заключенный в фигурные скобки список инициализаторов, которому предшествует имя типа в круглых скобках. Ниже представлен составной литерал типа struct book:
(struct book) {"Идиот", "Федор Достоевский", 6.99}
В листинге 14.11 приведен пример применения составных литералов для предоставления двух альтернативных значений переменной структуры. (На момент написания книги это средство поддерживалось не всеми компиляторами, но со временем ситуация должна поменяться.)
Листинг 14.11. Программа complit.c
Составные литералы можно использовать также как аргументы функций. Если функция ожидает структуру, то ей можно передавать в качестве фактического аргумента составной литерал:
struct rect {double х; double у,();
double rect_area(struct rect r){return r.x * r.y;)
double area;
area = rect_area ( (struct rect) {10.5, 20.0});
592 глава 14
Это приводит к тому, что area присваивается значение 210.0.
Если функция ожидает адрес, то ей можно передать адрес составного литерала:
В результате переменная area получает значение 210.0.
Составные литералы, которые встречаются за пределами любых функций, имеют статическую продолжительность хранения, а те, что находятся внутри блока — автоматическую продолжительность хранения. В отношении составных литералов действуют те же синтаксические правила, что и для обычных списков инициализаторов. Это значит, например, что в составных литералах можно применять назначенные инициализаторы.
Читать дальшеИнтервал:
Закладка: