c语言之条件编译、结构体、共用体
一、条件编译
编译器根据条件的真假决定是否编译相关的代码,常见的条件编译有两种方法:
- 宏是否定义
- 宏的值
(一)宏是否定义
语法:
#ifdef <macro> …… #else …… #endif
#include <stdio.h> #define _DEBUG_ int main(int argc, char *argv[]) { #ifdef _DEBUG_ printf("The macro _DEBUG_ is defined\n"); #else printf("The macro _DEBUG_ is not defined\n"); #endif }
(二)宏的值
语法:
#if <macro> …… #else …… #endif
#include <stdio.h> #define _DEBUG_ 1 int main(int argc, char *argv[]) { #if _DEBUG_ printf("The macro _DEBUG_ is defined\n"); #else printf("The macro _DEBUG_ is not defined\n"); #endif }
二、结构体
(一)简述
在实际的处理对象中,有许多信息是由多个不同类型的数据组合在一起进行描述,而且这些不同类型的数据是互相联系组成了一个有机的整体。此时,就要用到一种新的构造类型数据——结构体(structure),简称结构。
结构体的使用为处理复杂的数据结构(如动态数据结构等)提供了有效的手段,而且,它们为函数间传递不同类型的数据提供了方便。
(二)概念
结构体是用户自定义的新数据类型,在结构体中可以包含若干个不同数据类型和不同意义的数据项(当然也可以相同),从而使这些数据项组合起来反映某一个信息。
例如,可以定义一个职工worker结构体,在这个结构体中包括职工编号、姓名、性别、年龄、工资、家庭住址、联系电话。这样就可以用一个结构体数据类型的变量来存放某个职工的所有相关信息。并且,用户自定义的数据类型worker也可以与int、double等基本数据类型一样,用来作为定义其他变量的数据类型。
(三)定义
struct 结构体名 { 数据类型 成员名1; 数据类型 成员名2; : . 数据类型 成员名n; };
- 在大括号中的内容也称为“成员列表”或“域表”。
- 其中,每个成员名的命名规则与变量名相同;
- 数据类型可以是基本变量类型和数组类型,或者是一个结构体类型;
- 用分号“;”作为结束符。整个结构的定义也用分号作为结束符
例如:
struct worker { long number; char name[20]; char sex; int age; float salary; char address[80]; };
说明:
结构体类型中的成员名可以与程序中的变量名相同,二者并不代表同一对象,编译程序可以自动对它们进行区分。
最后,总结一下结构体类型的特点:
- 结构体类型是用户自行构造的。
- 它由若干不同的基本数据类型的数据构成。
- 它属于C语言的一种数据类型,与整型、实型相当。因此,定义它时不分配空间,只有用它定义变量时才分配空间。
(四)结构体变量
- 方式一:定义类型同时定义变量
这种形式的定义的一般形式为:
struct 结构体名 { 成员列表; }变量名;
例如:
struct worker { long number; char name[20]; char sex; int age; float salary; char address[80]; } worker1, worker2;
- 方式二:直接定义结构类型变量
这种形式的定义的一般形式为:
struct //没有结构体名 { 成员列表; }变量名;
列如:
struct { long number; char name[20]; char sex; int age; float salary; char address[80]; } worker1, worker2;
- 方式三:符号常量代表结构体类型
#define WORKER struct worker; // WORKER与struct worker完全等效 WORKER { long number; char name[20]; char sex; int age; float salary; char address[80]; }; WORKER worker1, worker2;
(五)结构体使用方式
1、大小
一个结构体变量占用内存的实际大小,也可以利用sizeof求出。它的运算表达式为:
sizeof(运算量) //求出给定的运算量占用内存空间的字节数
其中运算量可以是变量、数组或结构体变量,可以是数据类型的名称。
例如:
#include <stdio.h> #define WORKER struct worker // WORKER与struct worker完全等效 WORKER { long number; char name[20]; char sex; int age; float salary; char address[80]; }; int main(int argc, char *argv[]) { WORKER worker1; printf("%d\n",sizeof(worker1)); }
2、结构体初始化与变量初始化
#include <stdio.h> #include <string.h> #define N 32 struct student { int no; char name[N]; float score; }s3={3, "zhangsan", 90}, s4={4, "lisi", 80}; int main(int argc, char *argv[]) { struct student s1, s2; strcpy(s1.name, "xiaoming"); // 通过 strcpy 给变量赋值 s1.score = 90; // 给分数赋值 s1.name[0] = 'k'; //修改名称中的第一个字符 printf("%d %s %2.f\n", s1.no, s1.name, s1.score); // -1209245696 kiaoming 90 s2 = s1; s2.no = 2; printf("%d %s %2.f\n", s2.no, s2.name, s2.score); // 2 kiaoming 90 }
结构变量的初始化:
与其他类型变量一样,也可以给结构体的每个成员赋初值,这称为结构体的初始化。一种是在定义结构体变量时进行初始化,语法格式如下:
struct 结构体名 变量名={初始数据表};
另一种是在定义结构体类型时进行结构体变量的初始化。
struct 结构体名
{
成员列表;
}变量名={初始数据表};
结构变量使用方式:
在定义了结构体变量后,就可以用不同的赋值方法对结构体变量的每个成员赋值。例如:
strcpy(worker1.name,”Zhang San”);
worker1.age=26;
strcpy(worker1.phone,”1234567”);
worker1.sex=’m’;
:
:
除此之外,还可以引用结构体变量成员的地址以及成员中的元素。例如:引用结构体变量成员的首地址&worker1.name;引用结构体变量成员的第二个字符worker1.name[1];引用结构体变量的首地址&worker1。
注意
(1)不能将一个结构体类型变量作为一个整体加以引用,而只能对结构体类型变量中的各个成员分别引用。
例如,对上面定义的结构体类型变量wan,下列引用都是错误的:
cout<<wan; cin>>wan; 但是可以如下引用: cout<<wan.name; cin>>wan.name;
(2)如果成员本身又属一个结构体类型,则要用若干个成员运算符,一级一级地找到最低的一级成员。只能对最低级的成员进行赋值或存取以及运算。例如,对上面定义的结构体类型变量worker1,可以这样访问各成员:
worker1.age worker1.name worker1.birthday.year worker1.birthday.month worker1.birthday.day
注意:不能用worker1.birthday来访问worker1变量中的成员birthday,因为birthday本身是一个结构体变量。
(3)对成员变量可以像普通变量一样进行各种运算(根据其类型决定可以进行的运算)。例如:
worker2.age=worker1.age; sum=worker1.age+worker2.age; worker1.age++;
(4)在数组中,数组是不能彼此赋值的,而结构体类型变量可以相互赋值。
在C程序中,同一结构体类型的结构体变量之间允许相互赋值,而不同结构体类型的结构体变量之间不允许相互赋值,即使两者包含有同样的成员。
3、嵌套结构体
#include <stdio.h> #include <string.h> #define N 32 struct student { int no; char name[N]; struct birthday{ int year; int month; int day; }date; float score; }s3={3, "zhangsan", 90}, s4={4, "lisi", 80}; int main(int argc, char *argv[]) { struct student s1; strcpy(s1.name, "xiaoming"); // 通过 strcpy 给变量赋值 s1.score = 90; // 给分数赋值 s1.name[0] = 'k'; //修改名称中的第一个字符 s1.date.year=1993; s1.date.month=03; s1.date.day=10; printf("%d %s %d-%d-%d %2.f\n", s1.no, s1.name, s1.date.year, s1.date.month, s1.date.day, s1.score); // -1210147384 kiaoming 1993-3-10 90 }
三、结构体数组
(一)定义
具有相同结构体类型的结构体变量也可以组成数组,称它们为结构体数组。结构体数组的每一个数组元素都是结构体类型的数据,它们都分别包括各个成员(分量)项。
定义结构体数组的方法和定义结构体变量的方法相仿,只需说明其为数组即可。可以采用三种方法:
- 方式一 在定义结构体类型同时定义结构体数组
结构体数组的定义形式如下:
struct 结构体名
{
成员表列;
}数组名[元素个数];
例如:
struct student { char name[20]; char sex; int age; char address[20]; }stu[3];
- 方式二 直接定义结构体数组
struct //没有结构体名
{
成员表列;
}数组名[元素个数];
例如:
struct { char name[20]; char sex; int age; char address[20]; }stu[3];
- 方式三 先定义结构体类型,再用它定义结构体数组
struct 结构体名
{
成员表列;
};
struct 结构体名
struct student{ char name[20]; char sex; int age; char address[20]; }; struct student stu[3];
(二)数组初始化
结构体数组在定义的同时也可以进行初始化,并且与结构体变量的初始化规定相同,只能对全局的或静态存储类别的结构体数组初始化。
结构体数组初始化的一般形式是:
struct 结构体名 { 成员列表; }; struct 结构体名 数组名[元素个数]={初始数据表};
或者:
struct 结构体名 { 成员表列; }数组名[元素个数]={初始数据表};
由于结构体变量是由若干不同类型的数据组成,而结构体数组又是由若干结构体变量组成。所以要特别注意包围在大括号中的初始数据的顺序,以及它们与各个成员项间的对应关系。
(三)数组的使用
一个结构体数组的元素相当于一个结构体变量,因此前面介绍的有关结构体变量的规则也适应于结构体数组元素。以上面定义的结构体数组stu[3]为例说明对结构体数组的引用:
1、引用某一元素中的成员
若要引用数组第二个元素的name成员,则可写为:
stu[1].name
2、同一类型结构体变量相互赋值
可以将一个结构体数组元素值赋给同一结构体类型的数组中的另一个元素,或赋给同一类型的变量:
struct student stu[3], student1;
现在定义了一个结构体类型的数组,它有3个元素,又定义了一个结构体类型变量student1,则下面的赋值是合法的:
student1=stu[0]; stu[0]=stu[1]; stu[1]=student1;
3、不能输出整个结构体变量
不能把结构体数组元素作为一个整体直接进行输入输出。如:
printf("...", stu[0]); scanf("...", &stu[0]);
都是错误的。
只能以单个成员为对象进行输入输出,如:
scanf(“…”,stu[0].name); scanf(“…”,&stu[1].num); printf(“…”,stu[0].name); printf(“…”,stu[1].num);
(四)案例
#include <stdio.h> #define N 20 struct student { int no; char name[N]; float score; }s2[5]; int main() { int i; struct student s1[5] = {{1, "s1", 85}, {2, "s2", 90}}; printf("%d-%s-%f\n", s1[0].no, s1[0].name, s1[0].score); for(int i = 0; i < sizeof(s2)/sizeof(struct student); i++) { printf("%d-%s-%f\n", s2[i].no, s2[i].name, s2[i].score); } return 0; }
四、结构体指针
(一)结构体指针
可以设定一个指针变量用来指向一个结构体变量。此时该指针变量的值是结构体变量的起始地址,该指针称为结构体指针。
结构体指针与前面介绍的各种指针变量在特性和方法上是相同的。与前述相同,在程序中结构体指针也是通过访问目标运算“*”访问它的对象。结构体指针在程序中的一般定义形式为:
struct 结构体名 *结构指针名;
其中的结构体名必须是已经定义过的结构体类型。
例如:
对于结构体类型struct student
,可以说明使用这种结构体类型的结构指针如下:
struct student *pstu
其中pstu是指向struct student结构体类型的指针。结构体指针的说明规定了它的数据特性,并为结构体指针本身分配了一定的内存空间。但是指针的内容尚未确定,即它指向随机的对象。
(二)结构体指针的使用
当表示指针变量p所指向的结构体变量中的成员时,“(*结构体指针名).成员名”这种表示形式总是需要使用圆括号,显得很不简炼。因此,对于结构体指针指向的结构体成员项,给出了另外一种简洁的表示方法,如下表示:
结构体指针名->成员名
它与前一种表示方法在意义上是完全等价的。例如,结构体指针p指向的结构体变量中的成员name可以表示如下:
(*p).name 或 p->name
(三)案例
1、案例一
#include <stdio.h> #define N 20 struct student { int no; char name[N]; float score; }; int main(int argc, const char *argv[]) { int i; struct student s1[] = {{1, "s1", 85}, {2, "s2", 90}}; struct student *p; p = s1; p = &s1[0]; for(i = 0; i < sizeof(s1)/sizeof(struct student); i++) { printf("%d-%s-%f\n", p->no, p->name, (*p).score); p++; } return 0; }
2、案例二
#include <stdio.h> #define N 20 struct student { int no; char name[N]; float score; }; void printf_info(struct student *p, int n) { int i; for(i = 0; i < n; i++) { printf("%d-%s-%f\n", (p+i)->no, (p+i)->name, (p+i)->score); } } int main(int argc, const char *argv[]) { int i; struct student s1[] = {{1, "s1", 85}, {2, "s2", 90}}; printf_info(s1, sizeof(s1)/sizeof(struct student)); return 0; }
五、共用体
(一)什么是共用体
在C语言中,不同数据类型的数据可以使用共同的存储区域,这种数据构造类型称为共用体,简称共用,又称联合体。共用体在定义、说明和使用形式上与结构体相似。两者本质上的不同仅在于使用内存的方式上。
定义一个共用体类型的一般形式为:
union 共用体名 { 成员表列; };
例如:
union gy { int i; char c; float f; };
这里定义了一个共用体类型union gy,它由三个成员组成,这三个成员在内存中使用共同的存储空间。由于共用体中各成员的数据长度往往不同,所以共用体变量在存储时总是按其成员中数据长度最大的成员占用内存空间。
在这一点上共用体与结构体不同,结构体类型变量在存储时总是按各成员的数据长度之和占用内存空间。
例如,定义了一个结构体类型:
struct gy { int i; char c; float f; };
则结构体类型struct gy的变量占用的内存大小为2+1+4=7个字节(不考虑字节对齐)。
注意:
在使用共用体类型变量的数据时要注意:在共用体类型变量中起作用的成员是最后一次存放的成员,在存入一个新的成员后原有的成员就失去作用。如有以下赋值语句:
a.i = 1; a.c = ’a’; a.f = 1.5;
完成以上三个赋值运算以后,a.f是有效的,a.i和a.c已经无意义了。
(二)共用体的使用
在程序中经常使用结构体与共用体相互嵌套的形式。
即共用体类型的成员可以是结构体类型,或者结构体类型的成员是共用体类型。
例如,下列结构体类型datas的第三个成员是共用体类型:
struct datas { char *ps; int type; union { float fdata; int idata; char cdata; }udata; };
六、typedef
在C语言中,允许使用关键字typedef定义新的数据类型,其语法如下:
typedef <已有数据类型> <新数据类型>;
如:
typedef int INTEGER;
这里新定义了数据类型INTEGER, 其等价于int:
INTEGER i; <==> int i;
在C语言中经常在定义结构体类型时使用typedef,例如
typedef struct _node_ { int data; struct _node_ *next; } listnode, *linklist;
这里定义了两个新的数据类型listnode
和linklist
。其中listnode
,等价于数据类型struct _node_
而 linklist
等价于struct _node_ *