指针的进阶,进来看看你都知道多少?

指针进阶


本章重点

  1. 字符指针
  2. 数组指针
  3. 指针数组
  4. 数组传参和指针传参
  5. 函数指针
  6. 函数指针数组
  7. 指向函数指针数组的指针
  8. 回调函数

1.字符指针

在指针的类型中我们知道有一种指针类型为字符指针 char*;
一般用法:

#includeM<stdio.h>
int main()
{
	char ch='a';
	char * p=&ch;
	*p='w';
	printf("%c",ch);
	return 0;
}

还有一种使用方式如下:

int main()
{
    const char* pstr = "hello bit.";//这里是把一个字符串的首地址放在了指针变量中,*pstr是字符h不是整个字符串
    printf("%s\n", pstr);//或者printf(pstr);
    return 0;
}
#include <stdio.h>
int main()
{
    char str1[] = "hello bit.";
    char str2[] = "hello bit.";
    const char *str3 = "hello bit.";
    const char *str4 = "hello bit.";
    if(str1 == str2)
 		printf("str1 and str2 are same\n");
    else
 		printf("str1 and str2 are not same\n");   
    if(str3 == str4)
 		printf("str3 and str4 are same\n");
    else
 		printf("str3 and str4 are not same\n");  
    return 0;
}

输出的结果:
str1 and str2 are not same
str3 and str4 are same

分析首先,创建了两个数组,那么这儿两个数组的地址肯定不一样,而数组名是首元素的地址所以输出结果是str1 and str2 are not same,其次,又创建了两个字符指针但是这两个字符指针指向的是同一块地方,即都是指向常量字符串的首地址,顾打印出str3 and str4 are same。

2.指针数组

  • 指针数组是一个数组,是存放指针的数组。
  • 用一级指针模拟写一个二维数组。
#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 *arr[]={arr1,arr2,arr3};
	int i=0;
	for(i=0;i<3;i++)
	{
		int j=0;
		for(j=0;j<5;j++)
		{
			printf("%d",arr[i][j]);
		}
	    printf("\n");
	}
	return 0;

}

运行结果:
12345
23456
34567

常见的还有:
int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组

3.数组指针

数组指针是指针,是指向数组的指针

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p,*p==*&arr==arr
    return 0;
}

首先,数组指针是一个指向数组指针,那么存的一定是一个数组的地址所以&arr,数组名是首元素的地址,而&arr是整个数组的地址。
在这里插入图片描述
为什么,加1就不一样了?

对于数组名和首元素地址而言,其类型是int类型,+1跳过一个整形,即4个字节,而对于&arr而言,其类型是int [10],即40个字节,顾&arr是整个数组的地址。

3.1数组指针的应用:

void print2(int(*p)[4], int r, int c)//用二维数组的首元素地址,即第一行的地址,即一维数组的地址
{
	int i = 0;
	int j = 0;
	for (i = 0; i < r; i++)
	{
		for (j = 0; j < c; j++)
		{
			1.printf("%d", *(*(p + i) + j));//1与2等价
			2.printf("%d", p[i][j]);

		}
		printf("\n");
	}


}
void print1(int arr[][4], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d", arr[i][j]);

		}
		printf("\n");
	}

}
int main()
{
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	print1(arr, 3, 4);
	print2(arr,3,4);
	return 0;
}

学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:
int arr[5];整形数组
int * parr1[10];整形指针数组
int ( * parr2)[10];整形数组指针
int ( * parr3[10])[5];优先级:()>函数调用()和[ ]>*,因此,它肯定是一个数组,每个元素类型又是一个数组指针,因此是数组指针数组

在这里插入图片描述

3.2数组参数,指针参数

1.一维数组传参

#include<stdio.h>
void test(int arr[])//正确
{

}
void test(int arr[10])//正确
{

}
void test(int* p)//正确
{

}
void test2(int* arr2[])//正确
{

}
void test2(int* arr2[5])正确
{

}
void test2(int** p)//正确
{

}
int main()
{
	int arr[10] = { 0 };
	int* arr2[5] = { 0 };
	test(arr);
	test2(arr2);
	return 0;
}

2.二维数组传参

void test(int arr[3][5])//ok?正确
{}
void test(int arr[][])//ok?错误
{}
void test(int arr[][5])//ok?正确
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?错误
{}
void test(int* arr[5])//ok?错误
{}
void test(int (*arr)[5])//ok?正确
{}
void test(int **arr)//ok?错误
{}
int main()
{
 int arr[3][5] = {0};
 test(arr);
}

3.一级指针传参

#include <stdio.h>
void print(int *p, int sz)
{
 int i = 0;
 for(i=0; i<sz; i++)
 {
 printf("%d\n", *(p+i));
 }
}
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int *p = arr;
 int sz = sizeof(arr)/sizeof(arr[0]);
 //一级指针p,传给函数
 print(p, sz);//p==arr,顾用一级指针接收
 return 0;
}

思考:
当一个函数的参数部分为一级指针的时候,函数能接收什么参数?

数组名,一级指针变量,&某一变量

4.二级指针传参

#include <stdio.h>
void test(int** ptr)
{
 printf("num = %d\n", **ptr); 
}
int main()
{
 int n = 10;
 int*p = &n;
 int **pp = &p;
 test(pp);
 test(&p);
 return 0;
}

思考:
当函数的参数为二级指针的时候,可以接收什么参数?

&一级指针,二级指针变量,指针数组名

4.函数指针

函数指针,即指向函数的指针

#include <stdio.h>
void test()
{
 printf("hehe\n");
}
int main()
{
 printf("%p\n", test);
 printf("%p\n", &test);
 return 0;
}

输出结果:
00721253
00721253

得出结论:数组名与取地址数组名的地址是一样的。

那么函数的地址改怎么存呢?先写一个简单的函数。

int add(int x, int y)
{
	return x + y;
}
int main()
{
	int a = 2;
	int b = 3;
	int ret = add(a, b);
	printf("%d\n", ret);
	return 0;
}

对于整形指针:类型*,如int *
对于数组指针:类型*,如int()[ 5 ]
那么对于函数指针:类型
,如int(*)(int,int)

函数指针调用函数:

int add(int x, int y)
{
	return x + y;
}
int main()
{
	int (*p)(int, int) = &add;
	//由于&add==add,即int(*p)(int,int)=add;
	int ret = (*p)(2,3);//p是指向函数的指针,解引用找到这个函数
	int ret=p(2,3);//这种写法更为实用
	printf("%d\n", ret);
	return 0;
}

段有趣的代码:
//代码1
(* (void ( * )( ))0)();
//代码2
void (* signal(int , void( * )(int)))(int);

解读
代码1:调用地址为0的函数,先将0进行强制转换为函数指针,解引用,调用。
代码2:函数signal有两个参数,第一个参数是整形,第二个参数是函数指针,signa的返回类型是函数指针类型,也许这样写更容易理解void( * )(int)signal(int , void( * )(int))但是语法上就错了

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

5.函数指针数组

首先什么是函数值针数组呢?
我们知道整形指针数组是存放整形指针的数组,那么函数指针数组就是存放函数指针的数组,即存放函数地址的数组。

我们通过写一段代码,认识函数指针数组的写法与方便之处。

void menu()
{
	printf("**********************\n");
	printf("*****1.add 2.sub******\n");
	printf("*****3.mul 4.div******\n");
	printf("*****0.exit   ********\n");
	printf("**********************\n");
}
int add(int x, int y)//相似
{
	return x + y;
}
int sub(int x, int y)//相似
{
	return x - y;
}
int mul(int x, int y)//相似
{
	return x * y;
}
int div(int x, int y)//相似
{
	return x / y;
}
void test()
{
	int a = 0;
	int b = 0;
	int input = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入两个操作数\n");//相似
			scanf("%d %d", &a, &b);
			ret = add(a, b);
			printf("%d\n", ret);
			break;
		case 2:
			printf("请输入两个操作数\n");//相似
			scanf("%d %d", &a, &b);
			ret = sub(a, b);
			printf("%d\n", ret);
			break;
		case 3:
			printf("请输入两个操作数\n");//相似
			scanf("%d %d", &a, &b);
			ret = mul(a, b);
			printf("%d\n", ret);
			break;
		case 4:
			printf("请输入两个操作数\n");//相似
			scanf("%d %d", &a, &b);
			ret = div(a, b);
			printf("%d\n", ret);
			break;
		default:
			break;
		}
	} while (input);
}
int main()
{
	test();
	return 0;
}

是不是感觉switch语句太多了,而且我们发现这些函数的很相似,switch语句中的结构也很相似,那么用函数指针数组来改一改。

void menu()
{
	printf("**********************\n");
	printf("*****1.add 2.sub******\n");
	printf("*****3.mul 4.div******\n");
	printf("*****0.exit   ********\n");
	printf("**********************\n");
}
int add(int x, int y)
{
	return x + y;
}
int sub(int x, int y)
{
	return x - y;
}
int mul(int x, int y)
{
	return x * y;
}
int div(int x, int y)
{
	return x / y;
}
int (*arr[])(int, int) = { NULL,add,sub,mul,div };//函数指针数组,下标引用操作符里可省略。
void test()
{
	int a = 0;
	int b = 0;
	int input = 0;
	int ret = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		if (input == 0)
		{
			break;
		}
		else if (input >= 1 && input <= 4)
		{
			printf("请输入两个操作数\n");
			scanf("%d %d", &a, &b);
			ret=arr[input](a, b);//调用函数,创建数组时[]里不能用变量,但是访问时是可以用变量的
			printf("%d\n", ret);
		}
		else
		{
			printf("请重新输入\n");
		}

	} while (input);
}
int main()
{
	test();
	return 0;
}

现在的代码,比之前的更加方便,简略。

6.指向函数指针数组的指针

从该名字上看复杂,但本质它就是一个数组指针

int msin()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr;//数组指针,首先是指针(*p)类型照抄
	int (*arr2[5])(int, int);
	int(*(*pp))(int, int) = &arr2;//首先是指针,(*pp)类型照抄
	return 0;
}
void test(const char* str)
{
 printf("%s\n", str);
}
int main()
{
 //函数指针pfun
 void (*pfun)(const char*) = test;
 //函数指针的数组pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 //指向函数指针数组pfunArr的指针ppfunArr
 void (*(*ppfunArr)[5])(const char*) = &pfunArr;
 return 0;
}

7.回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

void menu()
{
	printf("**********************\n");
	printf("*****1.add 2.sub******\n");
	printf("*****3.mul 4.div******\n");
	printf("*****0.exit   ********\n");
	printf("**********************\n");
}
int add(int x, int y)//回调函数
{
	return x + y;
}
int sub(int x, int y)//回调函数
{
	return x - y;
}
int mul(int x, int y)//回调函数
{
	return x * y;
}
int div(int x, int y)//回调函数
{
	return x / y;
}
void calc(int (*p)(int,int))
{
	int a = 0;
	int b = 0;
	int ret = 0;
	printf("请输入两个操作数\n");
	scanf("%d %d", &a, &b);
	ret = p(a, b);
	printf("%d\n", ret);
}
void test()
{
	int input = 0;
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			break;
		case 1:
			calc(add);//传的是函数的地址,通过函数值针的的方式来调用函数
			break;
		case 2:
			calc(sub);
			break;
		case 3:
			calc(mul);
			break;
		case 4:
			calc(div);
			break;
		default :
			printf("请重新输入\n");
			break;
		}

	} while (input);
}
int main()
{
	test();
	return 0;
}

补充演示qsort函数的使用,即快排函数的使用
void qsort( void *base, size_t num, size_t width, int (*compare )(const void *e1, const void *e2 ) ); ——来自cplusplus.com

qsort函数使用时,要包含头文件#include<stdlib.h>,第一个参数是数组的首元素地址,第二个参数是数组的元素个数,第三个参数是数组每个元素占多少字节,第四个参数是函数地址。

之前我们学过冒泡排序(但是比较局限):
冒泡排序:

void bubble_sort(int arr[], int sz)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < sz - 1; i++)
	{
		for (j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
}
int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}

	return 0;
}

快速排序:

//排整形
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;//升序
	//return *(int*)e2 - *(int*)e1;//降序
}
int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz,sizeof(arr[0]),cmp_int);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}
//排浮点数
#include<stdlib.h>
int cmp_float(const void* e1, const void* e2)
{
	return (int)(* (float*)e1 - *(float*)e2);//注意返回的是整形,所以要强转
}
int main()
{
	float arr[ ] = {9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0,0.0};
	int sz = sizeof(arr) / sizeof(arr[0]);
	qsort(arr,sz,sizeof(arr[0]),cmp_float);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%.1f ", arr[i]);
	}
	return 0;
}
//排结构体年龄
#include<stdlib.h>
struct stu
{
	char name[20];
	int age;
};
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[3] = { {"张三",20},{"李四",55},{"王五",30} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", s[i].age);
	}

	return 0;
}
//排结构体名字
#include<stdlib.h>
#include<string.h>
struct stu
{
	char name[20];
	int age;
};
int  cmp_stu_by_name(const void* e1, const void* e2)
{
	return strcmp(((struct stu*)e1)->name, ((struct stu*)e2)->name);
}
int main()
{
	struct stu s[3] = { {"张三",20},{"李四",55},{"王五",30} };
	int sz = sizeof(s) / sizeof(s[0]);
	qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%s ", s[i].name);
	}

	return 0;
}

为什么要有四个参数? 首先比较不同类型的数据那么你不知道你要接收的是什么类型,还有知道首地址,所以第一个参数是void*首地址,你要比较多少是不知道的,所以需要第二个参数,你所比较的数据类型不知道占多少个字节,所以需要第三个参数,不同数据类型比较方法不同,所以需要分离出来,就需要第四个参数。

用冒泡排序的思想来写一个快排

int cmp_int(const void* e1, const void* e2)//不同数据比较方法不同
{
	return *(int*)e1 - *(int*)e2;
}
void swap(char* p1, char* p2, int n)//我们一次访问一个字节,一个一个字节进行交换,这样无论什么类型都能交换
{
	int i = 0;
	for (i = 0; i < n; i++)
	{
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2++;
	}
}
void bubble_sort(void* base, size_t num, size_t width, int (*cmp)(const void* e1, const void* e2))
{
	size_t i = 0;
	for (i = 0; i < num - 1; i++)
	{
		size_t j = 0;
		for (j = 0; j < num - 1 - i; j++)
		{
			if (cmp((char*)base+j*width,(char*)base+(j+1)*width) > 0)//回调函数的应用
			{
				swap((char*)base + j * width, (char*)base + (j + 1) * width,width);
			}
		}
	}
}
int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	bubble_sort(arr, sz, sizeof(arr[0]), cmp_int);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

8.指针和数组笔试题解析

必须记住的原则:

  1. sizeof(单独数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。
  4. sizeof 只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么,sizeof 是操作符。
  5. strlen是求字符串长度的,统计的是\0之前出现的字符个数,一定要找到\0才算结束,所以可能存在越界访问的,strlen是库函数。

1.一维数组

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));

答案:
16byte
4或8byte
4byte
4或8byte
4byte
4或8byte
16byte
4或8byte
4或8byte
4或8byte

2.字符数组

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

答案:
6byte
4或8byte
1byte
1byte
4或8byte
4或8byte
4或8byte
随机值
随机值
error
error
随机值
随机值
随机值

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

答案:
7byte
4或8byte
1byte
1byte
4或8byte
4或8byte
4或8byte
6
6
error
error
6
随机值
5

char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

答案:
4或8byte
4或8byte
1byte
1byte
4或8byte
4或8byte
4或8byte
6
5
error
error
随机值
随机值
5

3.二维数组

int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));

答案:
48byte
4byte
16byte
4或8byte
4byte
4或8byte
16byte
4或8byte
16byte
16byte
16byte

在这里插入图片描述
注意:sizeof()内部是不参与计算的,它只是计算类型的大小

#include<stdio.h>
int main()
{
	int a=0;
	1.sizeof(a);//值属性
	2.sizeof(int);//类型属性,也就是说sizeof测的是a的数据类型
	return 0;
}

9.指针笔试题

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}

2 5

在这里插入图片描述
2.

struct Test
{
 int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
 printf("%p\n", p + 0x1);
 printf("%p\n", (unsigned long)p + 0x1);
 printf("%p\n", (unsigned int*)p + 0x1);
 return 0;
}

0x100014
0x100001
0x100004

int main()
{
    int a[4] = { 1, 2, 3, 4 };
    int *ptr1 = (int *)(&a + 1);
    int *ptr2 = (int *)((int)a + 1);
    printf( "%x,%x", ptr1[-1], *ptr2);
    return 0;
}

4 0x2000000

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

#include <stdio.h>
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;
    p = a[0];
    printf( "%d", p[0]);
	return 0;
}

1
//首先注意逗号表达式,那么二维数组a中存的就应该是1,3,5,0,0,0
数组名a[0]是首元素1的地址,那么*(p+0)==p[0]==1。

int main()
{
    int a[5][5];
    int(*p)[4];
    p = a;
    printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    return 0;
}

0xfffffffc -4

在这里插入图片描述
小指针-大指针是负数,注意-4在内存中是以补码的形式存储的即11111111111111111111111111111100,对于%p来讲它就是地址即0xfffffffc

#include <stdio.h>
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
}

at

分析:
在这里插入图片描述
7.

int main()
{
 char *c[] = {"ENTER","NEW","POINT","FIRST"};
 char**cp[] = {c+3,c+2,c+1,c};
 char***cpp = cp;
 printf("%s\n", **++cpp);
 printf("%s\n", *--*++cpp+3);
 printf("%s\n", *cpp[-2]+3);
 printf("%s\n", cpp[-1][-1]+1);
 return 0;
}

POINT
ER
ST
EW

在这里插入图片描述
分析:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结:

以上就是指针进阶的所有内容了,如有关于这方面的问题不妨关注我,我们一起探讨,如果喜欢本篇,那就请求大佬们点个赞[卑微],谢谢观看!在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值