1、
声明结构的过程和定义结构变量的过程可以组合成一个步骤。
struct book {
char title[20];
char author[20];
float value;
} library;
2、
初始化结构:
struct book library = {
"The Pious Pirate and the Devious Damsel",
"Renee Vivotte",
1.96
};
也可以使用指定初始化器:(可以按照任意顺序使用)
struct book gift = { .value = 10.99 };
struct book surprise = {
.value = 25.99,
.author = "James Broadfool",
.title = "Rue for the Toad"
};
3、
在指定初始化器后面的普通初始化器,为指定成员后面的成员提供初始值。另外,对特定成员的最后一次赋值才是它实际获得的值。
eg:
#include <stdio.h>
struct Student {
int id;
char name[20];
int age;
float score;
};
int main( )
{
struct Student s1 = {
.id = 1001, // 指定初始化 id
"Alice", 20, 85.5 // 普通初始化器:从id后面的成员开始
// "Alice" → name 20 → age 85.5 → score
};
...
};
eg:
struct book gift = { .value = 18.90,
.author = "Philionna Pestle",
0.25};
赋给value的值是0.25,因为它在结构声明中紧跟在author成员之后。新值0.25取代了之前的18.90。
4、
结构变量名并不是结构的地址,因此要在结构变量名前面加上 & 运算符。
5、
在有些系统中,一个结构的大小可能大于它各成员大小之和。因为系统对数据进行校准的过程中产生了一些"缝隙"。
6、
barney.income == (*him).income == him->income
// 假设 him(一个结构指针) == &barney(一个结构变量)
7、
C允许把一个结构赋值给另一个结构,即使成员是数组,也能完成赋值。但是数组不能这样做。另外,还可以把一个结构初始化为相同类型的另一个结构。
8、
函数不仅能把结构本身作为参数传递,还能把结构作为返回值返回(把结构的信息从被调函数传回主调函数)。
9、
typedef用于为已存在的数据类型创建一个新的名称。
eg:
①
typedef char * STRING;
STRING name, sign; // 等价于 char *name, char *sign;
如果用#define的话则如下
#define STRING char *
STRING name, sign; // 等价于 char *name, sign; 这导致只有name才是指针
②
typedef struct {
float x;
float y;
}rect;
rect r1 = { 3.0, 6.0 };
rect r2;
上述代码等价于:
struct {
float x;
float y;
}r1 = { 3.0, 6.0 };
struct {
float x;
float y;
}r2;
注:使用typedef时要记住,typedef 并没有创建任何新类型,它只是为某个已存在的类型增加了一个方便使用的标签。
10、
一些声明示例:
int board[8][8]; // 一个内含int数组的数组
int **ptr; // 一个指向指针的指针,被指向的指针指向int类型
int *risks[10]; // 一个内含10个元素的数组,每个元素都是一个指向int的指针
int (*rusks)[10]; // 一个指向数组的指针,该数组内含10个int类型的值
int *oof[3][4]; // 一个 3 x 4 的二维数组,每个元素都是指向int的指针
int (*uuf)[3][4]; // 一个指向 3 x 4 的二维数组的指针,该数组中内含int类型值
int ( *uof[3] )[4]; // 一个内含3个指针元素的数组,其中每个指针都指向一个内含4个int类型元素的数组
注:
①:数组名后面的[ ]和函数名后面的( )具有相同的优先级。它们比 * 的优先级高。
②: [ ] 和 ( )的优先级相同,都是从左往右结合。
③: int *oof[3][4];
3\] 比 \* 的优先级高,由于从左到右结合,所以\[3\]先与 oof 结合。因此,oof 首先是一个内含3个元素的数组。然后再与\[4\]结合,所以 oof 的每个元素都是内含 4 个元素的数组。\* 说明这些元素都是指针。最后,int 表明了这 4 个元素都是指向 int 类型的指针。 ④: int (\*uuf)\[3\]\[4\]; 圆括号()使得 \* 先与 uuf 结合,说明 uuf 是一个指针,所以 uuf 是一个指向 3 x 4 的 int 类型二维数组的指针。 11、 char \*fump(int); // 返回字符指针的函数 char (\*frump)(int); // 指向函数的指针,该函数的返回类型为char char (\*flump\[3\])(int); // 内含3个指针的数组,每个指针都指向返回类型为char的函数 12、 typedef int arr5\[5\]; typedef arr5 \* p_arr5; typedef p_arr5 arrp10\[10\]; arr5 togs; // togs 是一个内含 5 个 int 类型值的数组 p_arr5 p2; // p2 是一个指向数组的指针,该数组内含 5 个 int 类型的值 arrp10 ap; // ap 是一个内含 10 个指针的数组,每个指针都指向一个内含 5 个 int 类型值得数组 13、 声明一个函数指针时,必须声明指针指向的函数类型。为了指明函数类型,要指明函数签名,即函数的返回类型和形参类型。 eg: void ToUpper(char \*); // 带 char \* 类型参数、返回类型是 void 的函数 void (\*pf)(char \*); // 声明了一个指针 pf ,指向该函数类型 解析:第1对圆括号把 \* 和 pf 括起来,表明 pf 是一个指向函数的指针。因此,(\*pf)是一个参数列表为(char \*)、返回类型为 void 的函数。如果想声明一个指向某类型函数的指针,可以写出该函数的原型后把函数名替换成(\*pf)形式的表达式,创建函数指针声明。 void \*pf(char \*); // pf 是一个返回字符指针的函数 14、 void ToUpper(char \*); void ToLower(char \*); int round(double); void (\*pf)(char \*); pf = ToUpper; // 有效,ToUpper 是该类型函数的地址 pf = ToLower; // 有效,ToLower 是该类型函数的地址 pf = round; // 无效,round 与指针类型不匹配 pf = ToLower(); /\* 无效,ToLower()不是地址,且ToLower()的返回类型是void,他没有返回值,不能在赋值语句中进行 赋值。 \*/ 15、 void ToUpper(char \*); void ToLower(char \*); void (\*pf)(char \*); char mis\[\] = "Nina Metier"; pf = ToUpper; (\*pf)(mis); // 把 ToUpper 作用于语法一 pf = ToLower; pf(mis); // 把 ToLower 作用于语法二 解析: ①语法一 由于 pf 指向 ToUpper 函数,那么 \*pf 就相当于 ToUpper 函数,所以表达式 (\*pf)(mis) 和 ToUpper(mis) 相同。从 ToUpper 函数和 pf 的声明就能看出,ToUpper 和 (\*pf) 是等价的。 ②语法二 由于函数名是指针,那么指针和函数名可以互换使用,所以 pf(mis) 和 ToUpper(mis) 相同。从 pf 的赋值表达式语句就能看出 ToUpper 和 pf 是等价的。 16、 function1(sqrt); // 传递 sqrt() 函数的地址 function2(sqrt(4.0)); // 传递 sqrt() 函数的返回值