指针和多重指针的一些理解( 谭浩强的恶梦....... )

说到C语言,就不得不说到指针,指针是有点难理解,对于刚学C语言的同学来说是很痛苦的。。。以前对指针的概念很模糊,只知道指针和地址相关,但是最近看了一本《深入理解计算机系统》,感觉好像有点开窍了,再加上stm32的寄存器开发,以及最近在Linux中总是看到多重指针,于是花了点时间去理顺一下,在这做个总结吧。

从底层讲起
计算机的内存是无序(没有序号)的,为了方便人的操控,于是把物理的地址映射到逻辑地址,即变成有序号的地址了,也就是我们看到的程序中的那些地址,形象的比喻是 存储器是一个很大的字节数组,这个数组是由很多个内存块组成的,每个块呢都有其唯一的编号,可以理解为是下标,那个编号(下标)就是 地址啦 ,我们经常说有个值保存在某个地址上,其实就是这个值放在的这个地址指向的内存块中
我们举个例子

	int a = 10;
	
	把10 存进去变量a中是吧,这我们都知道,按照我上面收的,值是保存在地址指向的内存里,假如这个值是放在  地址 0x000080 所指向的内存中 
	画个图比较形象点,但是你看,我每次操作都要写个0x0000080然后怎样怎样  ,这是不是很繁琐呢,	所以我们引用了变量,更方便人们来操控这些值

在这里插入图片描述
在这里插入图片描述

到了这,我们明白数值是和地址的关系,我们现在再来讲指针

在一些入门的书籍都是这样写的

int a = 10int *p = &a;

int * p 我们根据优先级可以 这样来写 (int *) p
这个司空见惯了,我们来分析下,10 这个值存在了一个地址所指向的内存中,我们用变量a来代表这个地址,&符号是取地址符,int * 是一个 整型的指针 的 数据类型,也是一个数据类型,只不过他是这个类型是用来声明这个变量是用来 储存地址的

int *p = &a; 几时把a的地址赋值给p;我们看下代码

#include <stdio.h>
int main()
{
int a = 8;
int *p = &a; //a的地址赋给了 p

printf(" a = %d \n",a);

printf("&a = %p \n",&a);  //取a的地址

printf(" p = %p \n",p);    //p的值是什么呢

}

在这里插入图片描述

我们可以看出,这个整型指针类型的 p 的值是 和 &a的值是一样的,p也是个变量, 那么 这个 p有值 ,那肯定个有存放值的 内存啦,那么的话肯定有地址咯, 仿照按照上面说的 p的值存放在,p的地址所指向的内存中,我们在来看看代码

#include <stdio.h>
int main()
{
int a = 8;
int *p = &a; //a的地址赋给了 p

printf(" a = %d \n",a);

printf("&a = %p \n",&a);  //取a的地址

printf(" p = %p \n",p);    //p的值是什么呢

printf("p= %p  (hex)\n",&p);    //这个是变量p的地址   & 是取地址的意思(多说几遍)

}

在这里插入图片描述

果然,跟我们推测的一样,也是有个不一的值,在这里 a的地址和p的地址相差 4,我们都知道int 的类型是 4个字节的(32 位),
这个有什么相关联吗??

(*补充下吧,知道了就跳过 *:上文说了,内存是个大数组(比喻),数组是有下标的,一个下标(地址)对一个小块内存,我们称他为内存块, 书上这个内存是大多是 8 比特位的 ,就是一个字节喽,32位刚好四个字节,也就是一个整型数据的大小,也就 是四个地址的内存块的和,有因为 内存的取用是用顺序的(高地址到低地址),所以a和p的地址相差 4 ,你看,数据类型的和地址有着很多的关系的 。)
在这里插入图片描述

我们在书上还看过这个样的代码

printf(" *p = %d \n",*p);

我们运行下
在这里插入图片描述

看! *p的值是 和 a一样的,我推测下

p的作用是什么?

在这里*可不是乘 ,而是 取值,取什么值?
取地址所指向的值,在这里 *p 是指 以p的值为地址(前面说了p的值是变量a的地址) ,通过这个地址来找到内存中所存放的值(这一点很重要,要理解)(像立即数寻址一样)
所以有了 *p = a 这一说法 , *p也叫指针变量,这样一来,我们可以不对变量a操作,也可以间接修改 a 的值了! 这也是指针的厉害之处 !

普通的一级指针我们大致上了解 , 现在我说下 多重指针 (以二级指针为例 -

二级指针 是这样的

int *p;
有两个
,依照最开始说的 int **p 看成 (int *) *p(符号的优先级),看,现在还是一个 int *的类型,变量是 *p ,*p是啥?上面我们说了 p,仿照p的, *p的意思是 ,p是个指针变量,既然是变量是可以存放值得呀,这个变量又是指针变量, 是专门存放地址的变量, 就是以p的值为地址 ,通过这个地址来找到指向内存中所存放的值, 我们看代码(二级指针用q来表示)

![在这里插入图片描述](https://img-blog.csdnimg.cn/20200619092246287.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3JvdXNlMjYxNw==,size_16,color_FFFFFF,t_70在这里插入图片描述
看,q变量的值,正是 &p,q是存放了变量p的地址

*q 是 p的值,跟我们说的一样, *是其通过以变量所保存的值为地址,来访问改地址的内存, *q 通过 q的值 0x0028FEB8,以这个值为地址 找到了以该地址的所指向的内存, 我们发现q的值正好的 p的地址.

我们再来看一段代码

#include <stdio.h>
int main()
{
    int a = 8;
    int *p = &a;   //a的地址赋给了 p
   
    int **q = &p;
    printf(" a = %d \n",a);
    
    printf("&a = %p \n",&a);  //取a的地址
    
    printf(" p = %p \n",p);    //p的值是什么呢
    
    printf("&p= %p  (hex)\n",&p);    //这个是变量p的地址   & 是取地址的意思(多说几遍)
    
    printf("*p = %d \n \n",*p); 
    
    printf(" q = %p \n \n",q); 
    printf("* q = %p \n \n",*q); 
    printf("q的地址 = %p \n \n",&q); 
    printf("**q = %d \n",**q);  
}

在这里插入图片描述

加了一行 这个

printf("**q = %d \n",**q);  

**q怎么是 8呢?

上面我们知道 *q的值是 p的值 ,我们这来看
**q -> *(*q)
*q = p
**q -> *(*q) -> *(p)
p=0x0028FEBC
还是上面说的 *是通过以变量的值作为地址,再来访问内存,所以 才出现了 **p =a =8

我们是不是对二级指针有较好的理解啦

就先到这里啦
多重指针的使用可以点这里

  • 13
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
这是一个比较大的项目,需要设计数据库、界面和逻辑等多个方面。以下是一个简单的图书馆管理系统的示例代码,供参考: ```c #include <stdio.h> #include <string.h> // 图书信息结构体 typedef struct { char id[10]; // 图书编号 char name[50]; // 图书名称 char author[50]; // 作者 int count; // 数量 } Book; // 学生信息结构体 typedef struct { char id[10]; // 学号 char name[50]; // 姓名 int borrow_count; // 借阅数量 Book books[5]; // 借阅的图书 } Student; // 学生列表 Student students[100]; int student_num = 0; // 图书列表 Book books[100]; int book_num = 0; // 初始化图书列表 void init_books() { books[0] = (Book){"001", "C语言程序设计", "谭浩强", 10}; books[1] = (Book){"002", "Java编程思想", "Bruce Eckel", 5}; book_num = 2; } // 初始化学生列表 void init_students() { student_num = 0; } // 根据图书编号查找图书 Book* find_book(char* id) { for (int i = 0; i < book_num; i++) { if (strcmp(books[i].id, id) == 0) { return &books[i]; } } return NULL; } // 根据学生学号查找学生 Student* find_student(char* id) { for (int i = 0; i < student_num; i++) { if (strcmp(students[i].id, id) == 0) { return &students[i]; } } return NULL; } // 借书 void borrow_book(char* student_id, char* book_id) { Student* student = find_student(student_id); if (student == NULL) { printf("学生不存在!\n"); return; } Book* book = find_book(book_id); if (book == NULL) { printf("图书不存在!\n"); return; } if (book->count <= 0) { printf("图书已被借完!\n"); return; } if (student->borrow_count >= 5) { printf("借阅数量已达上限!\n"); return; } student->books[student->borrow_count] = *book; student->borrow_count++; book->count--; printf("借书成功!\n"); } // 还书 void return_book(char* student_id, char* book_id) { Student* student = find_student(student_id); if (student == NULL) { printf("学生不存在!\n"); return; } Book* book = find_book(book_id); if (book == NULL) { printf("图书不存在!\n"); return; } int index = -1; for (int i = 0; i < student->borrow_count; i++) { if (strcmp(student->books[i].id, book_id) == 0) { index = i; break; } } if (index == -1) { printf("学生未借阅该图书!\n"); return; } for (int i = index; i < student->borrow_count - 1; i++) { student->books[i] = student->books[i + 1]; } student->borrow_count--; book->count++; printf("还书成功!\n"); } // 显示学生信息 void show_student(char* student_id) { Student* student = find_student(student_id); if (student == NULL) { printf("学生不存在!\n"); return; } printf("学号:%s\n", student->id); printf("姓名:%s\n", student->name); printf("借阅数量:%d\n", student->borrow_count); printf("借阅的图书:\n"); for (int i = 0; i < student->borrow_count; i++) { printf(" %s 《%s》 %s\n", student->books[i].id, student->books[i].name, student->books[i].author); } } // 显示图书信息 void show_book(char* book_id) { Book* book = find_book(book_id); if (book == NULL) { printf("图书不存在!\n"); return; } printf("编号:%s\n", book->id); printf("名称:%s\n", book->name); printf("作者:%s\n", book->author); printf("数量:%d\n", book->count); } // 显示所有图书 void show_all_books() { printf("所有图书:\n"); for (int i = 0; i < book_num; i++) { printf(" %s 《%s》 %s 数量:%d\n", books[i].id, books[i].name, books[i].author, books[i].count); } } // 显示所有学生 void show_all_students() { printf("所有学生:\n"); for (int i = 0; i < student_num; i++) { printf(" %s %s 借阅数量:%d\n", students[i].id, students[i].name, students[i].borrow_count); } } int main() { init_books(); init_students(); while (1) { printf("\n请选择操作:\n"); printf("1. 借书\n"); printf("2. 还书\n"); printf("3. 显示学生信息\n"); printf("4. 显示图书信息\n"); printf("5. 显示所有图书\n"); printf("6. 显示所有学生\n"); printf("7. 退出\n"); int choice = 0; scanf("%d", &choice); if (choice == 1) { printf("请输入学生学号和图书编号:\n"); char student_id[10], book_id[10]; scanf("%s %s", student_id, book_id); borrow_book(student_id, book_id); } else if (choice == 2) { printf("请输入学生学号和图书编号:\n"); char student_id[10], book_id[10]; scanf("%s %s", student_id, book_id); return_book(student_id, book_id); } else if (choice == 3) { printf("请输入学生学号:\n"); char student_id[10]; scanf("%s", student_id); show_student(student_id); } else if (choice == 4) { printf("请输入图书编号:\n"); char book_id[10]; scanf("%s", book_id); show_book(book_id); } else if (choice == 5) { show_all_books(); } else if (choice == 6) { show_all_students(); } else if (choice == 7) { break; } else { printf("无效的操作!\n"); } } return 0; } ``` 该示例代码包含了借书、还书、显示学生信息、显示图书信息、显示所有图书和显示所有学生等功能。需要注意的是,该代码仅为示例,实际的图书馆管理系统需要根据具体需求进行设计和开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值