------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
一、结构体
数组:只能由多个相同类型的数据构成
结构体:可以由多个不同类型的数据构成
1.结构体的定义方法
1)结构体类型的定义
#include <stdio.h>
int main()
{
// 1.定义结构体类型
// 下面的Person不是变量名,struct和Person共同组成结构体类型的名称
struct Person
{ // 里面的3个变量,可以称为是结构体的成员或者属性
int age; // 年龄
double height; // 身高
char *name; // 姓名
}; // 注意这里要写分号
return 0;
}
tips:定义结构体类型时,系统是不分配内存空间的。系统只分配内存空间给实际的结构体变量。
tips:结构体类型不能重复定义。
2)结构体变量的定义
#include <stdio.h>
int main()
{
// 1.定义结构体类型
struct Person
{
int age;
double height;
char *name;
};
// 2.根据结构体类型,定义结构体变量
// struct Person是结构体类型名称,p才是结构体变量的名称
// 只能在定义定义结构体变量时,对所有元素同时进行初始化。
struct Person p = {20, 1.55, "jack"};
/* 这样的写法是错误的
struct Person p;
p = {30, 1.67, "jake”}; */
p.age = 30; // 定义结构体变量后,仍然可以单独对每个元素进行赋值
p.name = "rose";
printf("age=%d, name=%s, height=%f\n", p.age, p.name, p.height);
// 还可以这样初始化各元素
struct Person p2 = {.height = 1.78, .name="jim", .age=30};
// 可以这样,把p2的各元素一起赋值给p的各元素
p = p2;
return 0;
}
3)结构体变量的3种定义方式
// 定义结构体变量的3种方式
// 1> 先定义类型,再定义变量(分开定义)
struct Student
{
int age;
};
struct Student stu1;
// 2> 定义类型的同时定义变量
struct Student2
{
int age;
} stu2; // 这里同时定义了变量 stu2, 它的变量类型是 stuct Student2
struct Student2 stu3; // 还可以用第一种方式继续定义其他struct Student2类型的变量
// 3> 定义类型的同时定义变量(省略了类型名称)
struct
{
int age;
} stu; // 这种方法的缺点是不能继续定义该类型的其他变量了,因为它没有类型名称,只有变量名称
4)结构体类型的作用域
1> 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
2> 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
2.结构体内存分析
原则1,数据成员对齐规则:结构体中的数据成员,第一个数据成员放在offset为0的地方,其它数据成员存储的起始位置要从其自身大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储)。(offset:偏移量,这里指相对于结构体地址而言。)
原则2,结构体作为成员:如果一个结构体作为另一个结构体的成员,则这个结构体类型的成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储。)
原则3,收尾工作:结构体的总大小,也就是sizeof的结果,必须是其内部基本最大成员的整数倍,不足的要补齐。
#include <stdio.h>
int main()
{
// 补齐算法 :结构体分配的空间为:大于所有成员所占内存之和的最大成员所占内存数的最小倍数。
struct Student //4+8=12,结构体所占内存=12补4=16,16为8的最小倍数(大于12的)。
{
int age;// 4个字节
char *name; // 8个字节 指针固定占8个字节!
};
//定义结构类型时,系统不会分配内存。系统只给真实的结构体分配内存。上面的结构体类型定义是不占用内存空间的。
struct Student stu; // 也就说,代码执行到这一行时,系统才分配16字节内存空间给结构体stu。
// 补齐算法(对齐算法)
// 结构体所占用的存储空间 必须是 最大成员字节数的倍数
int s = sizeof(stu);
printf("%d\n", s); // 执行结果是 16 ,我就不上图了。大家可以自己copy代码去试一试。
return 0;
}
2)内存储存细节
#include <stdio.h>
int main()
{
// 1.定义结构体类型(并不会分配存储空间)
struct Date
{
int year;
int month;
int day;
};
// 2.定义结构体变量(真正分配存储空间)
struct Date d1 = {2011, 4, 10};
return 0;
}
3.指向结构体的指针
/*
1.指向结构体的指针的定义
struct Student *p;
2.利用指针访问结构体的成员
1> (*p).成员名称
2> p->成员名称
*/
#include <stdio.h>
int main()
{
struct Student
{
int no;
int age;
};
// 结构体变量
struct Student stu = {1, 20};
// 指针变量p,将来指向struct Student类型的数据
struct Student *p;
// 指针变量p指向了stu变量
p = &stu;
p->age = 30;
// 第一种方式
printf("age=%d, no=%d\n", stu.age, stu.no);
// 第二种方式
printf("age=%d, no=%d\n", (*p).age, (*p).no);
// 第三种方式
printf("age=%d, no=%d\n", p->age, p->no);
return 0;
}
二、枚举
枚举是C语言中的一种基本数据类型,并不是构造类型,它可以用于声明一组常数。1.枚举的定义
1)枚举类型的定义
一般形式为:enum 枚举名 {枚举元素1,枚举元素2,……};
enum Season {spring, summer, autumn, winter};
2)枚举变量的定义
跟结构体一样,有3种方式定义枚举变量
1>先定义枚举类型,再定义枚举变量
enum Season {spring, summer, autumn, winter};
enum Season s;
2>定义枚举类型同时,定义枚举变量
enum Season {spring, summer, autumn, winter} s;
3>省略枚举类型名,直接定义枚举变量
enum {spring, summer, autumn, winter} s;
2.枚举的使用
1)C语言编译器会将枚举元素(spring、summer等)作为整型常量处理,称为枚举常量。
2)枚举元素的值取决于定义时各枚举元素排列的先后顺序。默认情况下,第一个枚举元素的值为0,第二个为1,依次顺序加1。
// 也就是说spring的值为0,summer的值为1,autumn的值为2,winter的值为3
enum Season {spring, summer, autumn, winter};
3)
也可以在定义枚举类型时改变枚举元素的值
enum season {spring, summer=3, autumn, winter};
没有指定值的枚举元素,其值为前一元素加1。也就说spring的值为0,summer的值为3,autumn的值为4,winter的值为5
3.枚举变量的基本操作
1.赋值
可以给枚举变量赋枚举常量或者整型值
enum Season {spring, summer, autumn, winter} s;
s = spring; // 等价于 s = 0;
s = 3; // 等价于 s = winter;
2.遍历枚举元素
enum Season {spring, summer, autumn, winter} s;
// 遍历枚举元素
for (s = spring; s <= winter; s++) {
printf("枚举元素:%d \n", s);
}
三、typedef
1.typedef作用简介
1)我们可以使用typedef关键字为各种数据类型定义一个新名字(别名)。
#include <stdio.h>
// typedef 原名 别名
typedef int myInt;
typedef unsigned int myUnsInt;
int main()
{
int a = -10; // 使用变量类型原名来定必变量
myInt i = -10; // 使用变量类型别名来定义变量
myUnsInt j = 11;
printf("%d %d %d\n", a, i, j);
return 0;
}
2)我们还可以在别名的基础上再起别名
typedef float myFloat;
typedef myFloat myFloat2;
3)起别名不会影响原名的使用
#include <stdio.h>
typedef char *String;
int main(int argc, const char * argv[]) {
// 相当于char *str = "This is a string!";
String str = "This is a string!";
printf("%s\n", str);
return 0;
}
2.typedef与指针
除开可以给基本数据类型起别名,typedef也可以给指针起别名
#include <stdio.h>
typedef char *String; // 给 char * 类型起别名为 String
int main(int argc, const char * argv[]) {
// 相当于char *str = "This is a string!";
String str = "This is a string!";
printf("%s", str);
return 0;
}
在第3行给指针类型char *起别名为String,然后在第7行使用String定义了一个字符串
3.typedef与结构体
给结构体起别名可以使代码更加简洁明
// 定义一个结构体
struct MyPoint {
float x;
float y;
};
// 起别名
typedef struct MyPoint Point;
int main(int argc, const char * argv[]) {
// 定义结构体变量
Point p;
p.x = 10.0f;
p.y = 20.0f;
return 0;
}
其实第2行至第8行的代码可简写为:
// 定义一个结构体,顺便起别名
typedef struct MyPoint {
float x;
float y;
} Point;
或者:
typedef struct {
float x;
float y;
} Point;
4.typedef与枚举类型
// 定义枚举类型
enum Season {spring, summer, autumn, winter};
// 给枚举类型起别名
typedef enum Season Season;
int main(int argc, const char * argv[]) {
// 定义枚举变量
Season s = spring;
return 0;
}
1至4行可简写为:
// 定义枚举类型,并且起别名
typedef enum Season {spring, summer, autumn, winter} Season
或者:
typedef enum {spring, summer, autumn, winter} Season;
5.typedef与指向函数的指针
#include <stdio.h>
// 定义一个sum函数,计算a跟b的和
int sum(int a, int b) {
int c = a + b;
printf("%d + %d = %d", a, b, c);
return c;
}
int main(int argc, const char * argv[]) {
// 定义一个指向sum函数的指针变量p
int (*p)(int, int) = sum;
// 利用指针变量p调用sum函数
(*p)(4, 5);
return 0;
}
2)下面使用typedef给指向函数的指针类型起别名
#include <stdio.h>
// 定义一个sum函数,计算a跟b的和
int sum(int a, int b) {
int c = a + b;
printf("%d + %d = %d", a, b, c);
return c;
}
typedef int (*MySum)(int, int);
int main(int argc, const char * argv[]) {
// 定义一个指向sum函数的指针变量p
MySum p = sum;
// 利用指针变量p调用sum函数
(*p)(4, 5);
return 0;
}
小结:
1.需要先定义结构体类型,再定义结构体变量。定义结构体类型时,系统是不分配内存空间的。系统只分配内存空间给实际的结构体变量。
2.结构体变量所占内存大小:补齐算法——最大元素的倍数。
3.枚举元素值从0开始,后续元素依次+1。
4.typedef的使用
To be continue……