1、结构体的基本使用
#include <stdio.h>
/*
数组:只能由多个相同类型的数据构成
结构体:可以由多个不同类型的数据构成
*/
int main()
{
//int age1;//定义整型变量方法
//结构体类型 结构体变量名 ,结构体类型默认不存在,自己来定义类型,不行基本数据类型,如int系统存在,但是构造数据类型系统不存在。
//定义结构体的类型,是告诉计算机系统这种类型中所包含的数据都有哪些
//1.定义结构体类型
struct person
{
//里面的3个变量,可以称为是结构体的成员或者属性
int age;
double height;
char* name;
};
//int ages[3] = {2,3,4};
// ages = {2, 3, 4};错误,因为ages是常量地址
//2.根据结构体类型,定义结构体变量
struct person p = {20, 1.55, "jack"}; // p为结构体变量名,类型名为person,但是因为是结构体,定义变量时,关键字struct不能省略。
p.age = 30;
p.name = "rose";//和数组的初始化很像,如果不在定义变量时初始化,之后初始化,只能给成员单独初始化,不能再同一初始化赋值了。
printf("age=%d, name=%s, height=%f\n", p.age, p.name ,p.height);
struct person p2;
//p2 = {25,1.65,"tom"}这种写法是错误的,只能在定义结构体变量的同时才能统一赋初值。
//就像数组一样,数组名代表地址,数组的地址不可随便改,也就是地址常量,常量不可改变
p2.age = 25;
p2.height = 1.65;
p2.name = "tom";
//如果赋初值时,可以用如下方式实现不按顺序赋初值。
//就像数组int pen[2]={[1]=1, [0]=2};必须告诉系统哪个值赋给哪个属性
struct person p3 = {.height = 1.44, .name="apple", .age=12};
return 0;
}
2、结构体的内存分析
#include <stdio.h>
int main()
{
return 0;
}
// 补齐算法
void test1()
{
struct student
{
int age;//4个字节
char* name//8个字节,不是5个字节,指针类型都是占用8个字节
// char name[];
};
struct student stu;
stu.age = 20;
stu.name = "jack";
int s = sizeof(stu);
printf("%d\n",s);//输出结果位16,原因补齐算法(对齐算法),结构体变量所占字节是最大成员变量所占字节的整倍数,如上指针占8个字节,所以4+8=12,12不是8的整倍数,补齐后为16。这与计算机组成原理有关。
//结构体所占用的存储空间,必须是最大成员字节数的倍数
}
//结构体内存分析细节
void test()
{
//1.定义结构体类型(并不会分配内存存储空间)
struct date
{
int year;
int month;
int day;
};
//定义结构体类型,是不会分配存储空间的,就像系统不会为int类型分配存储空间一样,只有在定义数据类型变量时才会分配存储空间。
// int a;
//2.定义结构体变量(真正分配存储空间)
struct date d1 = {2011, 4, 10};//09表示是八进制,因为以0开头
struct date d2 = {2012, 8, 9};
//会将d1所有成员的值对应地赋值给d2的所有成员
d2 = d1;
printf("%d,%d ,%d\n",d1.year,d1.month,d1.day);
printf("%d,%d ,%d\n",d2.year,d2.month,d2.day);
printf("%p - %p - %p\n",&d1.year, &d1.month, &d1.day);//就像数组一样定义不同的数组变量时,先分配大的内存地址,但每个数组成员是按由大到小的顺序排在每个数组变量的内存中,year地址最小,day地址最大。
int s = sizeof(d1);
printf("s=%d\n",s);
}
3、定义结构体多种方式
//定义结构体多种方式
void test()
{
/*
1.定义结构体变量的3种方式
1> 先定义类型,再定义变量(分开定义)
struct student
{
int age;
};
struct student stu1;
2> 定义类型的同时定义变量
struct student
{
int age;
}stu1;
struct student stu2;
3> 定义类型的同时定义变量(省略了类型名称)
struct
{
int age;
}stu;
struct
{
int age;
}stu1;
*/
/*定义结构体变量的第3种方式
struct
{
int age;
double height;
char* name;
}stu;
//缺点是下次再定义变量时,要重新复制,因为结构体类型不能重用
struct
{
int age;
double height;
char* name;
}stu2;
*/
/*错误写法:结构体类型重复定义,就像重复定义变量一样
struct student
{
int age;
double height;
char* name;
}stu;
struct student
{
int age;
double height;
char* name;
}stu2;//会报错,结构体类型struct student重复定义
*/
/*结构体类型不能重复定义
struct student
{
int age;
};
struct student
{
double height;
};
struct student stu;
*/
/*
这句代码做了两件事情:
1.定义结构体类型
2.利用新定义好的类型来定义结构体变量
//定义变量的第2种方式:定义类型的同时定义变量
struct student
{
int age;
double height;
char* name;
}stu; //相当于int a;
struct student stu2;
*/
//*************************************
/*
// 定义变量的第1种方式
// 1.定义结构体类型
struct student
{
int age;
double height;
char* name;
};
//2.定义结构体类型变量
struct student stu = {20, 1.78, "jack"};
*/
}
4、结构体作用域
#include <stdio.h>
/*
结构体类型的作用域
1> 定义在函数外面:全局有效(从定义类型的那行开始,一直到文件结尾)
2> 定义在函数(代码块)内部:局部有效(从定义类型的那行开始,一直到代码块结束)
*/
//结构体类型也是有作用域的
//从这行开始,一直到文件结尾,都是有效(跟全局变量一样)
struct date
{
int year;
int month;
int day;
};
int a ;
void test2()
{
struct date d1 = {2011, 9, 10};
//假如在此函数内,再次定义和外部结构体相同的结构体类型,不会报错,作用域搞定,就像在外部定义int a;在内部还可以定义相同变量名,只是作用域不同,如下:
int a;
struct date
{
int year;
int month;
int day;
};
//结构体类型也是有作用域的,从定义类型的那一行开始,一直到代码块结束,所以main函数中不可以使用此结构体类型
struct person
{
int age;
};
struct person p = {12};
}
/*结构体类型定义放到函数外面,之后的语句可以使用,就像全局变量一样。
struct date
{
int year;
int month;
int day;
};
int a;
*/
int main()
{
struct date d1 = {2009, 8, 9};
test2();
//不能使用test2函数中定义的类型
//struct person p2;//会报错,因为结构体类型有作用范围,许多程序自定义的东西是有作用范围的,如变量是有作用范围的,结构体类型是自己定义的,所以也有作用范围,但int不是程序自定义,是系统自带的。
return 0;
}
5、结构体数组
#include <stdio.h>
int main()
{
struct rankRecord
{
int no; // 序号 4
char* name; // 名称 8
int score; // 积分 4
};
/*
struct rankRecord r1 = {1, "jack", 5000};
struct rankRecord r1 = {2, "jim", 500};
struct rankRecord r1 = {3, "jake", 300};
*/
//int ages[3];
//int ages[3] = {11, 22, 33};
/*
char* names[3] =
{
{jack},
{jim},
{jake}
};
*/
//对齐算法
//能存放3个结构体变量,每个结构体变量占16个字节
struct rankRecord records[3] =
{
{1, "jack", 5000},
{2, "jim", 500},
{3, "jake", 300}
};
//错误写法
//records[0] = {4, "rose", 9000};//这种赋值修改,只能在定义结构体变量的同时才可以。
// 正确写法,修改方法如下:
records[0].no = 4;//可以修改第一条数据的no属性,其他属性同理。
for(int i=0; i<3; i++)
{
printf("%d\t%s\t%d\n",records[i].no, records[i].name, records[i].score);
}
return 0;
}
6、指向结构体的指针
#include <stdio.h>
/*掌握:
1.指向结构体的指针的定义
struct student* p;
2.利用指针访问结构体的成员
1> 结构体变量明发.成员名称
2> (*p).成员名称
3> p->成员名称
*/
int main()
{
struct student
{
int no;
int age;
};
// 结构体变量
struct student stu = {1, 20};
//stu.age = 30;
//根据指向整型的指针的定义,类推出指向结构体的指针。
// int* p;
// int a;
//p = &a;
// pirntf("%d\n", a);
// pirntf("%d\n", *p);
// 指针变量p将来指向struct student类型的数据。
struct student* p;
//万变不离其宗,使指针变量p指向结构体变量stu方法如下
p = &stu;
//利用指针变量间接访问所指向结构体变量成员
// 第一种访问方式
printf("no = %d, age=%d\n", stu.no ,stu.age);
// 第二种方式
printf("no = %d, age=%d\n", (*p).no ,(*p).age);
// 第三种方式(使用中最常见的方式)
printf("no = %d, age=%d\n", p->no , p->age);
//同访问方式一样,修改方式也是三种,原理一样
stu.age = 30;
(*p).age = 30;
p->age = 30;
return 0;
}
7、结构体的嵌套定义
#include <stdio.h>
int main()
{
//定义结构体类型
/*
struct studen
{
int no;
int year; // 这种定义方法不好
int month;
int day;
int year2;
int month2;
int day2;
};
*/
struct date
{
int year;
int month;
int day;
};
struct student
{
int no; // 学号
/*int year;
int month;
int day;*/
//此行定义代替以上三行定义
struct date birthday; // 生日,
struct date ruxuedate; // 入学日期
struct student stu; // 这种写法是错误的,不能自己包含自己的定义
};
struct student stu =
{
1, {2000, 9, 10}, {2012, 9, 10}
};
printf("year=%d,month=%d,day=%d\n",stu.birthday.year, stu.birthday.month, stu.birthday.day);
return 0;
}