很无力的复习。
1、函数的声明
在顺序中,可以先声明,证明有该函数,使编译器不报错,并在全编码搜索改函数, 这是由于c语言的特性决定的,因为c语言是从上往下走的,如果主函数前面没有该函数,编译器将不会运行该段编码,先声明则修补了这个问题,先声明,后运行。
有趣的是,函数的声明写很多次都不会有问题。
int max(int a, int b) ; //在顺序中,可以先声明,证明有该函数,使编译器不报错,并在全编码搜索改函数
// 这是由于c语言的特性决定的,因为c语言是从上往下走的,如果主函数前面没有该函数,编译器将不会运行该段编码
//先声明则修补了这个问题,先声明,后运行。
int main()
{
int a = 0;
int b = 0; //创建变量
cout << "请输入要比较的第一个数字" << endl;
cin >> a;
cout << "第二数字" << endl;
cin >> b;
cout <<"最大的为: " << max(a, b) << endl; //打印输出
system("pause");
return 0;
}
int max(int a, int b)
{
return a > b ? a : b; //三目运算符比较大小,a>b满足,输出a,否则输出b
}
2、分文件编写
为了降低主函数的复杂性和组合功能的实现,使用分文件的方式进行处理。
简单来说,为了保持主函数的简洁和分配工作,部分功能其他人编写测试完成后,通过分文件的方式插入到主函数中。
具体实现见下图
头文件编写
#pragma once
#include
using namespace std;//因为分文件在本质上也是一个函数,因此同样执行iOS标准体系,
//源文件有的标准,他都有
int max(int a, int b) //正常编写函数
{
return a > b ? a : b; //三目运算符比较大小,a>b满足,输出a,否则输出b
}
主文件编写
#include
#include "max.h" //在主函数中建立与分文件的链接
using namespace std;
int max(int a, int b); //声明使用分文件的函数
int main()
{
int a = 0;
int b = 0; //创建变量
cout << "请输入要比较的第一个数字" << endl;
cin >> a;
cout << "第二数字" << endl;
cin >> b;
cout << "最大的为: " << max(a, b) << endl; //打印输出
system("pause");
return 0;
}
3、指针
指针的作用: 可以通过指针间接访问内存
c语言是将所有的数据存放在内存上,而每一个数据都一个地址,用来索引和查找
那么如何快速,直接的访问内存?使用指针
#include
using namespace std;
int main()
{
//1、创建一个数据
int a = 10;
//2、使用指针:数据类型 *指针的变量名:
int* p;
//3、让指针记录变量A的地址
p = &a;
//4、打印变量的地址
cout << "变量a 的内存地址:"<<&a << endl;
system("pause");
return 0;
}
4、指针占有的内存地址
#include
using namespace std;
int main()
{
//1、创建一个数据
int a = 10;
//2、使用指针:数据类型 *指针的变量名:
int* p;
//3、让指针记录变量A的地址
p = &a;
//4、打印变量的地址
cout << "变量a 的内存地址:"<<&a << endl;
cout << *p << endl; //* 解引用
cout << sizeof(p) << endl;
cout << sizeof(char*) << endl;
cout << sizeof(float*) << endl;
cout << sizeof(double*) << endl;
system("pause");
return 0;
}
5、空指针
空指针:指针变量指向内存中编号为0的空间,实际上并不存在
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的
#include
using namespace std;
int main()
{
//指针变量p指向内存地址编号为0的空间
int* p = NULL;
//访问空指针报错
//内存编号0 ~255为系统占用内存,不允许用户访问
cout << *p << endl;
system("pause");
return 0;
}
6、野指针
顾名思义,不明指向,错误指向都叫野指针
int main() {
//指针变量p指向内存地址编号为0x1100的空间
int * p = (int *)0x1100;
//访问野指针报错
cout << *p << endl;
system("pause");
return 0;
}
7、const修饰指针
是将指针固定的方法,防止意外将其改变。
const修饰指针有三种情况
锁死,不得更改指针指向
2、const修饰常量 --- 指针常量
锁死指针的常量
3、const即修饰指针,又修饰常量
指向和常量都锁死
#include
using namespace std;
int main()
{
//1/创建变量
int a = 10;
int b = 20;
//2、const修饰的是指针,指针指向可以改,指针指向的值不可以更改
const int* p1 = &a;
//指向的值不可以改
//*p = 20;更改报错
p1 = &b; //更改指向允许,正确
//3、const修饰的是常量,指针指向不可以改,指针指向的值可以更改
int * const p2 = &a;
//p2 = &b; 更改报错
*p2 = 100; //更改指向常量的值,正确
//4、const既修饰指针又修饰常量
const int* const p3 = &a;
//p3 = &b;
//*p2 = 100;
system("pause");
return 0;
}
8、指针和数组
#include
using namespace std;
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
cout << "第一个元素为:" << arr[0] << endl;//基本访问数组
int* p = arr;
cout << "利用指针访问第一个元素:" << *p << endl;
p++;
//利用指针遍历数组
int* p2 = arr;
cout << "利用指针遍历数组:" << endl;
for (int i = 0; i < 9; i++)
{
cout << *p2 << " ";
p2++;
}
system("pause");
return 0;
}
9、
#include
using namespace std;
void swap01(int a,int b) //交换用的函数
{
int temp = a;
a = b;
b = temp;
cout << "形参a = " << a << endl;//形参
cout << "形参b = " << b << endl;
}
void swap02(int * p1, int* p2)//指针更改函数
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
int a = 10;
int b = 20;
swap01(a, b);
//swap02(&a, &b);
cout << "实参a = " << a << endl;//实参
cout << "实参b = " << b << endl;
system("pause");
return 0;
}
指针函数未启用时,
输出结果
启用swap02
交换,改变了原值
总结,正常的函数,从原内存地址调用后,进行处理,输出新的数据,但是指针可以返回原内存地址,更改初始数据。
10.案列,同时访问数组和函数
#include
using namespace std;
//2、创建冒泡排序的函数
void maopaopaixu(int* arr, int len) //*arr是数组的首地址,len是数组长度
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
void print_array(int * arr,int len) //3、打印函数
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " \t";
}
}
int main() {
//1、创建数组
int arr[10] = { 4,3,6,9,1,2,10,8,7,5 };
//数组长度
int len = sizeof(arr) / sizeof(arr[0]);
maopaopaixu(arr, len);
//3.打印排序后的数组
print_array(arr, len);
system("pause");
return 0;
}
用法参考本day8、9。
结构体属于用户==自定义的数据类型==,允许用户存储不同的数据类型
语法:struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有三种:
struct 结构体名 变量名
struct 结构体名 变量名 = { 成员1值 , 成员2值...}
定义结构体时顺便创建变量
#include
#include
using namespace std;
struct student//创建一个学生的结构体
{
//姓名
string name;
//年龄
int age;
//分数
int score;
};
//2通过学生类型创建具体学生
int main()
{
//2.1第一种结构体定义方式 学生1的属性
struct student s1;
s1.name = "张三";
s1.age = 18;
s1.score = 88;
cout << "姓名:" << s1.name << " 年龄:" << s1.age << " 分数:" << s1.score << endl;
//2.2第一种结构体定义方式 学生2的属性
struct student s2 = { "李四" ,20,99 };
cout << "姓名:" << s2.name << " 年龄:" << s2.age << " 分数:" << s2.score << endl;
system("pause");
return 0;
}
值得注意的是,使用第二种方法,要保证上下数据对应正确,否则报错,详见下图
12. 结构体数组
#include
#include
using namespace std;
//结构体数组
//1、定义结构体
struct student
{
//姓名
string name;
//年龄
int age;
//分数
int score;
};
int main()
{
//2、创建结构体数组
struct student std_arry[3] =
{
{"张三",18,99},
{"李四",22,88},
{ "赵四",23,77}
};
//3、改变结构体数组中的元素赋值
std_arry[2].age = 88;
std_arry[2].score = 100;
//4.遍历结构体中的数组
for (int i = 0; i < 3; i++)
{
cout << "姓名:" << std_arry[i].name << "\t" << "年龄:" << std_arry[i].age << "\t" << "分数:" << std_arry[i].score << endl;
}
system("pause");
return 0;
}
这里用的是一个二维数组,请注意命名方式
13.结构体指针
主要是讲述用->去访问结构体指针的属性。
#include
#include
using namespace std;
struct student
{
//姓名
string name;
//年龄
int age;
//分数
int score;
};
int main()
{
//创建学生结构体变量
struct student s = { "张三",18,100 };
//通过指针指向结构体变量
struct student *p = &s; //此时的struct 可以省略
//通过指针访问结构体变量中的数据
cout << "姓名: " << p->name << " " << "年龄:" << p->age << " " << "分数:" << p->score << endl;
//注意通过结构体指针,访问结构体中的的属性,需要利用“->”
system("pause");
return 0;
}
14.结构体嵌套结构体
一个结构体可以担当另外一个结构体的数据,用法非常简单粗暴,只需要在声明一个结构体,既可以在另外一个结构体中使用,来表明从属关系,但是有趣的是,测过测试,一个结构体一旦使用了数组,那么就不能在嵌套结构体。错误原因为数据类型不匹配。
看了下面的实际案例是应该是我的问题。我找个时间试试
#include
#include
using namespace std;
//定义老师的结构体
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
//教师结构体定义
struct teacher
{
//成员列表
int id; //职工编号
string name; //教师姓名
int age; //教师年龄
struct student stu; //子结构体 学生
};
int main() {
struct teacher t1;
t1.id = 10000;
t1.name = "老王";
t1.age = 40;
t1.stu.name = "张三";
t1.stu.age = 18; //此处不能使用数组来实现一对多,原因为数据类型不匹配
t1.stu.score = 100;
cout << "教师 职工编号: " << t1.id << " 姓名: " << t1.name << " 年龄: " << t1.age << endl;
cout << "辅导学员 姓名: " << t1.stu.name << " 年龄:" << t1.stu.age << " 考试分数: " << t1.stu.score << endl;
system("pause");
return 0;
}
15、结构体做函数参数
在函数中访问结构体的数据,使用方法与在main中一致,创建函数后,讲数据传入函数中即可
void print_student1(struct student s)
在函数内部使用也是一样的,使用 . 即可快速链接结构体属性,详见代码
第二,有两种使用方法,有值传递和地址传递两种,其中要注意,使用地址传递,如果在函数内修改数值,原地址的数据随之改变。
#include
#include
using namespace std;
//学生结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
//打印学生信息函数
//值传递
void print_student1(struct student s)
{
cout << "子函数中(值传递)姓名:" << s.name << " 年龄: " << s.age << " 分数:" << s.score << endl;
}
//地址传递
void print_student2(struct student * s)
{
cout << "子函数中(地址传递)姓名:" << s->name << " 年龄: " << s->age << " 分数:" << s->score << endl;
//测试改变原值代码
s->age = 180;
}
int main() {
//创建一个结构体变量
struct student s = { "张三",18,100 };
print_student1(s);
print_student2(&s);
//测试原值是否改变
cout << "使用地址传递后的原数据是否被改变,原值为18。" << s.age << endl;
system("pause");
return 0;
}
16.const的用法
const是为了防止原数据发生变化用的。在函数引入数据后加上const 即可锁定,如果改数据,就会报错。
主要用途,使用值传递,在传输数据时会复制数据,但是在面对兆级数据时,内存占用过大,因此这个时候通常使用指针,但指针访问属性,使用地址访问,对原数据可能造成威胁,为避免改动原数据,在使用地址传递时,不对原属性做改变的,加上const锁死。
//学生结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
//const使用场景
void printStudent(const student *stu) //加const防止函数体中的误操作
{
//stu->age = 100; //操作失败,因为加了const修饰
cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl;
}
int main() {
student stu = { "张三",18,100 };
printStudent(&stu);
system("pause");
return 0;
}
17.结构体实例
#include
#include
using namespace std;
//学生的结构体
struct student
{
//姓名
string student_name;
int score;
};
//老师的结构体
struct teacher
{
//姓名
string teacher_name;
//学生数组
struct student s_arry[5];
};
//随机给老师、学生的信息进行赋值
void all_ocate_space( teacher t_arry[], int len)
{
string name_seed = "abcdef"; //随机的赋值
//给老师来赋值
for (int i = 0; i < len; i++)
{
t_arry[i].teacher_name = "老师_"; //老师姓名和abcde连接在一起,让他们循环衔接的
t_arry[i].teacher_name += name_seed[i];
for (int j = 0; j < 5; j++)
{
t_arry[i].s_arry[j].student_name = "学生_";
t_arry[i].s_arry[j].student_name += name_seed[j];//学生姓名和abcde连接在一起,让他们循环衔接的
int random = rand() % 61 + 40; //使用了一个随机数
t_arry[i].s_arry[j].score = random;
}
}
}
void print_all(struct teacher t_arry[], int len)
{
for (int i = 0; i < len; i++)
{
//循环打印老师的信息
cout << "老师的姓名:: " << t_arry[i].teacher_name << endl;
//循环打印学生的信息
for (int j = 0; j < 5; j++)
{
cout << "学生姓名: " << t_arry[i].s_arry[j].student_name <<
" 考试分数: " << t_arry[i].s_arry[j].score << endl;
}
}
}
int main()
{
//1/创建3名老师的数组
struct teacher t_arry[4];
//2.通过函数给4名老师的信息赋值,并给老师带的学生信息赋值
int len = sizeof(t_arry) / sizeof(teacher);
//给老师和学生赋值
all_ocate_space(t_arry, len);
//打印所有的信息
print_all(t_arry, len);
system("pause");
return 0;
}
分段讲解
第一创建老师和学生的结构体
struct student
{
//姓名
string student_name;
int score;
};
//老师的结构体
struct teacher
{
//姓名
string teacher_name;
//学生数组
struct student s_arry[5];
};
学生结构体较为简单,和之前一样,由字符串和整形数字组成
但是老师要注意,构建学生结构体时,使用的数据类型为数组,这说明一个老师对应着五个学生。
在main函数中对老师和学生的结构体进行赋值
//1/创建3名老师的数组
struct teacher t_arry[4];
//2.通过函数给4名老师的信息赋值,并给老师带的学生信息赋值
int len = sizeof(t_arry) / sizeof(teacher);
//给老师和学生赋值
all_ocate_space(t_arry, len);
首先制定了几名老师,这里经测试t_arry[ ]内的数据不可以是len,这是语序的问题,想要实现,需要用别的方法。
len是用t_arry和teacher比较出来的。
接下来进行两个函数的操作
allocateSpace(tArray, len); //创建数据
printTeachers(tArray, len); //打印数据
创建数据
//随机给老师、学生的信息进行赋值
void all_ocate_space( teacher t_arry[], int len)
{
string name_seed = "abcdef"; //随机的赋值
//给老师来赋值
for (int i = 0; i < len; i++)
{
t_arry[i].teacher_name = "老师_"; //老师姓名和abcde连接在一起,让他们循环衔接的
t_arry[i].teacher_name += name_seed[i];
for (int j = 0; j < 5; j++)
{
t_arry[i].s_arry[j].student_name = "学生_";
t_arry[i].s_arry[j].student_name += name_seed[j];//学生姓名和abcde连接在一起,让他们循环衔接的
int random = rand() % 61 + 40; //使用了一个随机数
t_arry[i].s_arry[j].score = random;
}
}
}
引入数据类型是struct 中的teacher中的数组,和len
然后给老师、学生的姓名随机赋值
随机来源为 name_seed,依序打印,这里是先创建一个老师_,再加上上面一次打印的name_seed。有另外一种方法
void allocateSpace(Teacher tArray[] , int len)
{
string tName = "教师";
string sName = "学生";
string nameSeed = "ABCDE";
for (int i = 0; i < len; i++)
{
tArray[i].name = tName + nameSeed[i];
for (int j = 0; j < 5; j++)
{
tArray[i].sArray[j].name = sName + nameSeed[j];
tArray[i].sArray[j].score = rand() % 61 + 40;
}
}
}
先创建数据后,在缝合即可
随后在老师的循环下,再去创建学生,这里要注意顺序问题,以防眼花输错,本人出现类似问题,修改半天。
要先找到对应的t_arry[ i ],在去对应student_name[ j ],
这里使用了一个rand函数去构建分数的随机值。
然后,打印数据
void print_all(struct teacher t_arry[], int len)
{
for (int i = 0; i < len; i++)
{
//循环打印老师的信息
cout << "老师的姓名:: " << t_arry[i].teacher_name << endl;
//循环打印学生的信息
for (int j = 0; j < 5; j++)
{
cout << "学生姓名: " << t_arry[i].s_arry[j].student_name <<
" 考试分数: " << t_arry[i].s_arry[j].score << endl;
}
}
}
同样简单,根据系统提示,找到想对应关系即可,但要注意其中的数据类型和对应关系,对应错误也可能不会报错,但运行的结果,会变成乱码的地址。
最后运行结果如图
今日学习,结束,晚上进行通讯录管理系统的构建。