目录
一.指针运算?
1.曾经用过的案例:写一个模拟实现字符串的函数
2.指针+-整数?
3.指针-指针?
方法1:大地址-小地址或者小地址-大地址⛅
方法2:模拟实现指针相减?
4.指针的关系运算
二.指针和数组
☑️☑️☑️重点:
?画图笔记:
1.指针和数组间的联系:
2.用指针访问二维数组
三.二级指针✅
?️对于二级指针的运算有:
四.指针数组
?1.存放整型指针的数组
2.二级指针数组的应用
⁉️通过整型指针存放三个一维数组
一.指针运算?
1.曾经用过的案例:写一个模拟实现字符串的函数
前提说明:字符串在传参的时候,或者说作为一个表达式的时候,它的值是首字符的地址,它并不是把“abcdef”传给了my_strlen函数,而是把首字符'a'的地址传过去了,首字符'a'的地址是char类型的地址,需要一个char*的指针接收
#include<stdio.h>int my_strlen(char* str){int count = 0;while (*str != '\0'){count++;//str++;//指针+整数str = str + 1; }return count;}int main(){int len = my_strlen("abcdef");printf("%d\n", len);return 0;}
思路:看str指向的那个字符是什么,不是‘\0’,计数器count++,指针str是一个char*的指针,+1跳过一个char类型的变量,最后当遇到'\0',返回count作为返回值。
2.指针+-整数?
#define N_VALUES 5 float values [ N_VALUES ]; float * vp ; // 指针 +- 整数;指针的关系运算 for ( vp = & values [ 0 ]; vp < & values [ N_VALUES ];) { * vp ++ = 0 ; }3.指针-指针?
指针- 指针 == 地址 - 地址
前提:
两个指针指向同一块空间,指针的类型是一致的指针 - 指针得到的是指针和指针之间的元素个数(这是语法规定的)由前面我们可知指针+-整数是等于指针的,那如果指针 - 指针不就是整数了吗,分两种情况
方法1:大地址-小地址或者小地址-大地址⛅
1.大地址-小地址:随着数组下标的增长,地址是由低到高变化的
int main(){int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int n = &arr[9] - &arr[0];printf("%d\n", n);return 0;}
执行:
个人思路:把内存监视调出来:可以观察一下它们的地址
?实践证明:
用它们的地址相减:减出一个24,这个24是一个16进制数,可以代表它们之间相差的内存空间, 将16进制24转换为10进制后,是36byte也就是相差9个(整型)元素的地址空间
2.小地址 - 大地址(特殊情况)
int main(){int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int n = &arr[0] - &arr[9];printf("%d\n", n);return 0;}
方法2:模拟实现指针相减?
int my_strlen(char* str){ char* start = str;while (*str!='\0'){str++;}return str - start;}int main(){int len = my_strlen("abcdef");printf("%d\n", len);return 0;}
执行:
图解:
4.指针的关系运算
for ( vp = & values [ N_VALUES ]; vp > & values [ 0 ];) { *-- vp = 0 ; } 经过修改之后的代码 for ( vp = & values [ N_VALUES - 1 ]; vp >= & values [ 0 ]; vp -- ) { * vp = 0 ; }关于这两种写法的理解:
关于野指针的知识:vp1:起始位置这不是越界了吗?没有。
?理解:
这个位置后面的空间虽然不属于values数组,但指向这个位置是没有形成越界访问的这种效果,仅仅指向是没有问题的!!!只有当对vp1解引用了或者说访问数组values[5]的元素了,通过vp1改数组values后面空间的数据了,那确实是非法访问了?
关于vp1和vp2指针问题:
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
? 标准规定: 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针 进行比较。
二.指针和数组
二者区别:
指针就是指针,不是数组
数组就是数组,也不是指针
如何求大小:
指针的大小:4/8个字节,指针是存放地址的,地址的存放需要多大空间,指针变量的大小就是多少
数组的大小:取决数组的元素个数和每个元素的类型
我们看一个例子:
#include <stdio.h>int main(){ int arr[10] = {1,2,3,4,5,6,7,8,9,0}; printf("%p\n", arr); printf("%p\n", &arr[0]); return 0;}
☑️☑️☑️重点:
#include <stdio.h>int main(){int arr[] = { 1,2,3,4,5,6,7,8,9,0 };int* p = arr; //指针存放数组首元素的地址int sz = sizeof(arr) / sizeof(arr[0]);int i = 0;for (i = 0; i < sz; i++){printf("&arr[%d] = %p <====> p+%d = %p\n", i, &arr[i], i, p + i);}return 0;}
运行结果: ![](http://zhangshiyu.com/zb_users/upload/2023/04/20230411122119168118687984092.png)
?画图笔记:
循环部分的代码也可以写成:
for (i = 0; i < sz; i++) { printf("%d ", i[arr]); //i[arr] -- *(i+arr) //arr[i] -- *(arr+i) }
?注意的点:
三者等价:*(arr+i)-->*(p+i)-->arr[i]
不管是写成i[arr]还是arr[i],编译器都会转换成*(arr+i)或者*(i+arr)
所以 p+i 其实计算的是数组 arr 下标为i的地址。
那我们就可以直接通过指针来访问数组。
如下:
1.指针和数组间的联系:
#include<stdio.h>int main(){int arr[10] = { 0 };int* p = arr;int i = 0;//存放for (i = 0; i < 10; i++){*p = i + 1;p++;}//打印p = arr;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}return 0;}
存放的另一种写法:
for(i=0;i<10;i++)
{
*(p+i)=i+1;
}
图解:
上述图解和代码证明了:
1.指针可以指向数组元素的
2.因为指针可以运算,所以借助于指针可以访问数组
2.用指针访问二维数组
int main(){int arr[3][5];return 0;}
等价的写法: ?
arr[i][j] --> (*(arr+i))[j] -->*(*(arr+i)+j)
三.二级指针✅
指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 答:二级指针#include<stdio.h>int main(){int a = 10;//a是要在内存中申请4个字节的空间的//一级指针int* pa = &a;//0x0012ff40,pa是变量,用来存放地址,也得向内存申请,申请4/8//二级指针int** ppa = &pa;//0x0012ff48//三级指针int*** pppa = &ppa;printf("%d\n", **ppa);return 0;}
✅✅注意:最右边的*间隔分开只是为了更好解释它是一个什么样的指针,空格加不加无所谓的
一级指针:
int* pa = &a; --> *表明pa是指针,而int表明pa指向的对象a的类型是int
二级指针:是用来存放一级指针变量的地址
int* * ppa = &pa --> *告诉我们ppa是指针,而int*表明ppa指向的对象pa的类型是int*
如此类推....
可以打开内存监视窗口看一下情况,各个变量的地址和生成的对应指针存放的地址是一样的
画图:
?️对于二级指针的运算有:
*ppa 通过对ppa中的地址进行解引用,这样找到的是 pa ,*ppa 其实访问的就是 pa . int b = 20 ; * ppa = & b ; // 等价于 pa = &b; **ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a . ** ppa = 30 ; // 等价于 *pa = 30; // 等价于 a = 30;还有一点:其实在开发的过程中三级指针很少用。
四.指针数组
指针数组是指针还是数组? 答案:是数组。是存放指针的数组。 数组我们已经知道整形数组,字符数组。 int arr1 [ 5 ]; char arr2 [ 6 ];?1.存放整型指针的数组
#include<stdio.h>int main(){int* p;int a = 10;int b = 20;int c = 30;//指针数组--存放指针的数组int* arr[] = { &a,&b,&c };//int i = 0;for (i = 0; i < 3; i++){printf("%d ", *(arr[i]));}char* arr2[5];float* arr3[5];return 0;}
执行:
▶️关于这里的知识点总结:
除此之外还有:
char* arr2[5];//存放字符指针数组
float* arr3[5];//存放浮点型指针的数组
...等等数组
2.二级指针数组的应用
int main(){ char* arr[5];//{char* char* char* char* char*} char** p = arr;//&arr[0] - char** return 0;}
arr表示首元素的地址也就是&arr[0],我本以为它的类型在监视中是char**,结果是char*[5] -- 上图可知。
反思得:
其实首元素的地址:&arr[0]类型确实是char**,监视的时候是表示的是一个数组,监视的是数组类型而不是数组首元素地址的类型
原因:
监视窗口有自己的规则,这个不必深究
⁉️通过整型指针存放三个一维数组
#include<stdio.h>int main(){ //int arr1[] = { 1,2,3,4,5 };//数组arr1内部的元素是连续的,但是arr1,arr2,arr3三者不一定连续int arr2[] = { 2,3,4,5,6 };int arr3[] = { 3,4,5,6,7 };int* ptr[] = { arr1,arr2,arr3};int i = 0;for (i = 0; i < 3; i++){int j = 0;for (j = 0; j < 5; j++){printf("%d ", ptr[i][j]);}printf("\n");}return 0;}
注意:数组arr1的内部元素是连续的,但是arr1,arr2,arr3三者不一定连续
执行结果: