代码清单1-1 从C++智能指针获取的裸指针变成悬垂指针 1 #include <iostream> 2 #include <memory> 3 4 int main() 5 { 6 1.3 从Rust智能指针获取的裸指针变成悬垂指针在Rust中,如果使用不慎,也会踩类似C++那样将从智能指针获取的裸指针变成悬垂指针的坑,如代码清单1-3所示。 代码清单1-3 从Rust智能指针获取的裸指针变成悬垂指针 1 fn main() { 2 println! / 裸指针指向的值: 42// 尝试访问悬垂裸指针的值: 1692729408代码清单1-3相应的没有行号的代码在github代码库(github.com/wubin28/book_LRBACP)中文件夹位置为 代码清单1-3主要演示了如何从Rust智能指针获取裸指针,并在智能指针被销毁后,该裸指针如何变成悬垂指针的过程。
#include <stdio.h> /* 2018-05-28 如何通过被调函数修改主调函数普通变量的值 1,实参必须为该普通变量的地址 2,形参必须为指针变量 3,在背调函数中通过 * 指针和一位数组 一维数组名 一维数组名是个指针常亮 它存放的是一维数组第一个元素地址 程序 printf("%#X\n",&a[0]); printf ("%#X\n",a); 输出 0X19FF2C 0X19FF2C 下标和指针的关系 如果p是个指针变量,则 p[i] 永远等价于*(p+ i) 界定一个一维数组需要的几个参数【如果一个函数需要一个一维数组,则需要接收该数组的那些信息】 指针变量的运算 指针和二维数组 */ //_____________ 0; } */ /* //下标和指针的关系 int main(void) { int a[5] = {1,2,3,4,5}; int i; for (i=0; i<5; ++i) {
如int * pf3(int x,int y) ,pf3相比于*,优先与()结合,所以这是指针函数(返回值为指针)。 而当为int (*pf3)(int x,int y)时就为函数指针变量,指针指向 int (int ,int)类型的函数,本身类型为int (*)(int,int) 。 对于函数来说其函数名就是地址,如存在一函数为int (*pf3)(int x,int y)则其函数名pf3为地址且类型为int (*)(x,y),对于&pf3其为地址且类型跟pf3类型一样,两者等价相同 所以我上面说的结论没问题 函数指针数组 函数指针数组的格式是 int (*parr1[3])(),其中parr1与[3]先结合,说明数组有三个,而后再用函数指针类型对其parr1[3]整体进行修饰, 其中这里面的内容是指针的重点核心内容。 学完了指针(1)(2)(3)后,指针的知识讲完了大部分就只差一点小内容了。 一起加油吧 !
* e1, const void* e2) { return *(int*)e1 - *(int*)e2; } void test1() { int arr[] = { 9,8,7,6,5,4,3,2,1,0 我们自己定义了一个cmp_int函数,调用它的指针的形参名为cmp,cmp用来判断两个元素的大小。
"; 这两个不同的指针变量实际上指向的是同一个常量字符串hello bit. 所以这里的str3==str4。 "; cahr *p_b = &b; 浮点指针变量:用来存放浮点型变量的地址, float c = 3,14; float *p_c = &c; 2.数组指针变量 指针数组是数组元素是指针的数组; 那么数组指针是什么呢 [5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} }; // Print(arr, 3, 5);//打印arr数组的内容 // // return 0; //} 所以 3.函数指针变量 数组指针变量是用来存放数组的地址; 函数指针变量是用来存放函数的地址。 那么函数的地址是什么? 可以看到add与&add的地址是同一个,这也就说明函数的地址就是函数名的地址。 )(2, 3)); printf("%d\n", pf3(3, 5)); return 0; }//结果也就是5和8(2+3;3+5) 函数指针的隐藏 函数指针的表达式很冗长,有时候它会隐藏于某行代码之中很难辨识
e1是一个指针,存放了一个要比较的元素的地址 e2是一个指针,存放了一个要比较的元素的地址 e1指向的元素>e2指向的元素,返回>0的数字 e1指向的元素==e2指向的元素,返回0 e1指向的元素 指针和数组笔试题解析 一维数组: int a[] = {1,2,3,4}; printf("%d\n",sizeof(a)); 数组名的理解: 数组名是数组首元素的地址 但是有2个例外: 1. sizeof char* 指针变量的大小和类型无关,不管什么类型的指针变量,大小都是4/8个字节。 指针变量是用来存放地址的,地址存放需要多大空间,指针变量的大小就是几个字节。 32位环境下,地址是32个二进制位,需要4个字节,所以指针变量的大小就是4个字节。 64位环境下,地址是64个二进制位,需要8个字节,所以指针变量的大小就是8个字节。 不要在门缝里看指针,把指针给看扁了。
指针详解(3) 字符指针变量 字符指针变量,使用来存放字符数据的地址,常用于存放单个字符或字符串。 这里str3和str4指向的是同一个常量字符串,C/C++会把常量字符串存放在一块单独的内存区域,多个指针指向同一个字符串时,它们存储的都是同一个字符串的地址。 可以借助指针数组理解,这里使用三个一维数组模拟了二维数组,将每个一维数组的首元素的地址放在指针数组里,一共有三个一维数组,没个一维数组有五个元素,等价于 int arr[3][5]。 这里,函数参数就可以使用,数组指针来接收 int arr[3][5] = {0}; int (*parr)[5] = arr;//二维数组首元素的地址,每一行有5个元素,所以数组指针中括号里的值为5 使用指针的形式接收二维数组就可以写为 [5] = {{ 1, 2, 3, 4, 5 }, { 2, 3, 4, 5, 6 }, { 3, 4, 5, 6, 7}}; Print(arr, 3, 5); return 0; } 这里将二维数组写成指针的形式
指针和数组笔试题解析 在做题之前,我们再次明确一下数组名的理解: 数组名是数组首元素的地址,但是有2个例外: sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节 个字节 printf("%d\n", sizeof(*&a));//16个字节 //sizeof(*&a) --> sizeof(a) - 16 //&a --> int (*)[4] //对整型指针解引用 ,拿到的是整型;对数组指针解引用,拿到的就是整个数组 printf("%d\n", sizeof(&a + 1));//&a+1相对于&a是跳过了整个数组,但是即使跳过了整个数组,&a+1依然是地址, 指针笔试题 #include <stdio.h> int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1); printf ;//ER printf("%s\n", *cpp[-2] + 3);//ST *cpp[-2]+3 --> **(cpp-2)+3 printf("%s\n", cpp[-1][-1] + 1
printf ( "sz2 = %d\n" , sz2); } int main () { int arr[ 10 ] = { 1 , 2 , 3 五 ⼆级指针 【指针有三级、四级等只是⼆级相对常用】l 1 作用:类比于一级指针的1作用,我们便会想到二级指针的作用是用来存放一级指针变量的地址。 **ppa = 30 ; //等价于*pa = 30; //等价于a = 30; 六 指针数组 1定义:指针数组顾名思义就是用来存放指针的数组。 指针数组的每个元素都是⽤来存放地址(指针)的。 七 指针数组模拟⼆维数组 1限制:每个数组元素个数一样,不一样最好别用很麻烦。 本篇文章就到此结束,希望有所能帮到 读者更好的了解指针,后续还会继续更新指针相关识。
5、指针运算 5.1指针 +- 整数 在 C语言(指针)1中,我们已经了解过了指针 +- 整数的情况,知道了指针 +- 整数的结果取决于它所指向的对象的类型,这里再来看一种指针 5.2指针 - 指针 我们这里直接说结论:指针 - 指针的绝对值是指针和指针之间元素的个数。 但前提是这两个指针指向的是同一块空间。 那有么有指针 + 指针的运算呢? (3)指针指向的空间被释放。 上面的代码是一个非常典型的例子,大家觉得上面的代码有什么问题? (3)指针变量不再使用时,及时置NULL,指针使用之前检查有效性; (4)避免返回局部变量的地址。
所以str1和str2不同,str3和str4相同。 二. 数组指针变量 2.1 数组指针变量是什么 上一章我们讲解了指针数组,它是存放指针(地址)的数组,那么指针数组是指针还是数组呢? [5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}}; test(arr, 3, 5); return 0; } 这里的实参和形参都写成了二维数组的形式,其实还有另一种写法 [5] = { {1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7} }; test(arr, 3, 5); return 0; } arr[i] == *(arr+i) )=&Add; //函数指针变量 //int (*pi)(int x, int y) = Add; //int (*) (int,int)函数指针类型 int ret = (*pi)(3, 6) int (*p[3]) () p 先和 [ ] 结合,说明 p 是数组,是int (*)() 类型的函数指针。
这里str3和str4都是指向一个常量字符串,C/C++中会把常量字符串存储到单独的内存空间,当多个指针指向同一个常量字符串是,所指向的是同一个地址。 (int, int) = Add; int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的 注意:函数指针变量 int(*p)(int, int)中,int代表指针指向函数的返回类型 y) { return x+y; } int main() { int(*pf3)(int, int) = Add; printf("%d\n", (*pf3)(2, 3)); printf 2.重命名指针类型 typedef int* ptr_t; 3.重命名数组指针 当我们需要将数组指针类型 int(*)[5]重命名维parr_t时,就有所不同 typedef int( int (*parr1[3])(); *parr1[3]说明这是整个存放指针的数组,而int()()就说明指针所指向的是一个函数(也是指针所指向函数的类型)。
==str4) printf("str3 and str4 are same\n"); else printf("str3 and str4 are not same\n"); return 0 在看str3 str4,前面提到,它们指向的是常量字符串,常量字符串是在静态区存储的,但是这两个字符串都一样,系统就认为常量字符串反正不能被改,那就不给多个空间了,毕竟是一样的,所以str3 str4指向的地址都是一样的 3 二维数组传参的本质 在有了数组指针的理解之后,我们来了解一下二维数组传参的本质。 先看代码。 void test(int arr[3][5]) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 5; j++) { printf ("%d ", arr[i][j]); } printf("\n"); } } int main() { int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10}
相反,3和4他们指向同一个常量字符串,C/C++会把常量字符串存储到单独的一个内存区域,当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。所以3和4是一样的。 2. [5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}}; test(arr, 3, 5); return 0; } 这里实参是二维数组,形参也写成二维数组的形式,那还有什么其他的写法吗 (int, int) = Add; int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的 函数指针类型解析: int (*pf3) (int x, int y) | 函数类型名 指向函数的参数类型 | pf3指向函数的返回类型 4.2 函数指针变量的使用 通过函数指针调用指针指向的函数 #include <stdio.h> int Add(int int ( * parr1[3])(); int * parr2 3 ; int ( * )() parr3[3]; 答案是parr1 parr1 先和 [ ] 结合,说明parr1是数组,数组的内容是什么呢
== str4) printf("str3 and str4 are same\n");//3 else printf("str3 and str4 are not 在判断语句中,str1,str2,str3,str4都是单独出现的,说明它们表示首元素的地址,str1,str2中的首元素的地址不相同,str3,str4中的地址相同。 2.数组指针变量 整型指针变量:指向整型的指针,存放的是整型的地址 字符指针变量:指向字符的指针,存放的是字符的地址 ······ 那么数组指针变量:存放的应该是数组的地址,能够指向数组的指针变量(数组的地址 [5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 }; test(arr, 3, 5); return 0; } 在test(arr, 3, 5)函数中,arr单独出现, [5] = {1,2,3,4,5,2,3,4,5,6,3,4,5,6,7}; print(arr, 3, 5); return 0; } *(arr+i)是二维数组第i行数组名;(*(arr+i)
六、野指针 1.野指针是什么? 就像流浪猫、流浪狗一样没有家一样 野指针就是指针指向的位置是未知的 2.野指针的成因 那么为什么会出现野指针呢? 主要有以下几点: 指针未初始化 int * p ; * p = 20 ; 此处的p指针未初始化,就是一个野指针,其所指向的地址为随机值 而之后的 * p 就会非法访问 指针越界访问 通过指针遍历数组时超出数组范围 故 p 为野指针,是非法访问 3.如何避免野指针 指针初始化 必须养成指针初始化的习惯,未初始化的指针不知道指向何处 若明确知道指针应指向的地址,直接赋地址值; 若不确定指针指向,应赋值为NULL ) 3.int * const p ;(const放在 * 右边修饰) (1)const放在 * 左边修饰 int const * p ; const int * p ; //二者等价 限制的是指针指向的内容 限制的是指针变量本身的内容 不能修改指针变量本身的内容( 存储的地址 ) 但指针指向的内容( 即 * p )可以改变
这里要说一点如果我们在for循环的第一个表达式中创建变量,注意只能用一个数据类型。
今天我们更新了指针进阶(3)内容, 欢迎大家关注点赞收藏⭐️留言 一、数组指针变量 1.1数组指针变量是什么? 之前我们学习的指针数组,数组中存放的是一种数组,数组中存放的是地址(指针)。 1.3二维数组的传参 按我们之前所学的,二维数组的传参一般都是按下面这种形式去传参: #include<stdio.h> void print(int arr[3][5],int row,int col [5] = { {1,2,3,4,5},{2,3,4,5,6} ,{3,4,5,6,7} }; print(arr, 3, 5); return 0; } 这样便实现了数组的传参。 [5] = { {1,2,3,4,5},{2,3,4,5,6} ,{3,4,5,6,7} }; print(arr, 3, 5); return 0; } 这样也可以得到上面那种效果。 // 或者 int (*p)() = sum; // 利用指针变量p调用函数 int result = (*p)(1, 3); // 或者 int result = p(1, 3
数组名的理解 在上⼀个章节我们在使⽤指针访问数组的内容时,有这样的代码: int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int *p = &arr[0]; 这⾥我们使用 二级指针 指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里? 这就是二级指针 。 指针数组 指针数组是指针还是数组? 我们类比⼀下,整型数组,是存放整型的数组,字符数组是存放字符的数组。 那指针数组呢?是存放指针的数组。 指针数组模拟二维数组 #include <stdio.h> int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[] = { 2,3,4,5,6 }; int arr3[] = { 3,4,5,6,7 }; //数组名是数组首元素的地址,类型是int*的,就可以存放在parr数组中 int* parr[3] =
5.使用指针访问数组 #include<stdio.h> int main(void) { int arr[10] = { 0 }; //使用指针来访问数组 int sz = sizeof(arr sz; i++) { printf("%d ", *(p + i)); } return 0; } 1.数组就是数组,是一块连续的空间(数组的大小和数组元素个数和元素类型都有关系); 2.指针 (变量)就是指针(变量),是一个变量(4/8个字节); 3.数组名是地址,是首元素的地址; 4.可是使用指针来访问数组。 这里如果把计算数组元素个数的sz放在函数调用中,打印出来的就不是1~10的元素了,具体原因看以下代码: #include<stdio.h> void Print(int* p, int sz) //应该是指针 ("%d\n", sizeof(arr)); //8 } void test2(int* arr)//参数写成指针形式 { printf("%d\n", sizeof(arr));// 8 计算一个指针变量的大小