1、
声明结构的过程和定义结构变量的过程可以组合成一个步骤。
struct book {
char title20;
char author20;
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 name20;
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 board88; // 一个内含int数组的数组
int **ptr; // 一个指向指针的指针,被指向的指针指向int类型
int *risks10; // 一个内含10个元素的数组,每个元素都是一个指向int的指针
int (*rusks)10; // 一个指向数组的指针,该数组内含10个int类型的值
int *oof34; // 一个 3 x 4 的二维数组,每个元素都是指向int的指针
int (*uuf)34; // 一个指向 3 x 4 的二维数组的指针,该数组中内含int类型值
int ( *uof3 )4; // 一个内含3个指针元素的数组,其中每个指针都指向一个内含4个int类型元素的数组
注:
①:数组名后面的 和函数名后面的( )具有相同的优先级。它们比 * 的优先级高。
②: 和 ( )的优先级相同,都是从左往右结合。
③: int *oof34;
3 比 * 的优先级高,由于从左到右结合,所以3先与 oof 结合。因此,oof 首先是一个内含3个元素的数组。然后再与4结合,所以 oof 的每个元素都是内含 4 个元素的数组。* 说明这些元素都是指针。最后,int 表明了这 4 个元素都是指向 int 类型的指针。
④: int (*uuf)34;
圆括号()使得 * 先与 uuf 结合,说明 uuf 是一个指针,所以 uuf 是一个指向 3 x 4 的 int 类型二维数组的指针。
11、
char *fump(int); // 返回字符指针的函数
char (*frump)(int); // 指向函数的指针,该函数的返回类型为char
char (*flump3)(int); // 内含3个指针的数组,每个指针都指向返回类型为char的函数
12、
typedef int arr55;
typedef arr5 * p_arr5;
typedef p_arr5 arrp1010;
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() 函数的返回值