The C Programming Language(第 2 版) 笔记 / 6 结构 / 6.2 结构与函数

目录、参考文献


6.2 结构与函数

结构的合法操作只有几种:作为一个整体复制和赋值,通过 & 运算符取地址,访问其成员
其中,复制和赋值包括向函数传递参数以及从函数返回值
结构之间不可以进行比较
可以用一个常量成员值列表初始化结构,自动结构也可以通过赋值进行初始化

为了更进一步地理解结构,我们编写几个对点和矩形进行操作的函数
至少可以通过 3 种可能的方法传递结构:一是分别传递各个结构成员,二是传递整个结构,三是传递指向结构的指针
这 3 种方法各有利弊

makepoint 函数带有两个整型参数,并返回一个 point 类型的结构:

/* makepoint: make a point from x and y components */ 
struct point makepoint(int x, int y) 
{ 
    struct point temp; 
    temp.x = x; 
    temp.y = y; 
    return temp; 
}

注意,参数名和结构成员同名不会引起冲突,使用重名可以强调两者之间的关系

现在可以使用 makepoint 函数动态地初始化任意结构:

struct rect screen; 
struct point middle; 
struct point makepoint(int, int); 

screen.pt1 = makepoint(0, 0); 
screen.pt2 = makepoint(XMAX, YMAX); 
middle = makepoint((screen.pt1.x + screen.pt2.x)/2, (screen.pt1.y + screen.pt2.y)/2);

接下来需要编写一系列的函数对点执行算术运算:

/* addpoints: add two points */ 
struct addpoint(struct point p1, struct point p2) 
{
    p1.x += p2.x; 
    p1.y += p2.y; 
    return p1; 
}

其中,函数的参数和返回值都是结构类型
之所以直接将相加所得的结果赋值给 p1,而没有使用显式的临时变量存储
是为了强调结构类型的参数和其它类型的参数一样,都是通过值传递的

函数 prinrect 判断一个点是否在给定的矩形内部
我们采用这样一个约定:矩形包括其左侧边和底边,但不包括顶边和右侧边

/* ptinrect: return 1 if p in r, 0 if not */ 
int ptinrect(struct point p, struct rect r) 
{ 
    return p.x >= r.pt1.x && p.x < r.pt2.x && p.y >= r.pt1.y && p.y < r.pt2.y; 
}

这里假设矩形是用标准形式表示的,其中 pt1 的坐标小于 pt2 的坐标
下列函数将返回一个规范形式的矩形:

#define min(a, b) ((a) < (b) ? (a) : (b)) 
#define max(a, b) ((a) > (b) ? (a) : (b))

/* canonrect: canonicalize coordinates of rectangle */ 
struct rect canonrect(struct rect r) 
{ 
    struct rect temp; 
    temp.pt1.x = min(r.pt1.x, r.pt2.x); 
    temp.pt1.y = min(r.pt1.y, r.pt2.y); 
    temp.pt2.x = max(r.pt1.x, r.pt2.x); 
    temp.pt2.y = max(r.pt1.y, r.pt2.y); 
    return temp; 
}

如果传递给函数的结构很大,使用指针方式的效率通常比复制整个结构的效率要高
结构指针类似于普通变量指针,声明 struct point *pp;pp 定义为一个指向 struct point 类型对象的指针
如果 pp 指向一个 point 结构,那么 *pp 即为该结构,而 (*pp).x 和(*pp).y 则是结构成员
可以按照下例中的方式使用 pp

struct point origin, *pp;

pp = &origin;
printf("origin is (%d,%d)\n", (*pp).x, (*pp).y);

其中 (*pp).x 中的圆括号是必需的,因为结构成员运算符 . 的优先级比 * 的优先级高
表达式 *pp.x 的含义等价于 *(pp.x),因为 x 不是指针,所以该表达式是非法的

结构指针的使用频度非常高,为了使用方便,C 语言提供了另一种简写方式
假定 p 是一个指向结构的指针,可以用 p->结构成员 的形式引用相应的结构成员
这样,就可以用 printf("origin is (%d,%d)\n", pp->x, pp->y); 的形式改写上面的代码

运算符 .-> 都是从左至右结合的
所以,对于声明 struct rect r, *rp = &r; 以下 4 个表达式是等价的:

r.pt1.x 
rp->pt1.x 
(r.pt1).x 
(rp->pt1).x

在所有运算符中,结构运算符 .->、用于函数调用的 () 以及用于下标的 [] 的优先级最高
因此,它们同操作数之间的结合也最紧密
例如,对于结构声明

struct { 
    int len; 
    char *str; 
} *p;

表达式 ++p->len 将增加 len 的值,而不是增加 p 的值,因为其中的隐含括号关系是 ++(p->len)
可以使用括号改变结合次序,如 (++p)->len 将先执行 p1,再对 len 执行操作
(p++)->len 则先对 len 执行操作,然后再将 p1(该表达式中的括号可以省略)

同样的道理 *p->str 读取的是指针 str 所指向的对象的值
*p->str++ 先读取指针 str 指向的对象的值,然后再将 str1(与 *s++ 相同)
(*p->str)++ 将指针 str 指向的对象的值加 1
*p++->str 先读取指针 str 指向的对象的值,然后再将 p1


目录、参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值