深入探究指针及指针类型
目录
- 前言
- 1. 字符指针
- 2. 数组指针
- 2.1 数组指针的定义
- 2.2 & 数组名 VS 数组名
- 2.3 数组指针的应用
- 3. 数组传参和指针传参
- 3.1 一维数组传参
- 3.2 二维数组传参
- 3.3 一级指针传参
- 3.4 二级指针传参
- 4. 函数指针
- 5. 函数指针数组
- 6. 指向函数指针数组的指针
- 7. 回调函数
前言
本篇为指针的进阶,如果初阶指针还不太明白的伙伴们请戳:初阶指针。
指针的基本概念:
- 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
- 指针变量的大小是4/8个字节(根据32/64位平台)。
- 指针是有类型的,指针的类型决定了指针±整数的的步长,指针解引用操作时可以访问的范围。
- 指针的简单运算。
1. 字符指针
在指针的类型中我们知道有一种指针类型为字符指针 char* ;
int main()
{char ch = 'a';char* pc = &ch;*pc = 'b';return 0;
}
还可以写成下面这种形式:
int main()
{char* pc = "abcd";//是指针里存了一个字符串吗printf("%s\n", pc);return 0;
}
并不是,其实是把字符串的首元素地址a存放到指针pc里,%s只要给出字符串的起始地址,一直打印到\0之前。
因为此字符串为常量字符串,不能被修改,放在未被修饰的指针变量不安全,此时在前面加上const修饰,使其无法被修改,这样就很好的保护了字符串。
const char* pc = "abcd";
一道面试题:
int main()
{char* p1 = "abcd";char* p2 = "abcd";char arr1[] = "abcd";char arr2[] = "abcd";if (p1 == p2)puts("p1 == p2");elseputs("p1 != p2");if (arr1 == arr2)puts("arr1 == arr2");elseputs("arr1 != arr2");return 0;
}
//输出的结果是什么?
那么这两种形式的区别在哪?画图分析:
2. 数组指针
在探究数组指针前,看回顾一下什么是指针数组:
指针数组 - 是数组。是用来存放指针变量的数组。
int arr[10];//整形数组
char brr[10];//字符数组
...
int* crr[10];//整形指针数组 - 存放整形指针的数组
char* drr[10];//字符指针数组
指针数组的作用:
int main()
{int arr[] = { 1,2,3 };int brr[] = { 2,3,4 };int crr[] = { 3,4,5 };//定义指针数组int* prr[3] = { arr, brr, crr };//数组名相当于首元素地址//把三个数组首元素放在prr里//是地址,因此prr的类型为整形指针类型return 0;
}
这种写法,其实也就模拟写成了一个二维数组,这该怎么理解?
那段代码所对应的意义,知道了该指针数组的布局,那么访问并打印prr数组里的内容几乎和二维数组无差:
int main()
{int arr[] = { 1,2,3 };int brr[] = { 2,3,4 };int crr[] = { 3,4,5 };int* prr[3] = { arr, brr, crr };//三个元素,对应下标0 1 2//找到对应的首元素地址for (int i = 0; i < 3; ++i){//再用一个下标向后偏移分别找到该数组里的每个元素for (int j = 0; j < 3; ++j){//prr[i][j] == *(prr[i] + j) == *(*(prr+i)+j)printf("%d ", *(prr[i] + j));}printf("\n");}return 0;
}
用一个指针数组巧妙地把三个一维数组结合在一起,好像一个二维数组,这是指针数组的一个作用。
二维数组在内存中是连续存放的,这三个一维数组不一定是,所以说是模拟二维数组。
2.1 数组指针的定义
在了解了指针数组之后,接下来探究数组指针:
整形指针 - 指向整形的指针
字符指针 - 指向字符的指针
…
数组指针 - 是指针 - 指向数组的指针。
下面两条语句分别是什么含义:
int *p1[10];
int (*p2)[10];
//p1, p2分别是什么?
[ ]的优先级要高于星号的,所以必须加上()来保证p先和*结合。
2.2 & 数组名 VS 数组名
要弄清楚数组指针如何使用,就必须再次理解数组名和&数组名的含义:
数组名通常是代表首元素的地址,但是有两个意外:
- sizeof(数组名)
这里的arr是整个数组,计算的是数组的总大小。
必须是内部单独放一个数组名
- &数组名
这里的数组名表示的依然是整个数组,&数组名取出的是整个元素的地址。(单位是字节)
具体有什么区别?
代码说明:
int main()
{int arr[5] = { 0 };//数组首元素地址放在int*的指针int* p1 = arr;//取出整个数组的地址放在数组指针p2里int (*p2)[5] = &arr;//这两个指针的区别://p1存放的是第一个整形元素的地址,指向第一个元素,不能代表整个数组。//而p2存放的是整个数组arr的地址,指向的是一个数组。return 0;
}
去掉名字就是它的类型:int* \ int (*)[5]。
后面的方括号必须要带数组的元素个数,否者会警告。
2.3 数组指针的应用
了解了数组指针的基本概念,紧接着就来探究数组指针的用法:
打印数组元素:
int main()
{int arr[5] = { 1,2,3,4,5 };int (*p)[5] = &arr;//存放整个数组的地址for (int i = 0; i < 5; ++i){printf("%d ", *(*p + i));///首先解引用p找到了数组arr首元素的地址//即*p == arr//地址+i在解引用向后偏移找到每个元素}return 0;
}
这种用法用起来很别扭,其实是非常不合理的写法。
正常的写法应该是:
int main()
{int arr[5] = { 1,2,3,4,5 };int* p = arr;for (int i = 0; i < 5; ++i){printf("%d ", *(p + i));//存放首元素地址p的指针//p+i找每个元素地址//解引用找到元素}return 0;
}
这种方法和第一种比起来清晰程度不言自明。
数组指针的作用并不体现在一维数组上,在二维或者多维数组才是数组指针的妙用。
接下来探究数组指针正确的用法。
函数打印二维数组:
//有两种写法:
//数组传参数组接收:int arr[3][4]//数组传参实际上传过来的是首元素地址
//这里形参写成指针的形式该怎么写?
void print(???, int r, int c)
{
}
int main()
{int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };print(arr, 3, 4);return 0;
}
既然二维数组数组名是一它第一行一维数组的地址,那么形参就要设置一个指向一维数组地址的指针:
(*pa)是指针,指向数组,数组有四个元素(*pa)[4],每个元素是int类型
int (*pa)[4]
void print(int (*pa)[4], int r, int c)
{//pa+i指向第i行for (int i = 0; i < r; ++i){//pa+i解引用得到当前行的首元素地址for (int j = 0; j < c; ++j){//首元素地址+j再解引用得到当前元素printf("%d ", *(*pa + i) + j));//printf("%d ", pa[i][j]);}printf("\n");}
}int main()
{int arr[3][4] = { 1,2,3,4,2,3,4,5,3,4,5,6 };print(arr, 3, 4);return 0;
}
为什么数组指针+1跳过一个数组:
pa 的类型是一个数组指针:int (*)[4]
pa是指向一个数组,数组4个整形元素
因此pa+1跳过一个4个int元素的数组.
了解了指针数组和数组指针来回顾并看看下面代码的意思:
int arr[5];
//arr是整形数组
int *parr1[10];
//parr1是整形指针数组
int (*parr2)[10];
//parr2是整形数组指针
int (*parr3[10])[5];
//parr3是存放数组指针的数组
int main()
{int arr[] = { 1,2,3 };int brr[] = { 1,0,1 };int crr[] = { 1,2,3 };int (*parr[3])[3] = { &arr, &brr, &crr };for (int i = 0; i < 3; ++i){int (*pa)[3] = parr[i];//把parr数组的元素依次赋值给数组指针pafor (int j = 0; j < 3; ++j){//pa是第i个数组的地址//先解引用得到当前数组首元素地址//再利用j偏移解引用分别得到每个元素printf("%d ", (*pa)[j]);}printf("\n");}return 0;
}
3. 数组传参和指针传参
在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数该如何设计呢?
3.1 一维数组传参
void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
void test2(int *arr[20])//ok?
{}
void test2(int **arr)//ok?
{}
int main()
{int arr[10] = {0};int *arr2[20] = {0};test(arr);test2(arr2);return 0;
}
上面的传参形式都是正确的。
3.2 二维数组传参
void test(int arr[3][5])//ok?1
{}
void test(int arr[][])//ok?2
{}
void test(int arr[][5])//ok?3
{}
void test(int *arr)//ok?4
{}
void test(int* arr[5])//ok?5
{}
void test(int (*arr)[5])//ok?6
{}
void test(int **arr)//ok?7
{}
int main()
{int arr[3][5] = {0};test(arr);return 0;
}
2:error,原因:二维数组传参,函数形参的设计只能省略第一个[ ]的数字。
因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
这样才方便运算。
4:error,原因:二维数组的数组名表示的是首元素地址,是第一行的地址。
第一行可以看成是包含5个整形元素的数组的地址,既然是数组的地址放在一级指针肯定是错的,应该用数组指针。
5:error,原因:arr先和[ ]结合,是数组,每个元素是int*,因此错误。
7:error,原因:二级指针是存放一级指针的,而数组的地址没法存放到二级指针。
3.3 一级指针传参
如果函数的形参部分是一级指针,那么调用时实参可以写成什么?
void print(int *p)
{}
int main()
{> int a = 10;print(&a);> int* pa = &a;print(pa);> int arr[10];print(arr);return 0;
}
如果形参是一级指针,实参可以传整形的地址、整形指针和数组名(本质都是指针)。
3.4 二级指针传参
如果函数的形参部分是二级指针,那么调用时实参又可以写成什么?
void test(int** ptr)
{}
int main()
{int* p1;test(&p1);int* *p2 = &p1;test(p2);int* arr[10];test(arr)return 0;
}
形参是二级指针,实参可以:取地址一级指针、二级指针和指针数组。
4. 函数指针
数组指针是指向数组的指针,而函数指针就是指向函数的指针。
&数组名取出的是数组的地址:
int main()
{int arr[5] = { 0 };int(*pa)[5] = &arr;//数组指针return 0;
}
那么&函数名取出的是函数的地址吗?
int Add(int x, int y)
{return x + y;
}
int main()
{//取出函数的地址printf("%p\n", &Add);return 0;
}
因此函数也是有地址的。
对于函数来说:&函数名和函数名都是函数的地址。
因此和printf("%p\n", Add);
没有区别。
取出了函数的地址,要把它存起来,该怎么写?
其实是和数组指针非常类似:
(*pf)说明它是指针,(*pf)()这一对圆括号说明指向的是函数(函数调用操作符()),它指向的函数参数类型是int, int,返回类型是int,int (*pf)(int, int),指针pf存放的函数Add的地址。
int (*pf)(int, int) = &Add
int (*pf)(int x, int y) 参数名可以省略不写,只要参数类型即可。
既然有函数指针这个类型,那么就一定会有它的作用,接下来探究函数指针的作用:
把一个整形的地址取出来放在一个指针里,对指针解引用就可以找到并且修改它,对于函数指针来说,道理是一样的。
比如说利用指针来间接调用函数:
int Add(int x, int y)
{return x + y;
}
int main()
{//直接调用//int ret = Add(2, 3);//取地址放到指针pf里int (*pf)(int, int) = &Add;//这个&符可以省去//利用指针间接调用//对指针解引用,调用函数的同时传参int ret = (*pf)(2, 3);printf("%d\n", ret);return 0;
}
注:这种写法也是对的,
int ret = pf(2, 3);
,可以不写*号,完全等同于int ret = Add(2, 3);
这也就解释了为什么可以省略不写星号,上面带星号是为了更方便理解。
但是如果写上面那种形式就必须要带括号,否则就不是函数指针了。
通过上面代码的理解,也许会觉得函数指针有点多此一举,反正都一样,通过指针调用还把代码搞复杂了,其实函数指针在这个环境下是比较复杂。
但在其他环境里函数指针还是非常有作用的,之后会介绍它的其它妙用。
先来看看两段有趣的代码:
//代码1
( *( void (*)() )0 )();//代码2
void ( *signal(int , void(*)(int) ) )(int);
第一个:
第二个:
虽然解释了它的含义,但还不是不太方便理解,函数参数是函数指针,返回类型也是函数指针,有些绕。
这时可以使用typedef来把该函数声明进行优化:
typedef unsigned int u_int;
//其实就是把unsigned int重命名为u_int//把函数指针优化
void(*)(int)
//把void (*)(int)类型重命名为pf_t
下面改造该代码:
typedef void (*pf_t)(int);
int main()
{void (* signal(int, void (*)(int) ) )(int);pf_t signal(int, pf_t);return 0;
}
这里来介绍一下函数指针的作用。
写一个简易的计算器,这个计算机包含加法和减法(乘法除法原理于与之相同,明白它的含义就好,不再实现):
//选1加法
//选2减法
//选0退出
int Sub(int x, int y)
{return x - y;
}
int Add(int x, int y)
{return x + y;
}
void menu()
{puts("*******************");puts("***** 1. Add ****");puts("***** 2. Sub ****");puts("***** 0. exit ****");puts("*******************");
}
int main()
{int input = 0;do{menu();printf("请输入功能:");there:scanf("%d", &input);int x = 0;int y = 0;switch (input){case 1:printf("请输入两个操作数:");scanf("%d %d", &x, &y);printf("%d\n", Add(x, y));break;case 2:printf("请输入两个操作数:");scanf("%d %d", &x, &y);printf("%d\n", Sub(x, y));break;case 0:printf("Exit calcu!\n");break;default:printf("错误!请重新输入:\n");goto there;break;}} while (input);return 0;
}
测试结果:
虽然运行结果没问题,但是不难发现代码的实现有些冗余,如果加上了乘法除法的代码的重复部分会更多。
这时就体现出了函数指针的巧妙:
封装一个函数calc来负责加减计算,因为函数名就是函数地址,可以把实现加减的函数地址作为calc的参数,选择1传入Add函数的地址,选择2传入Sub的地址,再利用函数指针来找到并调用当前函数。
void menu()
{puts("********************");puts("***** 1. Add *****");puts("***** 2. Sub *****");puts("***** 0. exit *****");puts("********************");
}
int Sub(int x, int y)
{return x - y;
}
int Add(int x, int y)
{return x + y;
}
//计算
//形参要设置函数指针
void calc(int (*pf)(int, int))
{int x = 0;int y = 0;printf("请输入两个操作数:");scanf("%d %d", &x, &y);printf("%d\n", pf(x, y));
}
int main()
{int input = 0;do{menu();printf("请输入功能:");there:scanf("%d", &input);int x = 0;int y = 0;switch (input){case 1:calc(Add);break;case 2:calc(Sub);break;case 0:printf("Exit calcu!\n");break;default:printf("错误!请重新输入:\n");goto there;break;}} while (input);return 0;
}
这里就巧妙地使用了函数指针来实现相对于简洁的代码,如果没有函数指针的概念,固然是没办法写成这种形式。
这种形式也叫做回调函数,通过函数指针,在适当的时候回头调用它所指向的函数。
5. 函数指针数组
把函数指针放在数组中就是函数指针数组。
上面的两个函数Add和Sub,因为函数名就是函数的地址,把这两个函数放进数组里,数组的元素就是函数指针,而数组就叫做函数指针数组:
不妨来推导一下它的类型该怎么写,其实和函数指针比较类似:
int (*pf)(int, int) = Add;
- 这是函数指针。
实际上把pf换成数组的形式,就是函数指针数组:
int (*arr[2])(int, int) = {Add, Sub};
(元素个数可以省略)。
arr先和[ ]结合,是数组,把数组名arr和[ ]拿开,就是它的类型:函数指针,它的每个元素是函数指针。
int Sub(int x, int y)
{return x - y;
}
int Add(int x, int y)
{return x + y;
}
int main()
{//函数指针数组int (*arr[2])(int, int) = {Add, Sub};return 0
}
访问这个数组:
int Sub(int x, int y)
{return x - y;
}
int Add(int x, int y)
{return x + y;
}
int main()
{int (*arr[2])(int, int) = { Add,Sub };for (int i = 0; i < 2; ++i){int ret = arr[i](8, 4);printf("%d\n", ret);}return 0;
}
既然有函数指针数组这种形式,也就一定会有它的作用,利用这个数组,再把上面的计算器进行优化。
不仅仅想要实现加减乘除,还想要实现x & y、x | y、x ^ y…等等
如果要增加的多的功能,那么对应的case语句也必然也会增加,整体的代码就不容易阅读了,那可以用什么办法来简化代码?
这时利用函数指针数组,可以很大程度上优化代码,让其变得十分简洁。
代码的实现:首先需要创建一个函数指针数组 -int (*arr[])(int, int) = { 0,Add,Sub };
在最前面放个0,把两个函数的下标向后面推了一位,因此就可以根据input输入的值来直接对应其下标。
如果input = 0;直接退出计算器,再如果input>=1 && input <=2,用数组下标实现对应的·函数调用,否则重新输入,代码实现:
void menu()
{puts("********************");puts("***** 1. Add *****");puts("***** 2. Sub *****");puts("***** 0. exit *****");puts("********************");
}
int Sub(int x, int y)
{ return x - y;
}
int Add(int x, int y)
{return x + y;
}
int main()
{int (*pfArr[])(int, int) = { 0,Add,Sub }; int input = 0;int x = 0;int y = 0;do{menu();printf("请选择功能:");there:scanf("%d", &input);if (!input){puts("Exit calc!");}else if (input >= 1 && input <= 2){printf("请输入两个操作数:");scanf("%d %d", &x, &y);int ret = pfArr[input](x, y);printf("%d\n", ret);}else{puts("请重新输入!");goto there;}} while (input);return 0;
}
非常巧妙,利用函数指针数组大大简化了代码。
后面如果想增加一些功能,只需要实现要增加的函数功能,再数组里加上函数名,再把判断条件修改一下,调用起来非常方便且代码简洁。
6. 指向函数指针数组的指针
函数指针数组,也是数组,那么就会有它的地址,取出该数组的地址,就应该存放到指向【函数指针数组】的指针,那么该指针的形式该怎么写?
int (*pfArr[])(int, int) = { 0,Add,Sub };
这是函数指针数组,根据这个形式来改:
首先它是个指针(*ppfArr),指向的是一个数组(*ppfArr)[ ],数组的每个元素是函数指针( *(*ppfArr)[ ] )(),函数的参数是int, int,返回值是int
int ( *(*ppfArr)[] )(int, int) = &pfArr;
这便是指向函数指针数组的指针。
int main()
{//这是函数指针数组int (*pfArr[])(int, int) = { 0,Add,Sub };//指向函数指针数组的指针int (*(*ppfArr)[3])(int, int) = &pfArr;return 0;
}
既然是指针,它也可以存放在一个数组里去,也就是【指向函数指针数组的指针】的数组,这也是个数组,既然是数组也可…
7. 回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
首先介绍qsort函数的用法。
qsort是C语言的一个库函数,使用快速排序的思想实现的一个排序函数,它可以排序任意数据。
qsort参数:void qsort( void *base, size_t num, size_t width, int (*cmp)(const void *e1, const void *e2 ) );
四个参数:
void* base
- 需要排序的数据的起始位置
size_t num
- 待排序数据的元素个数
size_t width
- 待排序数据元素的大小(字节)
int (*cmp)(const void *e1, const void *e2 )
- 函数指针(比较函数),调用该指针指向的函数,参数e1和e2是要比较的两个元素的地址,因此该函数什么类型的数据都可以比较。
注意:第四个函数指针指向的函数其参数是
void*
的指针,而void*
的指针是无具体类型的指针,可以接收任意类型的地址,又因如此,所以void*
的指针是无法进行解引用操作,也不能±整数的操作(因为并不知道传的是什么类型的地址,之能设置为void*
类型),因此就需要强制类型转换为目标类型指针。
该比较函数的返回值:
利用qsort来实现数组排序:
int cmp_int(const void* e1, const void* e2)
{return (*(int*)e1 - *(int*)e2);
}
int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(int), cmp_int);for (int i = 0; i < sz; ++i){printf("%d ", arr[i]);}return 0;
}
qsort排序结构体成员:
struct Stu
{char name[20];int age;
};
int cmp_stu_by_name(const void* e1, const void* e2)
{//利用strcmp来比较字符串return strcmp( ((struct Stu*)e1)->name, ((struct Stu*)e2)->name );
}
int cmp_stu_by_age(const void* e1, const void* e2)
{return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
int main()
{struct Stu s[] = { {"zhangsan", 15}, {"lisi",20}, {"wangwu",25} };int sz = sizeof(s) / sizeof(s[0]);//通过名字排序qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);//通过年龄排序qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);return 0;
}
名字排序
年龄排序:
在了解了qsort函数的使用,接下来基于冒泡排序的思想改造qsort函数:
void Swap(char* p1, char* p2, int width)
{for (int i = 0; i < width; ++i){char tmp = *p1;*p1 = *p2;*p2 = tmp;++p1;++p2;}
}
int cmp_int(const void* e1, const void* e2)
{return *(int*)e1 - *(int*)e2;
}
void bubble_sort(void* base, int num, int width, int(*cmp)(const void* e1, const void* e2))
{for (int i = 0; i < num - 1; ++i){int f = 1;for (int j = 0; j < num - i - 1; ++j){if (cmp( (char*)base + j * width, (char*)base + (j + 1) * width ) > 0){Swap((char*)base + j * width, (char*)base + (j + 1) * width, width);f = 0;}}if (f == 1){break;}}
}
int main()
{int arr[] = { 9,8,7,6,5,4,3,2,1,0 };int sz = sizeof(arr) / sizeof(arr[0]);bubble_sort(arr, sz, sizeof(int), cmp_int);for (int i = 0; i < sz; ++i){printf("%d ", arr[i]);}return 0;
}
运行结果:
以上便是对指针深入的探究,指针类型有很多一不小心就容易搞混,所以在学习指针时要认真且大量的反复观看一熟练掌握指针。
深入探究指针及指针类型相关推荐
- [C++] 指向常量的指针 VS 指针类型的常量
指向常量的指针 VS 指针类型的常量 const 修饰指针时的位置不同,作用也不相同. 1. 指向常量的指针 不能通过指向常量的指针改变所指对象的值,但指针本身可以改变,可以指向另外的对象. 例: i ...
- 【C 语言】数组 ( 数组指针 | 数组指针定义 | 使用 数组类型* 定义数组指针 )
文章目录 总结 一.使用 数组类型* 定义数组指针 二.完整代码示例 总结 typedef int(ArrayType)[3];ArrayType *p = NULL; 一.使用 数组类型* 定义数组 ...
- C语言指针转换为intptr_t类型
C语言指针转换为intptr_t类型 1.前言 今天在看代码时,发现将之一个指针赋值给一个intptr_t类型的变量.由于之前没有见过intptr_t这样数据类型,凭感觉认为intptr_t是int类 ...
- Go_笔试题记录-指针与值类型实现接口的区别
1.如果Add函数的调用代码为: func main() {var a Integer = 1var b Integer = 2var i interface{} = &asum := i.( ...
- c++ 表达式必须包含指向类的指针类型_C++:18const关键字(附常量指针、指针常量、常量指针常量)...
一.const变量的一些基本特点 ①const修饰的变量不能被修改 const int a=10; a=20;//错误 ②因为const修饰的变量不能被修改,所以必须被初始化 int a=10; co ...
- C++之指针探究(六):二级指针和指针数组
前文:C++之指针探究(五):数组指针和二维数组 使用指向指针的指针来指向指针数组,至少有以下两个优势: ♠\spadesuit♠ 避免重复分配内存(下例中虽然我们进行了多个分类,但每本书名只占据 ...
- C++之指针探究(四):指针和二维数组
前文:C++之指针探究(三):指针数组和数组指针 一. 二维数组 二维数组通常也被称为矩阵(matrix). 六种初始化方式: (1) int a[3][4] = {1,2,3,4,5,6,7,8 ...
- C++之指针探究(三):指针数组和数组指针
前文:C++之指针探究(二):一级指针和一维数组 一. 指针数组 或: 指针数组的本质是数组,数组中每一个成员是一个指针.定义形式如下: char∗\ast∗ pArray[10]; 语法解析:p ...
- 对指针的详细认识(一)—— 指针概念+指针类型+野指针+指针运算+二级指针
文章目录 指针是什么? 指针的定义 指针的大小 指针类型 指针有哪些类型? 指针类型有什么意义? 野指针 野指针的成因 如何避免野指针 指针运算 指针+-整数 指针-指针 指针的关系运算 二级指针 指 ...
最新文章
- SharePoint 2010 新体验5 - Office Web Applications
- IBM 推出 Bluemix :Swift 将支持服务器端开发
- 主从mysql能过滤指定dml吗_MyCat教程二:mysql主从复制实现 - HG-93
- 0502团队项目 SCRUM团队成立
- Visual Studio Code设置断点时出现Unverified breakpoint该咋办
- vscode终端进程已终止 - 问题采集
- [DFS] [BFS] poj1979 poj3009 poj3669
- c语言内循环外循环怎么使用,开高速, 用内循环还是外循环? 教你正确使用内外循环!...
- poj 3468 A Simple Problem with Integers 基础线段树
- 不为人知的网络编程(八):从数据传输层深度解密HTTP
- vue 微信公众号 前端开发
- Android 滑动放大,Android多点触控实现对图片放大缩小平移,惯性滑动等功能
- 场景法设计测试用例ATM机取款问题
- KEIL MDK平台 S3C2440 编译链接、烧写调试
- 匈牙利算法解决指派问题清晰流程
- 常用计算机系统包括,常用的保护计算机系统的方法有()。
- 自然场景文字检测方案总结
- kindeditor自定义添加网络视频插件,修改批量图片上传方式flash为h5
- 网站域名被劫持、网站dns被劫持 域名跳转到别的网站的解决方法
- 怎么用计算机算拔模斜度,拔模斜度怎么标注【带斜度CAD图形的标注方法详细步骤】...