该专栏主要介绍 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)。在大小没有给定的情况下根据初始化内容确定数组的大小。例如:
#includeint 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 开始的。例如:
#includeint 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 个字节)。例如:
#includeint 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(数组名) 时,得到的是整个数组所占空间的大小;&数组名,是整个数组的地址。例如:
#includeint 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 个字节的存储空间。
在定义字符型数组时,必须比要存放的大字符数多一个字符。例如:
#includeint 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#includeint 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、结构体类型例如:成绩表的结构如下:
班级 | 学号 | 姓名 | 操作系统 | 数据结构 | 计算机网络 |
---|---|---|---|---|---|
字符串 | 长整型 | 字符串 | 实型 | 实型 | 实型 |
通讯录的结构如下:
姓名 | 工作单位 | 家庭住址 | 邮编 | 电话号码 | |
---|---|---|---|---|---|
字符串 | 字符串 | 字符串 | 字符串 | 字符串 | 字符串 |
把成绩和通讯录定义为结构体类型:
//结构体类型的定义格式:
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、结构体变量的初始化结构体变量的初始化采用 { },成员变量间用逗号隔开便可。例如:
#includeint 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#includeint 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)采用(->)操作符来访问结构体成员
当我们访问的不是一个结构体变量,而是指向一个结构体变量的指针,这时我们可以使用(->)操作符来访问结构体成员。例如:
#includestruct 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#includeint 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#includeint 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。可以改变枚举元素的值,如:
#includeint 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#includeint 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#includeint 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={100,100,0};
Point oPoint2;
例2:
#include//定义两个类型,一个整型的别名 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;
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
售后响应及时
7×24小时客服热线数据备份
更安全、更高效、更稳定价格公道精准
项目经理精准报价不弄虚作假合作无风险
重合同讲信誉,无效全额退款