结构声明
struct book{
char title(MAX);
char author(MAX);
float value;
};
定义结构变量
声明:struct book library;
初始化:struct book library = {
"abd",// 用逗号隔开
"asd",
1.95
};
访问结构成员
- gets(library.title); // 不需要带结构名
- scanf(“%f”, &library.value); // library.value为float类型
- // gets()读取换行符,但舍弃,不会对后续的输入造成影响;优先选择;
结构数组:
struct book library[NAXBOOK];
gets(library[count].title);
gets(library[count].title[0]);
printf("%s", library[index].title);
嵌套结构:
结构内包含另一个结构。
struct name{
char first[LEN];
char last[LEN];
};
struct guy{
struct name handle;// 声明一个name变量,嵌套;
char favfood[LEN];
char jio[LEN];
float income
};
struct guy fellow;
printf("%s", fellow.handle.first);
printf("%s", fellow.job);
指向结构的指针
方法1:指针->成员 如:him->income
方法2:(*指针).成员 如:(*him).income
struct guy fellow[2];
struct guy* him;
him = &fellow[0];// 同一结构类型,不需要带结构名称;
printf("%s", him -> handle.first);
printf("%s", him -> job);
// 指针him->与fellow.的作用等效;
C的结构体和C++结构体的区别
(1)C的结构体内不允许有函数存在,C++允许有内部成员函数,且允许该函数是虚函数。所以C的结构体是没有构造函数、析构函数、和this指针的。
(2)C的结构体对内部成员变量的访问权限只能是public,而C++允许public,protected,private三种。
(3)C语言的结构体是不可以继承的,C++的结构体是可以从其他的结构体或者类继承过来的。
以上都是表面的区别,实际区别就是面向过程和面向对象编程思路的区别:
C的结构体只是把数据变量给包裹起来了,并不涉及算法。而C++是把数据变量及对这些数据变量的相关算法给封装起来,并且给对这些数据和类不同的访问权限。C语言中是没有类的概念的,但是C语言可以通过结构体内创建函数指针实现面向对象思想。
C++的结构体和C++类的区别
(1)C++结构体内部成员变量及成员函数默认的访问级别是public,而c++类的内部成员变量及成员函数的默认访问级别是private。
(2)C++结构体的继承默认是public,而c++类的继承默认是private。
指向结构的指针
向函数传递结构的信息
传递结构成员
#include<stdio.h>
#define FUNDLEN 50
struct funds {
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum(double, double);
int main(void)
{
struct funds stan = {
"Garlic--Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94
};
printf("Stan has a total of $%.2f.\n",sum(stan.bankfund,stan.savefund)); //
return 0;
}
double sum(double x, double y)
{
return (x + y);
}
传递结构的地址 (结构指针做参数)
#include<stdio.h>
#define FUNDLEN 50
struct funds {
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum(const struct funds *); //
int main(void)
{
struct funds stan = {
"Garlic--Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94
};
printf("Stan has a total of $%.2f.\n", sum(&stan)); //
return 0;
}
double sum(const struct funds * money) //
{
return (money->bankfund + money->savefund);
}
传递结构的地址 (结构指针做参数)
#include<stdio.h>
#define FUNDLEN 50
struct funds {
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum(const struct funds *); //
int main(void)
{
struct funds stan = {
"Garlic--Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94
};
printf("Stan has a total of $%.2f.\n", sum(&stan)); //
return 0;
}
double sum(const struct funds * money) //
{
return (money->bankfund + money->savefund);
}
传递结构
传递结构是传值,函数将创建一个结构副本,然后把实际参数的值给这个副本,函数中的操作都是对副本进行的。
#include<stdio.h>
#define FUNDLEN 50
struct funds {
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum( struct funds moolah);
int main(void)
{
struct funds stan = {
"Garlic--Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94
};
printf("Stan has a total of $%.2f.\n", sum(stan));
return 0;
}
double sum( struct funds moolah) //函数创建一个副本moolah,然后对副本进行操作(不会改变实际参数stan)
{
return( moolah.bankfund + moolah.savefund);
}
函数指针的好处:
1)提供调用的灵活性。设计好了一个函数框架,但是设计初期并不知道自己的函数会被如何使用。比如C的”stdlib”中声明的qsort函数,用来对数值进行排序。显然,顺序还是降序,元素谁大谁小这些问题,库程序员在编写qsort的时候不可能决定。这些问题是要在用户调用这个函数的时候才能够决定。那边qsort如何保证通用性和灵活性呢?采用的办法是让函数的使用者来制定排序规则。于是调用者应该自己设计comparator函数,传给qsort函数。这就在程序设计初期保证了灵活性。尽管使用函数指针使得程序有些难懂,但是这样的牺牲还是值得的。
(2)回调函数。Windows编程中的事件handle函数,即回调函数,在事件队列都是一个函数指针来保存的:
typedef void (*event_handler) (unsigned int para1, unsigned int para2);
struct event {
unsigned int ev_id;
event_handler handler;
};
struct event event_queue[MAX_EVENT_SIZE];
程序可以通过扫描这个事件队列来获取每个事件对应的处理函数,然后调用它,即为回调函数。
结构和结构指针的选择
通常用结构指针作为函数的参数,这样效率较高。如需要防止原始数据被修改,使用const限定符。
结构中的字符数组和字符指针
字符数组比较简单。
字符指针由于只是给出一个地址,但并未分配内存,可能会存到意外的地方。
伸缩型数组成员(C99)
- 1.伸缩型数组成员必须是结构的最后一个成员
- 2.结构必须至少有一个成员
- 3.伸缩数组的声明类似于普通数组,只是它的方括号内是空的。
struct flex
{
int count;
double average;
double scores[]; //伸缩型数组成员
};
/*声明一个 struct flex类型的变量后不能使用scores,因为还没给它分配存储空间。
通常使用方法是声明一个struct flex类型的指针,然后用malloc()给它分配存储空间。如:*/
struct flex *pf;
pf = malloc(sizeof(struct flex) + 5* sizeof(double) );
使用结构数组的函数
//把结构数组传递给函数
#include<stdio.h>
#define FUNDLEN 50
#define N 2
struct funds {
char bank[FUNDLEN];
double bankfund;
char save[FUNDLEN];
double savefund;
};
double sum(const struct funds mooey[], int n); //传入结构数组的函数
int main(void)
{
struct funds jones[N] = {
{
"Garlic--Melon Bank",
4032.27,
"Lucky's Savings and Loan",
8543.94
},
{
"Honest Jack's Bank",
3620.88,
"Party Time Savings",
3802.91
}
};
printf("The joneses have a total of $%.2f.\n", sum(jones,N) ); *//使用sum函数
return 0;
}
double sum(const struct funds money[],int n)
{
double total;
int i;
for ( i = 0, total = 0; i < n; i++)
total += money[i].bankfund + money[i].savefund;
return total;
}
联合简介
联合(union)是一种数据类型,能在同一个内存空间存储不同的数据类型。
定义联合
union hold {
int digit;
double bigfl;
char letter;
}
枚举类型
枚举类型的定义
enum<类型名>{枚举常量表}
typedef enum<类型名>{枚举常量表}类型别名;
enum<类型名>{枚举常量表} 枚举变量名;
//有时甚至可以省略类型名
enum {枚举常量表} 枚举变量名;
枚举类型的使用
enum Weekday{Mon,Tue,Wed,Thu,Fri,Sat,Sun};
Weekday weekday1=Mon;
cou<<weekday<<endl;
//输出为0 编译器会默认从0开始给枚举常量编号
enum color {RED,GREEN=2,WHITE,BLACK} color1;
//RED=0 GREEN=2,WHITE=3,BLACK=4
//编译器会根据用户给枚举常量赋的值 顺序给后面的常量赋值 枚举常量的只可以重复
注意:不同的枚举类型的枚举常量表中的元素不能相同 否则会报错重定义
如果我们有两个枚举常量相同的枚举类型 可以使他们在不同的命名空间里定义
例如:
enum Color_set1 {RED,WHITE,BLACK};
enum Color_set2 {GREEN,BLUE,RED};
//编译时报错
总结
- 不能直接给枚举变量赋整数型初始值;
- 枚举变量能直接输出 不能直接输入
- 不同类型的枚举变量不能相互赋值
typefdef简介
与#define的区别
- typefdef创建的符号只受限于类型,不能用于值
- typefdef由编译器解释,不是预处理器
- 在其受限范围内,typefdef比#define更加灵活
用途一:
定义一种类型的别名,而不只是简单的宏替换。可以用作同时声明指针型的多个对象。比如:
char* pa, pb; // 这多数不符合我们的意图,它只声明了一个指向字符变量的指针,
// 和一个字符变量;
以下则可行:
typedef char* PCHAR; // 一般用大写
PCHAR pa, pb; // 可行,同时声明了两个指向字符变量的指针
用途二:
用在旧的C的代码中(具体多旧没有查),帮助struct。以前的代码中,声明struct新对象时,必须要带上struct,即形式为: struct 结构名 对象名,如:
struct tagPOINT1
{
int x;
int y;
};
struct tagPOINT1 p1;
用途三:
用typedef来定义与平台无关的类型。
比如定义一个叫 REAL 的浮点类型,在目标平台一上,让它表示最高精度的类型为:
typedef long double REAL;
在不支持 long double 的平台二上,改为:
typedef double REAL;
在连 double 都不支持的平台三上,改为:
typedef float REAL;
也就是说,当跨平台时,只要改下 typedef 本身就行,不用对其他源码做任何修改。
标准库就广泛使用了这个技巧,比如size_t。
另外,因为typedef是定义了一种类型的新别名,不是简单的字符串替换,所以它比宏来得稳健(虽然用宏有时也可以完成以上的用途)。
用途四:
为复杂的声明定义一个新的简单的别名。方法是:在原来的声明里逐步用别名替换一部分复杂声明,如此循环,把带变量名的部分留到最后替换,得到的就是原声明的最简化版。举例:
- 原声明:int *(a[5])(int, char);
变量名为a,直接用一个新别名pFun替换a就可以了:
typedef int *(pFun)(int, char);
原声明的最简化版:
pFun a[5];- 原声明:void (b[10]) (void ()());
变量名为b,先替换右边部分括号里的,pFunParam为别名一:
typedef void (*pFunParam)();
再替换左边的变量b,pFunx为别名二:
typedef void (*pFunx)(pFunParam);
原声明的最简化版:
pFunx b[10];- 原声明:doube(*)() (*e)[9];
变量名为e,先替换左边部分,pFuny为别名一:
typedef double(*pFuny)();
再替换右边的变量e,pFunParamy为别名二
typedef pFuny (*pFunParamy)[9];
原声明的最简化版:
pFunParamy e;
理解复杂声明可用的“右左法则”:
- 从变量名看起,先往右,再往左,碰到一个圆括号就调转阅读的方向;括号内分析完就跳出括号,还是按先右后左的顺序,如此循环,直到整个声明分析完。举例:
int (*func)(int *p); - 首先找到变量名func,外面有一对圆括号,而且左边是一个*号,这说明func是一个指针;然后跳出这个圆括号,先看右边,又遇到圆括号,这说明(func)是一个函数,所以func是一个指向这类函数的指针,即函数指针,这类函数具有int类型的形参,返回值类型是int。
- int (func[5])(int );
func右边是一个[]运算符,说明func是具有5个元素的数组;func的左边有一个,说明func的元素是指针(注意这里的不是修饰func,而是修饰func[5]的,原因是[]运算符优先级比高,func先跟[]结合)。跳出这个括号,看右边,又遇到圆括号,说明func数组的元素是函数类型的指针,它指向的函数具有int类型的形参,返回值类型为int。