一、指针
1. 基本概念
作用:通过指针间接访问内存
- 内存编号是从0开始记录的,一般用十六进制数字表示
- 可以利用指针变量保存地址
2. 定义与使用
语法:数据类型 * 指针变量名;
#include<iostream>
using namespace std;
int main()
{
int a = 10;
//指针定义
int * p;
p = &a;
cout << "a的地址为:" << &a << endl;
cout << "指针p为:" << p << endl;
//指针使用
//指针前加 * 代表解引用,找到指针指向的内存中的数据
*p = 1000;
cout << "a = " << a << endl;
cout << "*p = " << *p << endl;
system("pause");
return 0;
}
打印结果为:
a的地址为:00EFFAF4
指针p为:00EFFAF4
a = 1000
*p = 1000
请按任意键继续. . .
3. 指针所占内存空间
在32位操作系统下占4个字节,64位下占8个字节
代码:
#include<iostream>
using namespace std;
int main()
{
int a = 10;
//在32位操作系统下占4个字节,64位下占8个字节,不管是什么数据类型
int* p = &a;
cout << "sizeof(int *) = " << sizeof(int *) << endl;
cout << "sizeof(float *) = " << sizeof(float *) << endl;
cout << "sizeof(double *) = " << sizeof(double *) << endl;
cout << "sizeof(char *) = " << sizeof(char *) << endl;
system("pause");
return 0;
}
打印结果:
sizeof(int *) = 4
sizeof(float *) = 4
sizeof(double *) = 4
sizeof(char *) = 4
请按任意键继续. . .
4. 空指针
指针变量指向内存中编号为0的空间
用于初始化指针变量
注意:空指针指向的内存是不可以访问的
代码:
#include<iostream>
using namespace std;
int main()
{
//空指针用于给指针变量进行初始化
int* p = NULL;
//空指针不可以进行访问
// 0~255之间的内存编号是系统占用的,因此不可以访问
//*p = 100; //错误,引发一个异常
//cout << *p << endl; //错误,引发一个异常
system("pause");
return 0;
}
5. 野指针
指针变量指向非法的内存空间
代码:
#include<iostream>
using namespace std;
int main()
{
//在程序中,尽量避免出现野指针
//int* p = (int*)0x1100;
//cout << *p << endl; //引发异常
system("pause");
return 0;
}
空指针和野指针都不是我们申请的空间,因此不能访问。
6. const修饰指针
- const修饰指针 --常量指针
//常量指针,指针的指向可以修改,但指针指向的值不可以改
const int * p = &;
*p = 20; //错误,指针指向的值不可以更改
p = &b; //正确,指针的指向可以改
- const修饰常量 --指针常量
//指针常量,特点:指针的指向不可以改,指针指向的值可以改
int * const p = &a;
*p = 20; //正确,指针的值可以改
p = &b; //错误,指针的指向不可以改
//记忆技巧:const直接作用于p,则p不能修改
- const既修饰指针,又修饰常量
//特点:指针的指向和指针指向的值都不可以改
const int * const p = &a;
*p = 20; //错误,指针指向的值不可以更改
p = &b; //错误,指针的指向不可以改
7. 指针和数组
利用指针访问数组中元素
代码:
#include<iostream>
using namespace std;
int main()
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
cout << "第一个元素为:" << arr[0] << endl;
int * p = arr; //arr是数组首地址
cout << "利用指针访问第一个元素:" << *p << endl;
p++; //让指针向后偏移4个字节
cout << "利用指针访问第二个元素:" << *p << endl;
cout << "利用指针遍历数组" << endl;
int* p2 = arr;
for (int i = 0; i < (sizeof(arr) / sizeof(arr[0])); i++)
{
cout << *p2 << endl;
p2++;
}
system("pause");
return 0;
}
打印结果:
第一个元素为:1
利用指针访问第一个元素:1
利用指针访问第二个元素:2
利用指针遍历数组
1
2
3
4
5
6
7
8
9
10
请按任意键继续. . .
8. 指针和函数
利用指针作为函数参数,可以修改实参的值
代码:
#include<iostream>
using namespace std;
//实现两个数字进行交换
//值传递
void swap01(int a, int b)
{
int temp = a;
a = b;
b = temp;
cout << "swap01 a = " << a << endl;
cout << "swap01 b = " << b << endl;
}
//地址传递
void swap02(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main()
{
int a = 10;
int b = 20;
cout << "交换前:" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
//swap01(a, b);
swap02(&a,&b);
cout << "交换后:" << endl;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
system("pause");
return 0;
}
打印结果:
交换前:
a = 10
b = 20
交换后:
a = 20
b = 10
请按任意键继续. . .
地址传递原理:
9. 指针、数组、函数
案例描述:封装一个函数,利用冒泡排序,实现对整型数组的升序排序。
代码:
#include<iostream>
using namespace std;
//冒泡排序
void bubbleSort(int *arr, int 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 printArray(int* arr, int len)
{
for (int i = 0; i < len; i++)
{
cout << arr[i] << " ";
}
cout << endl;
}
int main()
{
int arr[10] = {4, 3, 6, 9, 1, 2, 10, 8, 7, 5 };
int len = sizeof(arr) / sizeof(arr[0]);
cout << "排序前:" << endl;
printArray(arr, len);
bubbleSort(arr, len);
cout << "排序后:" << endl;
printArray(arr, len);
system("pause");
return 0;
}
打印结果:
排序前:
4 3 6 9 1 2 10 8 7 5
排序后:
1 2 3 4 5 6 7 8 9 10
请按任意键继续. . .
二、结构体
1. 概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
2. 定义与使用
- 语法:
struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有三种:struct 结构体名 变量名
struct 结构体名 变量名 = { 成员1值,成员2值 }
定义结构体时顺便创建变量
注意:结构体变量创建时,struct关键字可以省略;但是定义结构体时,struct关键字不能省略;结构体变量用操作符“ . ”访问成员
代码:
#include<iostream>
using namespace std;
#include<string>
struct Student
{
string name;
int age;
int score;
}s3; //创建结构体变量第三种方法,顺便创建结构体变量
int main()
{
//结构体变量创建时,struct关键字可以省略
//但是定义结构体时,struct关键字不能省略
//创建结构体变量第一种方法
struct Student s1;
//给s1属性赋值,通过 . 访问结构体变量中的属性
s1.name = "张三";
s1.age = 18;
s1.score = 100;
cout << "姓名:" << s1.name << " 年龄: " << s1.age << " 分数:" << s1.score << endl;
//创建结构体变量第二种方法
Student s2 = {"李四", 19, 80};
cout << "姓名:" << s2.name << " 年龄: " << s2.age << " 分数:" << s2.score << endl;
//创建结构体变量第三种方法
s3.name = "王五";
s3.age = 20;
s3.score = 60;
cout << "姓名:" << s3.name << " 年龄: " << s3.age << " 分数:" << s3.score << endl;
system("pause");
return 0;
}
打印结果:
姓名:张三 年龄: 18 分数:100
姓名:李四 年龄: 19 分数:80
姓名:王五 年龄: 20 分数:60
请按任意键继续. . .
3. 结构体数组
作用:将自定义的结构体放入数组中方便维护
语法:struct 结构体名 数组名{ 元素个数 } = {{},{},... {}}
代码:
#include<iostream>
using namespace std;
#include<string>
struct Student
{
string name;
int age;
int score;
};
int main()
{
//创建结构体数组
struct Student stuArray[3] =
{
{"张三", 18, 100},
{"李四", 28, 99},
{"王五", 38, 66}
};
//给结构体数组中的元素赋值
stuArray[2].name = "赵六";
stuArray[2].age = 80;
stuArray[2].score = 60;
//遍历结构体数组
for (int i = 0; i < 3; i++)
{
cout << "姓名:" << stuArray[i].name << "; 年龄:" << stuArray[i].age << "; 分数:" << stuArray[i].score << endl;
}
return 0;
}
打印结果:
姓名:张三; 年龄:18; 分数:100
姓名:李四; 年龄:28; 分数:99
姓名:赵六; 年龄:80; 分数:60
4. 结构体指针
通过指针访问结构体中的成员
- 利用操作符
->
可以通过结构体指针访问结构体属性
代码:
#include<iostream>
using namespace std;
#include<string>
struct Student
{
string name;
int age;
int score;
};
int main()
{
//创建结构体数组
Student s = { "张三", 18, 100 };
//通过指针指向结构体变量
Student * p = &s;
//通过指针访问结构体变量中的数据
//通过结构体指针访问结构体中的属性,需要利用'->'
cout << "姓名:" << p->name << "; 年龄:" << p->age << "; 分数:" << p->score << endl;
system("pause");
return 0;
}
打印结果:
姓名:张三; 年龄:18; 分数:100
请按任意键继续. . .
5. 结构体嵌套结构体
作用:结构体中的成员可以是另一个结构体
如:每个老师辅导一个学生,一个老师的结构体中,记录一个学生的结构体。
代码:
#include<iostream>
using namespace std;
#include<string>
struct Student
{
string name = " ";
int age = 18;
int score = 0;
};
struct Teacher
{
int id = 0;
string name = " ";
int age = 0;
struct Student stu = {" ", 0, 0};
};
int main()
{
Teacher t;
t.id = 10000;
t.name = "老王";
t.age = 50;
t.stu.name = "小王";
t.stu.age = 20;
t.stu.score = 60;
cout << "老师姓名:" << t.name << "; 老师编号:" << t.id << "; 老师年龄:" << t.age << endl
<< "老师辅导的学生姓名:" <<t.stu.name << "; 学生年龄:" << t.stu.age << "; 学生考试分数:" << t.stu.score << endl;
system("pause");
return 0;
}
打印结果:
老师姓名:老王; 老师编号:10000; 老师年龄:50
老师辅导的学生姓名:小王; 学生年龄:20; 学生考试分数:60
请按任意键继续. . .
6. 结构体做函数参数
将结构体作为参数向函数中传递
传递方式:值传递;地址传递
代码:
#include<iostream>
using namespace std;
#include<string>
struct Student
{
string name = " ";
int age = 18;
int score = 0;
};
//值传递
void printStudent1(struct Student s)
{
s.age = 100;
cout << "子函数printStudent1中 姓名:" << s.name << "; 年龄:" << s.age << "; 分数:" << s.score << endl;
}
//地址传递
void printStudent2(struct Student * p)
{
p->age = 200;
cout << "子函数printStudent2中 姓名:" << p->name << "; 年龄:" << p->age << "; 分数:" << p->score << endl;
}
int main()
{
Student s;
s.name = "张三";
s.age = 20;
s.score = 80;
printStudent1(s);
cout << "main函数中打印 姓名:" << s.name << "; 年龄:" << s.age << "; 分数:" << s.score << endl;
printStudent2(&s);
cout << "main函数中打印 姓名:" << s.name << "; 年龄:" << s.age << "; 分数:" << s.score << endl;
system("pause");
return 0;
}
打印结果:
子函数printStudent1中 姓名:张三; 年龄:100; 分数:80
main函数中打印 姓名:张三; 年龄:20; 分数:80
子函数printStudent2中 姓名:张三; 年龄:200; 分数:80
main函数中打印 姓名:张三; 年龄:200; 分数:80
请按任意键继续. . .
7. 结构体中const使用场景
用const防止误操作
代码:
#include<iostream>
using namespace std;
#include<string>
struct Student
{
string name = " ";
int age = 18;
int score = 0;
};
//将函数中的形参改为指针,可以减少内存空间,而且不会复制新的副本出来
void printStudent(const Student *s)
{
//s->age = 150; //加入const之后,一旦有修改的操作就会报错,可以防止误操作
cout << "姓名:" << s->name << " 年龄:" << s->age << " 分数:" << s->score << endl;
}
int main()
{
struct Student s = {"张三", 15, 70};
printStudent(&s);
cout << "main函数中 姓名:" << s.name << " 年龄:" << s.age << " 分数:" << s.score << endl;
system("pause");
return 0;
}
打印结果:
姓名:张三 年龄:15 分数:70
main函数中 姓名:张三 年龄:15 分数:70
请按任意键继续. . .
- 案例
代码:
#include<iostream>
using namespace std;
#include<string>
struct Hero
{
string name = " ";
int age = 0;
string sex = " ";
};
void bubbleSort(Hero heroArray[], int len)
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (heroArray[j].age > heroArray[j + 1].age)
{
Hero temp = heroArray[j]; //注意temp是结构体Hero数据类型
heroArray[j] = heroArray[j + 1];
heroArray[j + 1] = temp;
}
}
}
}
void printHero(Hero heroArray[], int len)
{
for (int i = 0; i < len; i++)
{
cout << "姓名: " << heroArray[i].age << " 年龄:" << heroArray[i].age
<< " 性别:" << heroArray[i].sex << endl;
}
}
int main()
{
Hero heroArray[5] =
{
{"刘备", 23, "男"},
{"关羽", 22, "男"},
{"张飞", 20, "男"},
{"赵云", 21, "男"},
{"貂蝉", 19, "女"}
};
int len = sizeof(heroArray) / sizeof(heroArray[0]);
cout << "冒泡排序前:" << endl;
//ctrl + shift + / 是多行注释
for (int i = 0; i < len; i++)
{
cout << "姓名: " << heroArray[i].age << " 年龄:" << heroArray[i].age
<< " 性别:" << heroArray[i].sex << endl;
}
bubbleSort(heroArray,len);
cout << "冒泡排序后:" << endl;
printHero(heroArray, len);
system("pause");
return 0;
}
打印结果:
冒泡排序前:
姓名: 23 年龄:23 性别:男
姓名: 22 年龄:22 性别:男
姓名: 20 年龄:20 性别:男
姓名: 21 年龄:21 性别:男
姓名: 19 年龄:19 性别:女
冒泡排序后:
姓名: 19 年龄:19 性别:女
姓名: 20 年龄:20 性别:男
姓名: 21 年龄:21 性别:男
姓名: 22 年龄:22 性别:男
姓名: 23 年龄:23 性别:男
请按任意键继续. . .