17.1
每次调用时都检查函数malloc(或其他任何内存分配函数)的返回值是件很烦人的事情。请编写一个名为my_malloc的函数作为malloc函数的“包装器”。当调用函数my_malloc并且要求分配n个字节时,它会调用malloc函数,判断malloc函数确实没有返回空指针,然后返回来自malloc的指针。如果malloc返回空指针,那么函数my_malloc显示出错消息并且终止程序。
void *my_malloc(size_t n) {
void *p;
if ((p = malloc(n)) == NULL) {
printf("Error: malloc failed.\n");
exit(EXIT_FAILURE);
}
return p;
}
17.2
编写名为duplicate的函数,此函数使用动态存储分配来创建字符串的副本。例如,调用
p = duplicate(str);
将为和str长度相同的字符串分配内存空间,并且把字符串str的内容复制到新字符串,然后返回指向新字符串的指针。如果分配内存失败,那么函数duplicate返回空指针。
char *duplicate(char *str) {
char *news = (char *) malloc(strlen(s) + 1);
if (news == NULL) {
printf("Error: malloc failed\n");
return NULL;
}
strcpy(news, str);
return news;
}
17.3
编写下列函数:
int *create_array(int n, int initial_value);
函数应返回一个指向动态分配的n元int型数组的指针,数组的每个成员都初始化为initial_
value。如果内存分配失败,返回值为NULL。
int *create_array(int n, int initial_value) {
int *a, *p;
if ((a = (int *) malloc(n * sizeof(int)) == NULL) {
printf("Error: malloc failed\n");
return NULL;
}
for (p = a; p < a + n; p++)
*p = initial_value;
return a;
}
17.4
假设下列声明有效:
struct point { int x, y; };
struct rectangle { struct point upper_left, lower_right; };
struct rectangle *p;假设希望p指向一个rectangle结构,此结构的左上角位于(10, 25)的位置上,而右下角位于(20, 15)
的位置上。请编写一系列语句用来分配这样一个结构,并且像说明的那样进行初始化。
p = (struct rectangle *) malloc(sizeof(struct rectangle));
p->upper_left = {10, 25};
p->lower_right = {20, 15};
17.5
假设f和p的声明如下所示:
struct {
union {
char a, b;
int c;
} d;
int e[5];
} f, *p = &f;
那么下列哪些语句是合法的?
(a) p->b = ' ';
(b) p->e[3] = 10;
(c)(*p).d.a = '*';
(d) p->d->c = 20;
b和c是合法的,a应该是p->d.a = ' '; d应该是p_>d.c = 20;
17.6
请修改函数delete_from_list使它使用一个指针变量而不是两个(即cur和prev)。
struct node *delete_from_list(struct node **list, int n) {
struct node *item = *list;
while (item) {
if (item->value == n) {
*list = item->next;
free(item);
break;
}
list = &item->next;
item = item->next;
}
return *list;
}
17.7
下列循环希望删除链表中的全部结点,并且释放它们占用的内存。但是,此循环有错误。请解释错误是什么并且说明如何修正错误。
for (p = first; p != NULL; p = p->next)
free(p);
//循环释放了p,然后试图将p设置为它的下一个成员,而这个成员被取消了分配。
//解决这个问题的一个方法是创建一个指向下一个节点的指针,然后在p被反分配后将p分配给它。
struct node *next_node;
while (p != NULL) {
next_node = p->next;
free(p);
p = next_node;
}
17.8
15.2节描述的文件stack.c提供了在栈中存储整数的函数。在那一节中,栈是用数组实现的。请修改程序stack.c从而使栈现在作为链表来存储。使用单独一个指向链表首结点的指针变量(栈“顶”)来替换变量contents和变量top。在stack.c中编写的函数要使用此指针。删除函数is_full,用返回true(如果创建的结点可以获得内存)或false(如果创建的结点无法获得内存)的函数push来代替。
#include <stdio.h>
#include <stdlib.h>
#include "stack.h"
struct node {
int data;
struct node *next;
};
static struct node *top = NULL;
static void terminate(const char *message)
{
printf("%s\n", message);
exit(EXIT_FAILURE);
}
void make_empty(void)
{
while (!is_empty())
pop();
}
bool is_empty(void)
{
return top == NULL;
}
bool is_full(void)
{
return false;
}
void push(int i)
{
struct node *new_node = malloc(sizeof(struct node));
if (new_node == NULL)
terminate("Error in push: stack is full.");
new_node->data = i;
new_node->next = top;
top = new_node;
}
int pop(void)
{
struct node *old_top;
int i;
if (is_empty())
terminate("Error in pop: stack is empty.");
old_top = top;
i = top->data;
top = top->next;
free(old_top);
return i;
}
17.9
判断:如果x是一个结构而a是该结构的成员,那么(&x)->a与x.a是一样的。验证你的答案。
真。当扩展时,右箭头运算符(->),扩展为解除引用运算符和点运算符(*和.);
因此,(&x)->a扩展为*(&x).a。解除引用和寻址运算符相抵消,产生x.a。
17.10
修改16.2节的print_part函数,使得它的形式参数是一个指向part结构的指针。请使用->运算符。
void print_part(struct part *p)
{
printf("Part number: %d\n", p->number);
printf("Part name: %s\n", p->name);
printf("Quantity on hand: %d\n", p->on_hand);
}
17.11
编写下列函数:
int count_occurrences(struct node *list, int n);
其中形式参数list指向一个链表。函数应返回n在该链表中出现的次数。node结构的定义见17.5节。
int count_occurrences(struct node *list, int n) {
int sum = 0;
while (list != NULL) {
if (list->value == n)
sum++;
list = list->next;
}
return sum;
}
17.12
编写下列函数:
struct node *find_last(struct node *list, int n);
其中形式参数list指向一个链表。函数应返回一个指针,该指针指向最后一个包含n的结点,如果n
不存在则返回NULL。node结构的定义见17.5节。
struct node *find_last(struct node *list, int n) {
struct node *answer = NULL;
while (list != NULL) {
if (list->value == n)
answer = list;
list = list->next;
}
return answer;
}
17.13
下面的函数希望在有序链表的适当位置插入一个新结点,并返回指向新链表首结点的指针。但是,函数无法做到在所有的情况下都正确。解释问题所在,并说明如何修正。node结构的定义见17.5节。
struct node *insert_into_ordered_list(struct node *list, struct node *new_node)
{
struct node *cur = list, *prev = NULL;
while (cur->value <= new_node->value) {
prev = cur;
cur = cur->next;
}
prev->next = new_node;
new_node->next = cur;
return list;
}
该函数没有处理这样的边缘情况:当新节点必须插入到列表的第一个项目之前(当prev为NULL时),
以及当新节点必须插入到列表的最后一个项目之后(当cur为NULL时)。一个解决方案是使用一个指
向列表的指针,这也将消除有两个变量来插入新节点的必要性。
struct node *insert_into_ordered_list(struct node *list, struct node *new_node)
{
struct node **pp = &list;
while (list != NULL) {
if (list->value >= new_node->value)
break;
pp = &list->next;
list = list->next;
}
*pp = new_node;
new_node = list;
}
17.14
修改函数delete_from_list(17.5节),使函数的第一个形式参数是struct node **类型(即指向链表首结点的指针的指针),并且返回类型是void。在删除了期望的结点后,函数delete_from_list必须修改第一个实际参数,使其指向该链表。
void delete_from_list(struct node **list, int n) {
struct node *entry = *list;
while (entry != NULL) {
if (entry->value == n) {
*list = entry->next;
free(entry);
}
list = &entry->next;
entry = entry->next;
}
}
17.15
请说明下列程序的输出结果,并解释程序的功能。
#include <stdio.h>
int f1(int (*f)(int));
int f2(int i);
int main(void)
{
printf("Answer: %d\n", f1(f2));
return 0;
}
int f1(int (*f) (int))
{
int n = 0;
while ((*f)(n)) n++;
return n;
}
int f2(int i)
{
return i * i + i - 12;
}
Answer: 3
f1(f2) calls f(n) from n = 0 to n = 3 (where f2 returns 0).
f1 then returns 3, and the printf call prints Answer: 3.
17.16
编写下列函数。调用sum(g, i, j)应该返回g(i) + ... + g(j)。
int sum(int (*f)(int), int start, int end);
int sum(int (*f)(int), int start, int end) {
int result = 0;
while (start <= end) {
result += (*f)(start);
start++;
}
return result;
}
17.17
设a是有100个整数的数组。请编写函数qsort的调用,只对数组a中的后50个元素进行排序。(不需要编写比较函数。)
qsort(&a[50], 50, sizeof(a[0]), compare);
17.18
请修改函数compare_parts使零件根据编号进行降序排列
int compare_parts(const void *p, const void *q)
{
return ((struct part *) q)->number - ((struct part *) p)->number;
}
17.19
请编写一个函数,要求在给定字符串作为实际参数时,此函数搜索下列所示的结构数组寻找匹配的命令名,然后调用和匹配名称相关的函数:
struct {
char *cmd_name;
void (*cmd_pointer)(void);
} file_cmd[] =
{ {"new", new_cmd},
{"open", open_cmd},
{"close", close_cmd},
{"close all", close_all_cmd},
{"save", save_cmd},
{"save as", save_as_cmd},
{"save all", save_all_cmd},
{"print", print_cmd},
{"exit" , exit_cmd}
};
void run_command(char *str) {
int i;
for (i = 0; i < sizeof(file_cmd)/sizeof(file_cmd[0]); i++)
if (strcmp(str, file_cmd[i].cmd_name) == 0)
return (*file_cmd[i].cmd_pointer)();
return;
}