1 指针
作用:通过指针间接访问内存
1.内存编号是从0开始记录的,一般用十六进制数字表示。
2.可以利用指针变量保存地址。
1.1 指针变量的定义和使用
语法:数据类型 *变量名。如int *a;
#include<iostream>
using namespace std;
int main(){
int a = 1;
//创建指针变量
int *p;
//让指针记录变量a的地址
p = &a;
cout<<"a的地址:"<< &a <<"-" << p <<endl;
//通过解引用找到指针所指内存的数据
//指针前加 * 代表解引用, 找到指针所指内存的数据
*p = 10000;
// 均输出10000
cout<< "a= "<< a <<endl;
cout<< "a= "<< *p <<endl;
return 0;
}
1.2 指针所占内存空间
指针也是种数据类型,32位操作系统下:占4个字节,64位下占8个字节。
#include<iostream>
using namespace std;
int main(){
//指针所占内存空间
int a = 10;
int *p = &a;
//32位下都是占4个字节
cout<<"sizeof (int*) = "<<sizeof(int *)<<endl;
cout<<"sizeof (float*) = "<<sizeof(float *)<<endl;
cout<<"sizeof (double*) = "<<sizeof(double *)<<endl;
cout<<"sizeof (char*) = "<<sizeof(char *)<<endl;
return 0;
}
1.3 空指针和野指针
空指针:指针变量指向内存中编号位0的空间
作用:初始化指针变量。空指针指向的内存不可以访问。
#include<iostream>
using namespace std;
int main(){
//空指针,默认指向内存地址编号为0的空间
int *p = NULL;
//*p = 100 // 错误,因为0 - 255之间内存是 系统占用内存,不允许用户访问。
return 0;
}
野指针:指针变量指向非法的内存空间。
#include<iostream>
using namespace std;
int main(){
//野指针,
int *p = (int)0x1100;
//报访问权限错误
cout<<*p<<endl;
return 0;
}
空指针和野指针都不是用户申请的空间,不能访问。
1.4 const修饰指针
const修饰指针有三种情况:
- const修饰指针 - 常量指针
- const修饰常量 - 指针常量
- const即修饰指针,又修饰常量
//根据 * 和 const位置记录。
1.常量指针:指针的指向可以修改,但是指针指向的值不可以修改
int a = 10;
int b = 30;
// const先修饰的 *,因此指针的指向值不可改,即*p不可改
const int *p = &a;
*p = 20; //错误,指针指向的值不可修改
*p = &b;//正确
2.指针常量:指针的指向不可以修改,但是指针指向的值可以修改
int a=10,b=20;
// const 修饰的是 p,因此指针的地址不能改,所以指向不能改
int *const p = &a;
*p = 20; //正确,指针指向的值可以修改
*p = &b;//错误,指针的指向不可以修改
3.修饰指针:指针的指向不可以修改,指针指向的值不可以修改
int a=10,b=20;
const int *const p = &a;
*p = 20; //错误,指针指向的值不可以修改
*p = &b;//错误,指针的指向不可以修改
1.5 指针和数组
作用:利用指针访问数组中元素
#include<iostream>
using namespace std;
int main(){
int arr[5] = {1,2,3,4,5};
//p 指向 arr首地址
int *p = arr;
//输出1
cout<<*p<<endl;
//输出1
cout<<*(p++)<<endl;
//输出2
cout<<*(++p)<<endl;
return 0;
}
1.6 指针和函数
作用:利用指针作为函数的参数,可以修改实参的值
#include<iostream>
using namespace std;
void swap(int *a,int *b){
//*a 解引用,获取a的地址的值
int t = *a;
*a = *b;
// 将t的值存到b地址
*b = t;
}
int main(){
int a = 1;
int b = 2;
//地址传递,相当于int *a = &a
swap(&a,&b);
//a = 2, b = 1
cout<<"a:"<<a<<" b:"<<b<<endl;
return 0;
}
1.7 指针、数组和函数
案例:封装一个函数,利用冒泡排序,实现对整型数组的升序排序
#include<iostream>
using namespace std;
//指针使用冒泡排序
void buuble(int *arr,int n){
for(int i=0;i<n;i++){
for(int j=0;j<n-i-1;j++){
if(*(arr+j) > *(arr+j+1)){
int tmp = *(arr+j);
*(arr+j) = *(arr+j+1);
*(arr+j+1) = tmp;
}
/*if(arr[j] > arr[j+1]){
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}*/
}
}
}
int main(){
int a[10] = {4,3,1,6,8,83,3,1,8,5};
int n = sizeof(a) / sizeof(a[0]);
buuble(a,n);
for(int i=0;i<n;i++) cout<<a[i]<<" ";
return 0;
}
2 结构体
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
语法: struct 结构体名{ 结构体成员列表 };
通过结构体创建变量的方式有三种:
1.struct 结构体名 变量名
2.struct 结构体名 变量名 = {成员1值,成员2值 … }
3.定义结构体适顺便创建变量
#include<iostream>
using namespace std;
//结构体定义-一些类型集合组成的
struct stu{
//成员列表
string name;
int age;
}s3; //创建方式3:定义结构体时顺便创建结构体变量
int main(){
//创建方式1:struct stu s1。 创建变量时struct 关键词可省略
struct stu s1;
s1.name = "王";
s1.age = 10;
cout<<s1.age<<"-"<<s1.name<<endl;
//创建方式2:struct stu s2 = {...}
struct stu s2 = {"张",32};
cout<<s2.age<<"-"<<s2.name<<endl;
s3.age = 23;
s3.name = "李";
return 0;
}
2 .1 结构体数组
作用:将自定义的结构体放入数组中方便维护
语法:struct 结构体名 数组名[元素个数] = { {}, {}, {} …}
#include<iostream>
using namespace std;
//结构体定义-一些类型集合组成的
struct stu{
//成员列表
string name;
int age;
}s3;
int main(){
//结构体数组
struct stu arr[3] = {
{"张",12},
{"李",42},
{"王",22},
};
arr[2].age = 17;
for(auto s : arr){
cout<<s.age<<"-"<<s.name<<endl;
}
return 0;
}
2 .2 结构体指针
作用:通过指针访问结构体中的成员
#include<iostream>
using namespace std;
//结构体定义-一些类型集合组成的
struct stu{
//成员列表
string name;
int age;
}s3;
int main(){
//结构体
struct stu = { {"张",12} };
//指针指向 stu
stu *p = &stu;
cout<<p->age<<"-"<<p->name<<endl;
return 0;
}
2 .3 结构体嵌套结构体
作用:结构体中的成员可以是另一个结构体
例如:每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体
#include<iostream>
using namespace std;
struct stu{
string name;
int age;
};
struct teacher{
int id,
string name;
struct stu s;
};
int main(){
//结构体嵌套结构体
teacher t;
t.id = 1;
t.name = "王";
t.s.name = "李";
t.s.age = 3;
return 0;
}
2.4 结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式两种:1.值传递 。2.地址传递
#include<iostream>
using namespace std;
struct teacher{
int id,
string name;
};
//值传递
void test1(struct teacher t){
}
//地址传递
void test2(struct teacher &t){
}
int main(){
//结构体嵌套结构体
teacher t;
t.id = 1;
t.name = "王";
//值传递
test1(t);
//地址传递
test2(&t);
return 0;
}
2.5 结构体中const使用场景
作用:防止误操作
#include<iostream>
using namespace std;
struct Student{
int id,
string name;
};
// 函数中形参改为指针可减少空间,因为指针只占4个字节,存的的地址
void prints(const Student *s){
//形参加入const后修改会报错,不加const可修改,此时主函数值也会修改
s->id = 4;
cout<<s->name<<endl;
}
int main(){
//结构体嵌套结构体
Student s = {1,"王"};
prints(&s);
return 0;
}
2.6 结构体案例
学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员学生的成员有姓名、考试分数,创建数组存放3名老师,通过函数给每个老师及所带的学生赋值最终打印出老师数据以及老师所带的学生数据。
#include<iostream>
using namespace std;
struct Student{
string name;
int score;
};
struct Teacher{
string name;
Student stus[5];
};
//给老师和学生赋值
void init(struct Teacher tArr[],int len){
string nameS = "ABCDE";
for(int i=0;i<len;i++){
tArr[i].name = "教师_";
tArr[i].name += nameS[i];
//循环给老师下的学生赋值
for(int j=0;j<5;j++){
tArr[i].stus[j].name = "学生_";
tArr[i].stus[j].name += nameS[j];
tArr[i].stus[j].score = 70;
}
}
}
void prints(struct Teacher tArr[],int len){
for(int i=0;i<len;i++){
cout<<"教师姓名:"<<tArr[i].name<<endl;
for(int j=0;j<5;j++){
cout<<"学生:"<<tArr[i].stus[j].name<<"成绩为:"<<tArr[i].stus[j].score<<endl;
}
}
}
int main(){
struct Teacher tArr[3];
int len = sizeof(tArr) / sizeof(tArr[0]);
//初始化
init(tArr,len);
//打印
prints(tArr,len);
return 0;
}
3 综合案例-通讯录系统
功能列表
3.1 添加联系人
case 3: //删除
//case 中若存在很多行代码,可能会编译错误,因此加入 {},使其变为代码段
{
cout<<"输入要删除的联系人"<<endl;
string name;
cin>>name;
del(abs,name);
}