形式"/>
结构体和其他数据形式
文章目录
- 建立结构声明 —— 对象
- 定义结构变量 —— 实例
- 初始化结构
- 访问结构成员
- 结构的初始化器
- 声明结构数组
- 指向结构的指针
- 声明和初始化结构指针
- 用指针访问成员
- 用函数传递结构的信息
- 其他结构特性
- 结构和结构指针的选择
- 结构中的字符数组和字符指针
- 结构、指针和malloc()
- 复合字面量和结构(C99)
- 伸缩型数组成员(C99)
- 匿名结构(C11)
- 枚举类型
- 赋值
- typedef
- 函数和指针
建立结构声明 —— 对象
struct book{char title[MAXTITLE];char author[MAXAUTL];float value;
};// 如果把结构声明置于一个函数的内部,它的标记就只限于该函数内部使用,如果把结构声明置于函数的外部,那么该声明之后的所有函数都能使用它的标记
定义结构变量 —— 实例
结构有两层含义:1. 结构布局。结构布局告诉编译器如何表示数据,但是并未让编译器为数据分配空间。2. 创建一个结构变量。
创建结构变量的的一行是:struct book library;
,编译器使用 book 模板为该变量分配空间,这行是下面的简化:
struct book{char title[MAXTITLE];char author[MAXAUTL];float value;
} library;
// 无结构标记
struct {char title[MAXTITLE];char author[MAXAUTL];float value;
} library;
初始化结构
struct book library = {"The Pious Pirate and the Devious Damsel","Renee Vivottee",1.95
}
访问结构成员
结构类似于一个“超级数组”,这个数组中,可以是一个元素为 char 类型,下一个元素为 float 类型,下一个元素为 int 数据,如何访问结构中的成员?使用结构成员运算符 —— 点(.)访问结构中的成员。library.value。
结构的初始化器
C99 和 C11 为结构提供了指定初始化器,其语法与数组的指定初始化器类似。结构的指定初始化器使用点运算符和成员名(而不是方括号和下标)标识指定的元素。例如,只初始化 book 结构的 value 成员,可以这样做:
struct book surprise = {.value = 10.99};
声明结构数组
struct book library[MAXBKS];
library // 一个book结构的数组
library[2] // 一个数组元素,该元素是book结构
library[2].title // 一个char数组
library[2].title[4] // 数组中library[2]元素的title成员的一个字符
指向结构的指针
- 就像指向数组的指针比数组本身更容易操控,指向结构的指针通常比结构本身更容易操控。
- 在一些早期的 C 实现中,结构不能作为参数传递给函数,但是可以传递指向结构的指针。
- 传递指针通常更有效率。
- 一些用于表示数据的结构中包含指向其他结构的指针。
struct names {char first[LEN];char last[LEN];
} A;
struct names * him;
struct names fellow = {"Ellen","Jack"
};
him = &fellow;
printf("%s\n", (*him).first);
声明和初始化结构指针
声明结构指针很简单:struct names * him;
,首先是关键字 struct,其次是结构标记 names,然后是一个星号,其后跟着指针名。该声明并未创建一个新的结构,但是指针 him 现在可以指向任意现有的 names 类型的结构。和数组不同的是,结构名并不是结构的地址,因此要在结构名前面加上 & 运算符。
用指针访问成员
(*him).names == A[0].namess
,必须要使用圆括号,因为 . 运算符比 * 运算符的优先级高。
-> 运算符后面的结构指针和 . 运算符后面的结构名工作方式相同(不能写成 him.names,因为 him 不是结构名)
用函数传递结构的信息
程序员可以选择是传递结构本身,还是传递指向结构的指针。也可以把结构的成员作为参数。
struct funds{char bank[FUNDLEN];double bankfund;char save[FUNDLEN];double savefund;
};
double sum(const struct funds * money)
{return money->bankfund + money->savefund;
}int main()
{struct funds stan = {"Garlic-Melon Bank",4032,27,"Lucky",8345};printf("Stan has a total of $%.2f.\n", sum(&stan));
}
其他结构特性
- 现在的 C 允许把一个结构赋值给另一个结构,但是数组不能这样做,即使成员是数组,也能完成赋值。
struct names right_field = {"Ruthis", "George"};
struct names captain = right_field;
- 现在的 C 函数不仅能够把结构本身作为参数传递,还能把结构作为返回值返回。把结构作为函数参数可以把结构的信息传送给函数;把结构作为返回值的函数能把结构的信息从被调函数传回主调函数。
结构和结构指针的选择
把指针作为参数有两个优点:无论是以前还是现在的 C 实现都使用这种方法,而且执行起来很快,只需要传递一个地址即可。缺点是无法保护数据,被调函数中的某些操作可能会意外影响原来结构中的数据,但是可以使用 const 限定符解决这个问题。
struct vector ans, a, b;
void vector sum_vect(const struct vector *, const struct vector *, struct vector *);
...
sum_vect(&a, &b, &ans);
把结构作为参数传递的优点是:函数处理的是这个原始数据的副本,这保护了原始数据。
struct vector ans, a, b;
struct vector sum_vect(struct vector, struct vector);
...
ans = sum_vect(a, b);
传递结构的两个缺点是:较老版本的实现可能无法处理这样的代码,而且传递结构浪费时间和存储空间。
结构中的字符数组和字符指针
如果要用结构储存字符串,用字符数组作为成员比较简单,用指向 char 的指针也行,但是误用会导致严重的问题。
struct pname {char* first;char* last;
};
struct pnames 结构不用字符串分配任何存储空间。它使用的是储存在别处的字符串(比如字符串常量或数组中的字符串),简而言之,pnames 结构变量中的指针应该只用来在程序中管理那些已分配和在别处分配的字符串。
结构、指针和malloc()
如果使用 malloc() 分配内存并使用指针储存该地址,那么在结构中使用指针处理字符串就比较合理。
void getinfo(struct pname * pst)
{char temp[SLEN];s_gets(temp, SLEN);pst->first = (char*)malloc(strlen(temp) + 1);strcpy(pst->first, temp);...
}
这两个字符串都未储存在结构中,它们存储在 malloc() 分配的内存块,然而,结构中储存着这两个字符串的地址,处理字符串的函数通常都要使用字符串的地址。
复合字面量和结构(C99)
C99 的复合字面量特性可用于结构和数组,如果只需要一个临时结构值,复合字面量很好用,在所有函数的外部,具有静态存储期,如果在块中,具有自动存储期。
struct book {char title[MAXTITL];char author[MAXAUTL];float value;
};int main()
{struct book readfirst;readfirst = (struct book){"Crime and Punishment", "Fyodor Dostoyevsky", 11.25};
}
伸缩型数组成员(C99)
- 伸缩型数组成员必须是结构的最后一个成员;
- 结构中必须至少有一个成员;
- 伸缩型的声明类似于普通数组,只是它的方括号中是空的;
struct flex
{int count;double average;double scores[]; // 伸缩型数组成员
};
声明一个 struct flex 类型的结构变量时,不能用 scores 做任何事,因为没有这个数组预留存储空间,C99 的意图是希望你声明一个指向 struct flex 类型的指针,然后用 malloc() 来分配足够的空间。
匿名结构(C11)
嵌套结构:
struct names
{char first[20];char last[20];
};
struct person
{int id;struct names name;
}
struct person ted = {8483, {"Ted", "Grass"}};
在C11中,可以用嵌套的匿名成员结构定义 person:
struct person
{int id;struct {char first[20]; char last[20];}; // 匿名结构
};
枚举类型
enum spectrum {red, orange, yellow, green}; // 创建了一个 spectrum 作为标记名,允许把 enum spectrum 作为一个类型名使用,默认值是 0 1 2 3 4
enum spectrum color; // 第2个声明使color作为该类型的变量,color 可能的值是 red,orange,yellow等
int c;
color = blue;
if(color == yellow)...
for(color = red; color <= green; color++)...
赋值
enum feline {cat, lynx=10, puma, tiger};
cat值是0,lynx是10,puma是11,tiger是12
typedef
利用 typedef 可以为某一类现有类型创建别名。
- 与 #define 不同,typedef 创建的符号名之受限于类型,不能用于值;
- typedef 由编译器解释,不是预处理器;
- typedef 比 #define 更灵活;
typedef unsigned char BYTE;
BYTE x, y[10], *X;
- 用BYTE代替 unsigned char 表面打算用 BYTE 类型的变量表示数字,而不是字符码。
- 使用 typedef 还能提高程序的可移植性。
typedef char * STRING;
STRING name, sign; // char *name, *sign;
#define char * STRING;
STRING name, sign; // char * name, sign; 只有name是指针
还可以把 typedef 用于结构:
typedef struct complex {float real;float imag;
} COMPLEX;
// 更常用的是省略该结构的标签,typedef struct {double x; double y} rect;
rect r1;
typedef 并没有创建任何新类型,它只是为某个已存在的类型增加一个方便使用的标签。
int board[8][8]; // 声明一个内含int的数组;
int **ptr; // 声明一个指向指针的指针,被指向的指针指向int;
int * risks[10]; // 声明一个内含10个元素的数组,每个元素都是一个指向 int 的指针;
int (*risks)[10]; // 声明一个指向数组的指针,该数组包含10个int类型的值;
int * oof[3][4]; // 声明一个3x4的二维数组,每个元素都是指向int的指针;
int (* uuf)[3][4]; // 声明一个指向3x4二维数组的指针,该数组中内含int类型值;
int (& ufo[3])[4]; // 声明一个内含3个指针元素的数组,其中每个指针都指向一个内含4个int类型元素的数组
函数和指针
一个指向函数的指针,函数指针常用作另一个函数的参数,告诉该函数要使用哪一个函数。
void ToUpper(char*);
void ToLower(char*);
void (*pf)(char*);
pf = ToUpper;
pf = ToLower;
更多推荐
结构体和其他数据形式
发布评论