- c + + 学习笔记(指针、结构体、链表)
- 一、指针
- 二、指针与数组
- 1 数字数组
- 2 字符串数组
- 3 指针的指针
- 4 数组地址和首元素地址
- 5 数组指针
- 6 二维数组和数组指针
- 三、指针与函数
- 1 整型作为函数的参数
- 2 一维数组作为函数的参数
- 3 二维数组作为函数的参数
- 4 函数的返回值类型为指针类型
- 5 函数指针
- 5.1 函数指针变量注意
- 5.2 函数指针变量 使用typedef定义
- 5.3 函数指针作为函数的参数
- 四、 动态内存
- 1 基本操作
- 2 操作数组空间
- 五、 字符串处理函数
- 1 测量字符串的长度 strlen
- 2 字符串拷贝函数 strcpy
- 3 字符串追加 strcat
- 4 字符串比较 strcmp
- 六、 结构体
- 1 结构体
- 1.1 结构体概述
- 1.2 结构体变量初始化和清空
- 1.3 使用键盘赋值
- 1.4 单独操作结构体中的单元
- 1.5 相同类型结构体变量之间赋值方法
- 2 结构体嵌套结构体
- 3 结构体数组
- 4 结构体指针变量
- 4.1 结构体数组元素的指针变量
- 5 结构体的指针成员
- 5.1 结构体指针指向堆区
- 5.2 浅拷贝
- 5.3 深拷贝
- 5.4 结构体变量在堆区 结构体的指针成员也指向堆区
- 6 结构体的对齐规则
- 七、 链表
- 1 概述
- 2 静态链表
- 3 动态链表
- 3.1 main函数设计
- 3.2 链表插入节点
- 3.3 遍历链表
- 3.4 按姓名查询
- 3.5 删除节点
- 3.6 释放链表
- 3.7 整体的代码
指针就是用来存储地址的类型
int main()
{
int num = 100;
int* p = #
cout << "num的地址是:" << p<
二、指针与数组
1 数字数组
数组名就是数组首地址
int * arr[4] 这是一个指针数组,本质是个数组,每一个元素是一个指针。
下面例子里arr每一个元素存储的是arr0的每一个对应的元素的地址
//数字数组练习
void test02() {
int arr0[4] = { 10,20,30,40 };
//声明一个指针数组
int* arr[4];
//输出结果为16,因为指针大小为4B(32位平台)
cout << sizeof(arr) << endl;
//计算数组大小
int n = sizeof(arr) / sizeof(arr[0]);
//将四个数字的地址赋值给指针数组
for (int i = 0; i < n; i++)
{
arr[i] = &arr0[i];
//输出地址
cout << arr[i] << "->";
//输出内容
cout << *arr[i] << endl;
}
}
2 字符串数组
这里声明的arr是指针数组,每一个元素存贮的都是一个地址,所以后面{}里面的元素都进行了(char *)的强制转换。
arr[i] 代表的是第i个元素的首地址
arr[i]+n 代表的是 第i个元素 从第n个字符开始的地址
*(arr[i] + n) 代表的是 第i个元素 从第n个字符的具体的值
//字符串数组练习;
void test01() {
char* arr[4] = { (char *)"abcde",(char*)"fghij",(char*)"jlmno",(char*)"pqrst" };
//获取数组大小;
int n = sizeof(arr) / sizeof(arr[0]);
cout << "显示每一个元素" << endl;
for (int i = 0; i < n; i++)
{
//打印每一项的内容。arr[i]获取的是每一个元素的首地址
cout << arr[i] << " ";
}
cout << endl;
cout << "从每一个元素第三项开始显示" << endl;
for (int i = 0; i < n; i++)
{
cout << arr[i] + 3 << " ";//这里是获取每个元素第三项的地址,显示的是第三项及以后的内容
}
cout << endl;
cout << "显示第三项" << endl;
for (int i = 0; i < n; i++)
{
cout << *(arr[i] + 3) << " ";
}
}
3 指针的指针
num是个整型变量
*p接收num的地址
**p接收指针p的地址
//指针的指针;
void test03() {
int num = 10;
int* p = #
int** q = &p;
cout << "p的值:" << p << endl;;
cout << "*p的值:" << *p << endl;
cout << "q的值:" << q << endl;
cout << "*q的值:" << *q << endl;
cout << "**q的值:" << **q << endl;
}
4 数组地址和首元素地址
区分: 数组首元素地址 和 数组首地址
数组首元素地址:数组第一个元素的地址 &arr[0]==arr arr+1跳过的是第一个元素
数组首地址: 数组的地址 &arr &arr+1跳过整个数组
这两个数值上一样,但是意义不同
//数组地址和首元素地址;
void test04() {
int arr[5] = { 12,34,56,78,91 };
cout << "数组首元素地址:arr = " << arr << endl;
cout << "跳过第一个元素:arr + 1 = " << arr + 1 << endl;
cout << "数组首地址:&arr = " << &arr << endl;
cout << "跳过整个数组 &arr + 1 = "<< &arr + 1 << endl;
}
5 数组指针
本质是:指针变量保存的是 数组的首地址
这里注意区分指针数组和数组指针
指针数组 int* p[5]
数组指针 int(* p)[5]
注意:对数组首地址取 * ==数组首元素地址
//数组指针;
void test05() {
//本质是:指针变量保存的是 数组的首地址
int arr[5] = { 10,20,30,40,50 };
int(*p)[5] = NULL;//声明数组指针,小括号不可以去掉
cout << "sizeof(p)=" << sizeof(p) << endl;//32位平台为4B
cout << "p=" << p << endl;//因为初始化为空,所以大小为0
//因为是数组指针,一个数组是5个元素,一个元素4B大小,所以一共20B,十六进制为14
cout << "p+1=" << p + 1 << endl;
//如何通过数组指针去访问数组元素
int* q = arr;//获取首元素地址
cout << "第二个元素为:" << *(q + 2) << endl;//q为首元素地址,q+2为第二个元素的地址,整体取 * 获取到具体的值
}
6 二维数组和数组指针
二维数组的组名代表的是第0行的行地址 +1跳过一个行
对行地址取 * 代表当前行第0列的列地址
//二维数组和数组指针;
void test06() {
//声明二维数组
int arr[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
cout << "arr=" << arr << endl;//第0行首地址
cout << "arr + 1 =" << arr+1 << endl;//第一行首地址
cout << "*arr=" << *arr << endl;//第0列首地址
cout << "*arr+1=" << *arr+1 << endl;//第1列首地址
cout << "**arr=" << **arr << endl;//第0行第0列的元素值
cout << "*(*arr+1)=" << *(*arr+1) << endl;//第0行第1列元素的值
cout << "**arr + 1 =" << **arr +1 << endl;//第0行第0列的元素值+1
}
三、指针与函数
如果想在函数内部 修改函数外部的值 需要将外部变量的地址 传递给函数
1 整型作为函数的参数
void setnum(int* p) //int * p=&num
{
*p = 100;/
if (strcmp(pb->name,name)==0)
{
return pb;
}
else
{
return NULL;
}
return NULL;
}
}
3.5 删除节点
删除节点的时候注意,要记得上一个节点,这样才能删除掉当前的节点(让上一个节点的next指向当前节点的next)
还要注意删除的时候删除的是头节点还是链表的中尾部节点
为什么中尾部和在一起
在下面的代码中pf记录上一个节点,pb是当前节点。删除尾部节点和中间节点的时候,代码都可以使用pf->next=pb->next。因为删除尾部节点的时候就是让pf的next为NULL,而pb的next恰好是NULL。所以这俩可以和在一起。
//删除节点
Stu* deleteLink(Stu* head, int num) {
//判断链表是否存在
if (NULL == head)
{
cout << "链表不存在" << endl;
return NULL;
}
else
{
//逐个节点比较
Stu* pf = head, * pb = head;
while ((pb->num != num) && (pb->next != NULL)) {
pf = pb;
pb = pb->next;
}
//循环结束
if (pb->num==num)//找到删除点
{
if (pb==head)//头节点删除
{
head = head->next;
delete pb;
}
else//删除中尾部节点
{
pf->next = pb->next;
delete pb;
}
}
else
{
cout << "未找到要删除的节点" << endl;
}
return head;
}
}
3.6 释放链表
释放是逐个节点进行释放,要注意pb和head的关系
//释放链表
Stu* freeLink(Stu* head) {
//判断链表是否存在
if (NULL == head)
{
cout << "链表不存在" << endl;
return NULL;
}
else//逐个节点释放
{
Stu* pb = head;
while (pb != NULL) {
head = head->next;
delete pb;
pb = head;
}
return head;
}
}
3.7 整体的代码
#include
#include
using namespace std;
//定义链表节点类型
struct Stu
{
//数据域
int num;
char name[32];
//指针域
Stu* next;
};
//构建一个链表头
Stu* head = NULL;
//帮助信息
void help() {
cout << "********************************" << endl;
cout << "* help:帮助信息 *" << endl;
cout << "* insert:插入链表节点 *" << endl;
cout << "* print:遍历链表 *" << endl;
cout << "* search:查询链表某个节点 *" << endl;
cout << "* delete:删除链表某个节点 *" << endl;
cout << "* free:释放整个链表 *" << endl;
cout << "* quit:退出程序 *" << endl;
cout << "* cls :清空屏幕 *" << endl;
cout << "********************************" << endl;
}
//插入函数 使用头插法
Stu* insertLink(Stu* head, Stu tmp) {
//从堆区申请带插入的节点空间
Stu* pi = new Stu;
//给空间赋值
*pi = tmp;
pi->next = NULL;
//判断链表是否存在
if (NULL==head)//不存在
{
head = pi;
}
else//存在
{
pi->next = head;
head = pi;
}
return head;
}
//插入函数 尾插法(未使用)
Stu* insertLink1(Stu* head, Stu tmp) {
//从堆区申请带插入的节点空间
Stu* pi = new Stu;
//给空间赋值
*pi = tmp;
pi->next = NULL;
//判断链表是否存在
if (NULL == head)//不存在
{
head = pi;
}
else//存在
{
Stu* pb = head;
//寻找尾节点
while (pb->next!=NULL)
{
pb = pb->next;
}
//在尾节点插入pi
pb -> next = pi;
}
return head;
}
//遍历链表
void printLink(Stu* head) {
//判断链表是否存在
if (NULL==head)
{
cout << "链表不存在" << endl;
return;
}
else
{
Stu* pb = head;
while (pb!=NULL)
{
cout << pb->name << " " << pb->num << endl;
pb = pb->next;
}
return;
}
}
//查询链表
Stu* searchLink(Stu* head, char* name) {
//判断链表是否存在
if (NULL==head)
{
cout << "链表不存在" << endl;
return NULL;
}
else
{
//逐个节点查询
Stu* pb = head;
while ((strcmp(pb->name,name)!=0)&&(pb->next!=NULL))//判断pb的名字和要查询的名字不一致且还有下一个节点的时候,循环继续
{
pb = pb->next;
}
if (strcmp(pb->name,name)==0)
{
return pb;
}
else
{
return NULL;
}
return NULL;
}
}
//删除节点
Stu* deleteLink(Stu* head, int num) {
//判断链表是否存在
if (NULL == head)
{
cout << "链表不存在" << endl;
return NULL;
}
else
{
//逐个节点比较
Stu* pf = head, * pb = head;
while ((pb->num != num) && (pb->next != NULL)) {
pf = pb;
pb = pb->next;
}
//循环结束
if (pb->num==num)//找到删除点
{
if (pb==head)//头节点删除
{
head = head->next;
delete pb;
}
else//删除中尾部节点
{
pf->next = pb->next;
delete pb;
}
}
else
{
cout << "未找到要删除的节点" << endl;
}
return head;
}
}
//释放链表
Stu* freeLink(Stu* head) {
//判断链表是否存在
if (NULL == head)
{
cout << "链表不存在" << endl;
return NULL;
}
else//逐个节点释放
{
Stu* pb = head;
while (pb != NULL) {
head = head->next;
delete pb;
pb = head;
}
return head;
}
}
int main()
{
help();
//在while循环里执行操作
while (1)
{
char cmd[64] = "";
cout << "请输入操作指令:";
cin >> cmd;
if (strcmp(cmd, "help") == 0)
{
help();
}
else if (strcmp(cmd, "insert") == 0)
{
cout << "请输入要插入的节点信息(num name):" << endl;
Stu tmp;
cin >> tmp.num >> tmp.name;
head = insertLink(head, tmp);
}
else if (strcmp(cmd, "search") == 0)
{
cout << "请输入要查询的姓名:" << endl;
char name[32] = "";
cin >> name;
Stu* ret = NULL;
ret = searchLink(head, name);
if (ret!=NULL)
{
cout << "查询的结果: num= " << ret->num << " name= " << ret->name << endl;
}
else
{
cout << "查无此人" << endl;
}
}
else if (strcmp(cmd, "print") == 0)
{
printLink(head);
}
else if (strcmp(cmd, "delete") == 0)
{
cout << "请输入要删除的学号:" << endl;
int num = 0;
cin >> num;
head = deleteLink(head, num);//如果删除的是头节点,头节点就需要更新,所以拿头节点接一下
}
else if (strcmp(cmd, "free") == 0)
{
head = freeLink(head);
cout << "释放完成" << endl;
}
else if (strcmp(cmd, "quit") == 0)
{
return 0;
}
else if (strcmp(cmd, "cls") == 0)
{
system("cls");
}
else
{
cout << "请输入正确的指令" << endl;
}
}
}



