个人博客:SHIZHZ's Blogs
本文是对《K&R》第六章《Structures》的摘要。
结构(struct)是其它数据类型的集合,用来构建聚合性更高更复杂的数据类型。
1. struct的声明及初始化。关键字struct用来声明结构,基本语法规则为:
struct {... /* struct members list here */ } x, y, z;
可以看出其声明方式与其它普通数据类型声明一样,如:
int x, y, z;
可见整个struct {... /* struct members list here */ } 的作用在于描述数据类型。如果同一个struct需要在多处声明使用,那么可以给struct一个tag,格式为:
struct tag {... /* struct members list here */ };
随后声明该struct的变量时,通过如下方式即可:
struct tag x, y, z;
声明struct变量时可对其进行初始化:
struct {
int x;
int y;
} x = {1, 2};
如果花括号中的初始化值的个数多于实际struct成员,则编译器会报错,如果少于实际成员,则剩下成员对应值为0。
2. struct成员的访问。通过操作符“.”访问struct的成员:
struct point {
int x;
int y;
} p1;
// access x and y of p1
printf("x: %d, y: %d\n", p1.x, p1.y);
从本质上讲,struct的作用只是基于已有的数据类型定义出了一个聚合性更高的新的数据类型,所以struct的成员也可以其它的struct,如:
struct circle {
struct point o;
float r;
};
3. 对struct的操作。对struct的合法操作包括:a. 将其作为一个整体拷贝、赋值;b. 用操作符&取址; c. 访问其成员。拷贝与赋值包括将struct作为参数传递给函数或从函数中返回。struct与其它基本数据类型一样,在作为函数参数或函数返回值时是按值传递的:
#include <stdio.h>
struct point
{
int x;
int y;
};
struct point makepoint(int x, int y)
{
struct point o;
o.x = x;
o.y = y;
return o;
}
void changepoint (struct point p)
{
p.x = 5;
p.y = 5;
}
int main (int argc, char const* argv[])
{
struct point o = makepoint(1, 2);
changepoint(o);
printf("x: %d, y: %d\n", o.x, o.y);
return 0;
}
结果为:
$ ./a.out
x: 1, y: 2
可见函数makepoint在返回struct point o时,是将其栈中该对象的值拷贝到了main函数中对应的对象中,而changepoint改变的也只是main函数传递给它的一份拷贝。
通常情况下传递struct的指针个函数会比较有效率,特别是在struct的成员较多较复杂的情况下。通过指针访问struct的成员的方式是:
p->member-of-struct-p
struct point *p = &o;
printf("%d\n", p -> x);
将->与其它操作符一起使用时,需要考虑相互之间的优先级。如:
++p->x表示将p->x的值加1。
4. struct数组、指针与基本类型相似。
5. typedef: typedef用来创建新的类型名称。typedef没有创建新的数据类型,只是用来创建新的类型名称,见如下例子:
#include <stdio.h>
typedef struct
{
int x;
int y;
} Point;
typedef struct
{
Point o;
float r;
} Circle;
Point makepoint(int x, int y)
{
Point o;
o.x = x;
o.y = y;
return o;
}
void showpoint(Point p)
{
printf("Point:(x: %d, y: %d)\n", p.x, p.y);
}
void showcircle(Circle c)
{
printf("Circlr:\n\t");
showpoint(c.o);
printf("\tr: %.2f\n", c.r);
}
Circle makecircle(Point p, float r)
{
Circle circle = {p, r};
return circle;
}
int main (int argc, char const* argv[])
{
Point o = makepoint(1, 2);
Circle c = makecircle(o, 5);
showcircle(c);
return 0;
}
使用typedef的主要作用包括:
a. 从直观上看使代码更整洁,用一个更为简洁的名词替代复杂的类型声明过程
6. unions。union关键字所声明的数据结构用来保存不同类型和大小的数据。struct的内存大小是其所有成员的大小之和,union的大小是其成员大小的最大值。
#include <stdio.h>
int main (int argc, char const* argv[])
{
union u {
int i;
float f;
char* s;
} u_var;
u_var.i = 5;
printf("%d\n", u_var.i);
u_var.f = 6.5;
printf("%f\n", u_var.f);
u_var.s = "hello, world\n";
printf("%s\n", u_var.s);
return 0;
}
程序员需要在程序中保留union当前数据类型的信息。union在初始化时只能初始化为第一个成员的类型,如:
union u {
int i;
float f;
char* s;
} u_var = {6};
7. Bit-fields。程序中经常会用到各种flag标记,其只可能处于两种状态之一,用计算机最小的存储单元“位(bit)”来表示即可。通常的方式是定义对应位的掩码,如:
#define ONE 01
#define TWO 02
#define THREE 04
这样就可以通过位操作符(|, ^, ~, &)来设定对应的位,例如变量int flags的各个位表示不同的flag,那么:
flags |= ONE | TWO | THREE
表示打开对应的三个标记;
flags ~= ONE | TWO
表示关闭对应的三个标记。C语言提供了另外一种声明bit field的方法:
struct
{
unsigned int one: 1;
unsigned int two: 1;
unsigned int three: 1;
unsigned int : 0;
unsigned int next_word_boundary: 1;
} flags;
其中冒号后面的数字表示占用多少位,0表示对其到当前字的边界,使用sizeof(flags)得到结果为8。该结构中与bit field相关的信息都是依赖于机器的具体实现,实际使用时需要当心。一般还是用前面位掩码的方式吧:"(
________________
< 本篇终了:) >
----------------
\ ^____^
\ ( o o )\_______
(___)\ )\/\
||---------w |
|| ||