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

news/2024/10/22 15:29:49/

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

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

目录

  • C 程序设计教程(04)—— C 语言的数据类型(二):构造数据类型
    • 一、数组类型
        • 1、数组的定义
        • 2、数组的初始化
        • 3、数组元素的访问
        • 4、数组的存储
        • 5、关于数组名
        • 6、字符串的表示
        • 7、字符数组的赋值
    • 二、结构体类型
        • 1、结构体类型
        • 2、结构体类型变量的定义
        • 3、结构体变量的初始化
        • 4、结构体成员的访问
    • 三、共用体(联合体)
        • 1、共用体的定义
        • 2、共用体变量的引用
    • 四、枚举类型
    • 五、用 typedef 定义类型
        • 1、为基本数据类型定义新的类型名
        • 2、为自定义数据类型(结构体、共用体和枚举类型)定义简洁的类型名称
        • 3、为数组定义简洁的类型名称
        • 4、为指针定义简洁的名称

我们已经学过 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个值默认为0int 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;  //定义新的类型名为DATEtypedef 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;

http://www.ppmy.cn/news/10244.html

相关文章

电子书销售是一种可以躺赚的商业模式么?

文章目录前言调研后的结论电子书销售市场规模到底有多大&#xff1f;电子书产业链电子书阅读平台电子书销售平台国外国内其它销售模式探讨创建电子书创建电子书的工具电子书下载好去处相关法规前言 不知何时&#xff0c;有了网赚一词&#xff0c;例如去各大平台撸羊毛薅羊毛&a…

java springboot+mybatis电影售票网站管理系统前台+后台设计和实现

java springbootmybatis电影售票网站管理系统前台后台设计和实现 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java毕设项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言…

Java初识泛型 | 如何通过泛型类/泛型方法实现求数组元素最大值?

目录 一、引言 二、编程分析 1、泛型类实现 思路 代码 2、泛型方法实现 思路 代码 三、拓展&#xff1a;数组排序&#xff08;以冒泡排序为例&#xff09; 1、int类型 原代码 2、泛型类 3、泛型方法 一、引言 给定一个整型数组&#xff0c;求数组中所有元素的最大…

SQL优化实战-0002:select查询不建议使用星号(select *),最好指定具体查询字段

文章目录1.查询时的普遍写法2.问题分析2.1 计算负担2.2 IO负担2.3 覆盖索引失效2.4 缓存压力3.总结1.查询时的普遍写法 select * from the_table_name where ...2.问题分析 2.1 计算负担 数据库需要去解析更多的对象字段、权限、属性&#xff0c;查询数据字典将"*"…

风险事件标签识别之BiLSTM实现的代码+数据

项目介绍:   数据集:风险事件分类的训练集规模是10000+,包含10个一级标签和35个二级标签;大规模无标注的文本规模是亿级,可供选手选择用来进行语言模型训练。数据性质均为新闻资讯数据,并且进行了字符编码(保留了句子划分的标点符号),文中的字符会转换成唯一的ID,I…

哪些程序员适合自由工作?(附平台推荐)

在早些时候进行远程办公&#xff0c;接私活或者跨国进行编程&#xff0c;赚点外快等也不是什么奇怪的事情。但是那时候没有人想到会把这些工作完全变成自己的主要业务——也就是我们说的自由工作。也不知道是哪一个第1个吃了螃蟹的人发现自由工作还不错&#xff0c;于是经过后面…

WebLogic-执行队列

一&#xff0c;Tuning the Application Server 二&#xff0c;执行队列 Using Work Managers to Optimize Scheduled WorkThis chapter describes how WebLogic Server 12.1.3 lets you configure how your application prioritizes the execution of its work using a Work Ma…

【博学谷学习记录超强总结,用心分享|产品经理基础总结和感悟13】

这里写目录标题第一章、概述第二章&#xff0c;内容服务产品分析框架&#xff1a;用户-平台-创作者内容服务平台优化思考第一章、概述 在分析文字类内容产品之前&#xff0c;我们先来思考一下内容产品的本质是什么&#xff1f;笔者认为&#xff0c;所有满足用户需求的信息服务…