C 程序设计教程(04)—— C 语言的数据类型(二):构造数据类型

C 程序设计教程(04)—— C 语言的数据类型(二):构造数据类型

在这里插入图片描述

该专栏主要介绍 C 语言的基本语法,作为《程序设计语言》课程的课件与参考资料,用于《程序设计语言》课程的教学,供入门级用户阅读。

我们已经学过 int、char、float、double 等基本数据类型,这些数据类型只能表示某些特定的数据,不能完全表示现实中所有事物。例如:表示一个人的特征,则这个人的姓名、年龄、性别等信息就无法用基本数据类型来完成。因此,C 语言提供了构造类型。

C 语言中的构造数据类型包括:数组类型、结构体类型、共用体类型和枚举类型。

一、数组类型

数组是一个由若干相同类型的变量组成的集合,引用这些变量时可以使用同一个名称。数组由连续的存储单元组成,最低地址对应于数组的第一个元素,最高地址对应于数组的最后一个元素,数组可以是一维、二维和多维数组。

1、数组的定义

数组的定义格式如下:

type array_name[size];       //一维数组
type array_name[size][size]; //二维数组

说明:

(1)在定义数组时,数组的元素个数([size])必须是常数(C99标准支持变长数组,但大多编译器是不支持的)。

(2)对于 m 行 n 列的二维数组,可以理解为该二维数组有 m 个元素,每个元素是一个有 n 个元素的一维数组。

2、数组的初始化

数组的初始化就是给数组元素赋初始值,数组使用 { } 初始化,在大小给定的情况下,可以完全初始化,也可以不完全初始化(其余元素默认为 0)。在大小没有给定的情况下根据初始化内容确定数组的大小。例如:

#include<stdio.h>
int main()
{
    
    
    int i,j;
	int arr1[10]={
    
    1,2,3,4,5,6,7,8,9,0}; //完全初始化,定义包含10个整型元素的数组,10个数组元素都已赋初值
    int arr2[10]={
    
    1,2,3};  //不完全初始化,定义的数组有10个元素,但只给定了3个值,其余7个值默认为0
    int arr3[]={
    
    1,2,3};    //数组大小未给定,根据初始化内容确定数组大小(含有三个整型元素)
    int arr4[2][3]={
    
    {
    
    0,1,2},{
    
    3,4,5}}; //完全初始化,二维数组可以看作若干个一维数组,分别初始化
    int arr5[][3]={
    
    {
    
    0,1,2},{
    
    3,4,5}};  //省略行数
    // int arr6[2][]={
    
    {0,1,2},{3,4,5}};  //错误!列数不可省略,编译器编不过去

    printf("arr1: ");
    for(i=0;i<10;i++)
    {
    
    
        printf("%d  ",arr1[i]);
    }
    printf("\n\n");      

    printf("arr2: ");
    for(i=0;i<10;i++)
    {
    
    
        printf("%d  ",arr2[i]);
    }
    printf("\n\n");  
    
    printf("arr3: ");
	for(i=0;i<3;i++)  
    {
    
    
        printf("%d  ",arr3[i]);
    }
    printf("\n\n");  

    printf("arr4: ");
	for(i=0;i<2;i++) 
    {
    
    
        for(j=0;j<3;j++) 
		printf("%d  ",arr4[i][j]);
		printf("\n");
    }
    printf("\n"); 
	   
    printf("arr5: ");
	for(i=0;i<2;i++) 
    {
    
    
        for(j=0;j<3;j++) 
		printf("%d  ",arr5[i][j]);
		printf("\n");
    }
    return 0;
}

说明:二维数组可以省略行数,但不可省略列数。

上述程序的运行结果如下:

在这里插入图片描述

3、数组元素的访问

数组通过下标引用操作符([ ])实现对数组元素的访问。比如:arr[1] 代表数组 arr 中下标为 1 的元素,arr[i] 代表数组中下标为 i 的元素。数组元素的下标是从 0 开始的。例如:

#include<stdio.h>
int main()
{
    
    
    int i;
    int arr[10];
	for(i=0;i<10;i++)
	{
    
    
		arr[i]=i*2+1;
	}
	
	printf("arr: ");
	for(i=0;i<10;i++)
	{
    
    
		printf("%d  ",arr[i]);
	}
    return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

4、数组的存储

无论是一维数组还是二维数组,数组内的元素在内存中是连续存储的,相邻的两个元素之间的地址都相差一个固定的值(值的大小和数组元素的类型有关。如果数组元素类型是 int,则相差 4 个字节)。例如:

#include<stdio.h>
int main()
{
    
    
    int i,j;
    int arr1[]={
    
    1,2,3,4,5,6};
    int arr2[3][5]={
    
    {
    
    1,2,3,4,5},
	    {
    
    11,22,33,44,55},
		{
    
    111,222,333,444,555}};
    printf("输出arr1的地址:\n");
	for(i=0;i<6;i++)
	{
    
    
		printf("arr1[%d]:%p\n",i,&arr1[i]);
	}

    printf("输出arr2的地址:\n");
	for(i=0;i<3;i++)
	{
    
    
		for(j=0;j<5;j++)
		{
    
    
			printf("arr2[%d][%d]:%p\n",i,j,&arr2[i][j]);
		}		
	}	

    return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

5、关于数组名

数组名是第一个数组元素的地址。当求 sizeof(数组名) 时,得到的是整个数组所占空间的大小;&数组名,是整个数组的地址。例如:

#include<stdio.h>
int main()
{
    
    
    int arr1[5]={
    
    1,2,3,4,5};
    int arr2[2][3]={
    
    {
    
    1,2,3},{
    
    4,5,6}};
    //结果:20(5个元素,每个元素4个字节)
    printf("arr1的大小:%d\n",sizeof(arr1)); 
	//结果:24(整个数组大小)
    printf("arr2的大小:%d\n",sizeof(arr2)); 
	//结果:12(arr2[0]也是数组名,是二维数组第一行元素的数组名)
    printf("arr2[0]的大小:%d\n",sizeof(arr2[0]));
    //以下三个地址一样,但是第三个含义与前两个不一样
    printf("arr1的地址:%p\n",arr1);
    printf("第一个元素的地址:%p\n",&arr1[0]);
    printf("整个数组的地址:%p\n",&arr1);
    //前两个地址一样(都是arr1[1]的地址)
	printf("arr1+1的地址:%p\n",arr1+1);
    printf("arr1[0]+1的地址:%p\n",&arr1[0]+1);
    //&arr是整个数组的地址,+1跳过整个数组
    printf("&arr1+1的地址:%p\n",&arr1+1);
    return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

6、字符串的表示

C 语言并没有字符串类型,但允许使用字符串常量。字符串常量是用一对双引号括起来的一串字符。例如:

“China”、“OS”、“yes”、"0373-3048111"等。

字符串常量在存储时,系统自动在字符串的末尾加一个“字符串结束标志”(即:ASCII 码为 0 的字符 NULL),常用“\0”表示。因此,程序中长度为 n 个字符的字符串常量,在内存中占用 n+1 个字节的存储空间。

在定义字符型数组时,必须比要存放的最大字符数多一个字符。例如:

#include<stdio.h>
int main()
{
    
    
    char str[20]="I am a student.";
    printf("%s\n",str);
    return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

7、字符数组的赋值

定义字符数组时可以同时对数组进行初始化,但定义完成后,就不能按初始化的形式对其赋值了。例如:

char a[10]="abcdefgh";  //正确
char b[10];
b="abcdefgh";           //错误

(1)单字符赋值:通过数组下标方式或指针方式,引用数组元素,进行赋值。

(2)字符串赋值:使用 string.h 头文件中的字符串操作函数(strcpy)进行赋值。

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	//单字符赋值 
	char a[10];
	a[0]='C';a[1]='h';a[2]='i';a[3]='n';a[4]='a';
	//字符串赋值
	char b[10];
	strcpy(b,"abcdefgh");
	printf("数组a的内容: %s\n",a);
	printf("数组b的内容: %s\n",b);
	return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

二、结构体类型

结构体是由一组称为成员(成员变量)的不同数据所组成的,其中每个成员可以具有不同的类型。结构体通常用来表示类型不同但是又相关的若干数据。

结构体的成员可以是数组、指针变量、甚至是其他结构体,一般描述复杂对象时用到结构体。

结构体采用关键字 struct 来进行构建。

1、结构体类型

例如:成绩表的结构如下:

班级 学号 姓名 操作系统 数据结构 计算机网络
字符串 长整型 字符串 实型 实型 实型

通讯录的结构如下:

姓名 工作单位 家庭住址 邮编 电话号码 E-mail
字符串 字符串 字符串 字符串 字符串 字符串

把成绩和通讯录定义为结构体类型:

//结构体类型的定义格式:
struct 结构体名称
{
    
    
    成员列表
};

//定义“成绩”结构体
struct score
{
    
    
    char grade[20];  //班级
    long number;     //学号
    char name[20];   //姓名
    float os;        //操作系统
    float datastru;  //数据结构
    float compnet;   //计算机网络
};

//定义“通讯录”结构体
struct addr
{
    
    
    char name[20];         //姓名
    char department[30];   //工作单位
    char address[30];      //家庭住址
    char box[6];           //邮编
    char phone[20];        //电话
    char email[50];        //E-mail
};

2、结构体类型变量的定义

结构体类型变量的定义与其他类型的变量的定义相同,但结构体类型需要预先定义。结构体变量的定义有三种形式:

(1)先定义结构体类型,再定义结构体类型变量

//定义“成绩”结构体
struct score
{
    
    
    char grade[20];  //班级
    long number;     //学号
    char name[20];   //姓名
    float os;        //操作系统
    float datastru;  //数据结构
    float compnet;   //计算机网络
};
struct score student1,student2;  //定义结构体类型变量

(2)定义结构体类型同时定义结构体变量

struct score
{
    char grade[20];  //班级
    long number;     //学号
    char name[20];   //姓名
    float os;        //操作系统
    float datastru;  //数据结构
    float compnet;   //计算机网络
} score student1,student2;  //定义结构体类型变量
struct score student3;      //定义其他变量

(3)直接定义结构体类型变量

这种定义方式由于没有为结构体类型指定名称,则只能使用一次。不能再次定义该结构体类型的其他变量。

struct 
{
    
    
    char grade[20];  //班级
    long number;     //学号
    char name[20];   //姓名
    float os;        //操作系统
    float datastru;  //数据结构
    float compnet;   //计算机网络
} student1,student2;  //定义结构体类型变量

3、结构体变量的初始化

结构体变量的初始化采用 { },成员变量间用逗号隔开便可。例如:

#include<stdio.h>
int main()
{
    
    
	struct score
	{
    
    
	    char grade[20];  //班级
    	long number;     //学号
    	char name[20];   //姓名
    	float os;        //操作系统
    	float datastru;  //数据结构
    	float compnet;   //计算机网络
	};
	struct score s1={
    
    "xinguan211",2021021401,"tom",87.5,90.0,87.0};    //定义结构体类型变量
	printf("%s\n%d\n%s\n%lf\n%lf\n%lf\n",s1.grade,s1.number,s1.name,s1.os,s1.datastru,s1.compnet);
	return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

4、结构体成员的访问

(1)采用点操作符(.)访问结构体成员

相同结构体类型的结构体能够相互赋值:如有 struct stu s1,s2; 就可以有s1=s2,不同结构体类型的结构体不能相互赋值。结构体成员引用的一般格式:结构体变量名.成员名。例如:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	struct stu
	{
    
    
   		char name[20];
   		int age;
   		char id[20];
	};

	struct stu s1={
    
    "tom",20,"20210224111"};
	struct stu s2;
	s2=s1;
	strcpy(s2.name,"jerry");
	s1.age=22;
	strcpy(s2.id,"20210224345");
	printf("学生s1的信息如下:\n");
	printf("name: %s\nage: %d\nid: %s\n\n",s1.name,s1.age,s1.id);
	printf("学生s2的信息如下:\n");
	printf("name: %s\nage: %d\nid: %s\n",s2.name,s2.age,s2.id);
	return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

(2)采用(->)操作符来访问结构体成员

当我们访问的不是一个结构体变量,而是指向一个结构体变量的指针,这时我们可以使用(->)操作符来访问结构体成员。例如:

#include<stdio.h>
struct stu
{
    
    
   	char name[20];
   	int age;
   	char id[20];
};
void print(struct stu *s2) 
{
    
    
   	printf("%s %d %s\n",s2->name,s2->age,s2->id);
}

int main()
{
    
    
	struct stu s1={
    
    "tom",20,"20210224111"};
	print(&s1);
	return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

三、共用体(联合体)

一般情况下,系统会为每一个变量开辟独立的存储空间。例如:

char a;
int b;
float c;

系统将为这三个变量分别开辟1个字节、2个字节和4个字节的存储空间。使用共用体类型(union)可以让这三个变量共用一块内存空间。

1、共用体的定义

共用体的定义格式如下:

union 共用体名
{
    
    成员列表};

以下代码定义共用体类型的为data,该共用体中有三个成员,分别为a、b、c,它们占用同一个起始地址的存储空间,内存长度等于最长的成员的长度。

//定义共用体类型data
union data
{
    
    
	char a;
	int b;
	float c;
};

和定义结构体变量一样,可以用以下三种方式定义共用体变量:

// 格式1
union data
{
    
    
	char a;
	int b;
	float c;
};
union data x,y;

// 格式2
union data
{
    
    
	char a;
	int b;
	float c;
} x,y;

// 格式3
union 
{
    
    
	char a;
	int b;
	float c;
} x,y;

2、共用体变量的引用

定义了共用体变量之后,可以采用如下方式引用共用体变量的成员:

变量名.成员名

对共用体变量的说明:

(1)同一个内存段可以用来存放几种不同类型的成员,但每一时刻只能存放其中一种。也就是说,每一时刻只有一个成员起作用。

(2)共用体变量中起作用的是最后一次被赋值的成员。例如:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	union data
	{
    
    
		char a;
		int b;
		float c;
	};
	union data x;
	x.a='a';
	x.b=10;
	x.c=15.3;
	printf("%c\n",x.a);
	printf("%d\n",x.b);
	printf("%f\n",x.c);
	return 0;
}

完成以上三个赋值运算之后,只有 x.c 是有效的,x.a 和 x.b 已经无意义了。

以上程序的运行结果如下:

在这里插入图片描述

(3)共用体变量的地址和它的各成员的地址相同。例如:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	union data
	{
    
    
		char a;
		int b;
		float c;
	};
	union data x;
	x.a='a';
	x.b=10;
	x.c=15.3;
	printf("%p\n",&x);
	printf("%p\n",&x.a);
	printf("%p\n",&x.b);
	printf("%p\n",&x.c);
	return 0;
}

以上程序的运行结果如下:

在这里插入图片描述

(4)不能对共用体变量赋值,也不在定义共用体变量时对它进行初始化。例如:

union data
{
    
    
    char a;
    int b;
    float c;
} x={
    
    'a',10,15.3}; //错误,不能对共用体变量初始化
a=1;               //错误,不能对共用体变量赋值
union data m=a;    //错误

(5)不能把共用体变量作为函数参数,也不能使函数返回共用体类型的值,但可以使用指向共用体变量的指针。

(6)共用体类型可以出现在结构体类型定义中,也可以定义共用体数组。反之,结构体也可以出现在共用体类型的定义中,数组也可以作为共用体的成员。

四、枚举类型

枚举就是把变量可能取到的值一一列举出来,变量的取值只局限于列举出来的值的范围。声明枚举类型使用 enum 保留字,例如:

//定义枚举类型
enum weekday{
    
    sun,mon,tus,wed,thu,fri,sat};
//定义枚举类型的变量
enum weekday workday,weekend;
//也可以直接定义枚举类型的变量
enum {
    
    sun,mon,tus,wed,thu,fri,sat} workday,weekend;
//为枚举类型的变量赋值
workday = mon;
weekend = sun;

说明:

(1)枚举元素按常量处理,称为枚举常量,不能给枚举元素赋值。例如以下赋值是错误的:

sun = 0;
mon = 1;

(2)枚举元素作为常量,C 语言按枚举元素定义的顺序将他们的值定义为0、1、2、…。在前面的定义中,sun 的值为 0,mon 的值为 1,…,sat 的值为 6。可以改变枚举元素的值,如:

#include<stdio.h>
int main()
{
    
    
    enum weekday{
    
    sun=7,mon=1,tus,wed,thu,fri,sat};
    enum weekday workday,weekend;
    workday = mon;
    weekend = sun;
    printf("monday:%d\nsunday:%d",workday,weekend);
    return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

(3)枚举值可用于构造条件,例如:

if(workday == mon) {
    
    ...}
if(workday > sun) {
    
    ...}

(4)整数不能直接赋给枚举变量,因为他们属于不同的类型。需要进行强制类型转换才能赋值。例如:

workday = 2; //错误
workday = (enum weekday)2; //相当于将序号为2的枚举元素赋给workday,等价于下面的语句:
workday = tue;

(5)不能对枚举类型的变量直接读写,只能输出枚举变量的序号。

五、用 typedef 定义类型

除了可以直接使用标准类型名(如:int、char、float、double、long 等)和结构体、共用体、指针、枚举类型外,还可以用 typedef 定义新的类型名来代替已有的类型名。

例如:

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	typedef struct
	{
    
    
		int month;
		int day;
		int year;
	} DATE;  //定义新的类型名为DATE
	typedef int ARR[10]; 
	DATE birthday={
    
    10,5,2000};
	ARR a={
    
    0,1,2,3,4,5,6,7,8,9};
	int i;
	printf("birthday: %d-%d-%d\n\n",birthday.year,birthday.month,birthday.day);
	for(i=0;i<10;i++)
	{
    
    
		printf("数组元素a[%d]的值:%d\n",i,a[i]);
	}
	return 0;

以上程序的输出结果如下:

在这里插入图片描述

typedef 主要用于以下几种情形:

1、为基本数据类型定义新的类型名

比如:用 typedef 来定义与平台无关的类型。
可以定义一个名称为 REAL 的浮点类型:typedef long double REAL;
在不支持 long double 的机器上,可以修改为:typedef double REAL;
如果连 double 都不支持,可以修改为:typedef float REAL;

当跨平台时,只要修改 typedef 本身就行,不用对其他源码做任何修改。

#include<stdio.h>
#include<string.h>
int main()
{
    
    
	typedef double REAL;
	REAL a=100.2;
	printf("%f",a);
	return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

2、为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称

例1:

typedef struct tagPoint
{
    
    
    double x;
    double y;
    double z;
} Point;

//上面的代码实际上完成了两个操作:
//1、定义一个新的结构类型
//其中:struct 关键字和 tagPoint 一起构成了这个结构类型
struct tagPoint
{
    
    
    double x;
    double y;
    double z;
} ;

//2、使用 typedef 为这个新的结构起了一个别名,叫 Point,即:
typedef struct tagPoint Point;

//然后就可以像 int 和 double 那样直接使用 Point 定义变量,如下面的代码所示:
Point oPoint1={
    
    1001000};
Point oPoint2;

例2:

#include <stdio.h>
//定义两个类型,一个整型的别名 INTEGER,一个整型指针 PINT,这两个变量没有联系,是独立的。
int main()
{
    
    
    typedef int INTEGER, *PINT;
    INTEGER a,c;
    PINT b;
	a=10;  
	c=20;  
    b=&c;
    printf("&a=%p\n",&a);
    printf("b=%p\n",b);
    printf("a=%d\n",a);
    printf("*b=%d\n",*b);
    return 0;
}

以上程序的输出结果如下:

在这里插入图片描述

例3:

typedef int ElemType; //定义顺序表的数据元素为整数。
typedef struct
{
    
    
 	ElemType data[MAXSIZE];   //用数组存储顺序表中的元素
	unsigned int length;      //顺序表中元素的个数
} SeqList,*PSeqList;  //定义两个类型,一个是结构体别名SeqList和一个结构体指针PSeqList。

3、为数组定义简洁的类型名称

例如:

typedef int INT_ARRAY_100[100];
INT_ARRAY_100 arr;

4、为指针定义简洁的名称

例如:

typedef char* PCHAR;
PCHAR pa;

猜你喜欢

转载自blog.csdn.net/weixin_44377973/article/details/128590993
今日推荐