C----指针进阶

Posted 4nc414g0n

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C----指针进阶相关的知识,希望对你有一定的参考价值。

一维数组传参,二维数组,二级指针传参

一维

void test(int *arr[20]);//指针数组
void test(int **arr);//二级指针
test(arr);

二维

void test(int(*arr)[5])
test(arr);

二级指针

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;
}



指针数组

int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
//数组名 != &数组名

数组指针

int (*p2)[10];
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合

函数指针

int *pf(int,int)=&add;
int ret=(*pf)(3,5);
int ret=(pf)(3,5);//只有函数的‘*’可以省略
//函数名 == &函数名

函数指针数组

int *arr[10];
//数组的每个元素是int*
int (*parr1[10]])();

函数指针数组的用途:转移表

转移表实例:计算器


指向函数指针数组的指针

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)[10])(const char*) = &pfunArr;
	return 0;
}


回调函数

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

回调函数实现qsort 详见:C语言–回调函数实列 模拟qsort函数


指针注意点

  • C/C++会把常量字符串存储到单独的一个内存区域, 当几个指针,指向同一个字符串的时候,他们实际会指向同一块内存。 但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块
//如:
char str1[] = "hello";
char str2[] = "hello";
char *str3 = "hello";
char *str4 = "hello";
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");
//打印str1 and str2 are not same
//	  str3 and str4 are same
---------------------------------------------------------------------------
字符串怎么存储:
 - char* a = "abc" 开辟一块内存 存放字符串 abc\\0 并把这个字符串的首地址存放到 a 指针中,而且它开辟的内存是只读(跟常量差不多)的所以 a[1]='k' 是不对的,
因为这个字符串是只读的不可修改
 - char a[]="abc" 是开辟一块内存存放 abc\\0 ,这块内存首地址就是&a。
并且它可以修改。注意这快内存首地址是&a
  • 1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
    2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
    3. 除此之外所有的数组名都表示首元素的地址。
    4. 对于二维数组在sizof题中注释详细说明
int a[]={1,2,3,4,5};
int b[]={1,2,3,4,5};
int c[]={1,2,3,4,5};
int *arr[3]={a,b,c};
for(int j=0;j<3;j++)
{
	printf("%d",arr[i][j]);//等价于arr[i]+j,“相当”于二维数组	
	//也等价于*(*(arr+i)+j)
}



一维数组&字符数组&二维数组-sizeof&strlen

一维数组

int a[] = { 1,2,3,4 };//4*4=16
printf("%d\\n", sizeof(a));//16
printf("%d\\n", sizeof(a + 0));//4/8 a + 0 是第一个元素的地址,sizeof(a + 0)计算的是地址的大小
printf("%d\\n", sizeof(*a));//4  *a是数组的第一个元素,sizeof(*a)计算的是第一个元素的大小
printf("%d\\n", sizeof(a + 1));//4/8 a + 1是第二个元素的地址,sizeof(a+1)计算的地址的大小
printf("%d\\n", sizeof(a[1]));//4 - 计算的是第二个元素的大小
printf("%d\\n", sizeof(&a)); //4/8 - &a虽然数组的地址,但是也是地址,sizeof(&a)计算的是一个地址的大小
printf("%d\\n", sizeof(* &a));//16 - 计算的数组的大小
//&a -- int(*p)[4] = &a;
printf("%d\\n", sizeof(&a + 1));//4/8 - &a + 1 是数组后面的空间的地址
printf("%d\\n", sizeof(&a[0]));//4/8
printf("%d\\n", sizeof(&a[0] + 1));//4/8

字符数组

char arr[] = { 'a','b','c','d','e','f' };
printf("%d\\n", strlen(arr));//随机值
printf("%d\\n", strlen(arr + 0));//随机值
//printf("%d\\n", strlen(*arr));//err
//printf("%d\\n", strlen(arr[1]));//err
printf("%d\\n", strlen(&arr));//随机值
printf("%d\\n", strlen(&arr + 1));//随机值-6
printf("%d\\n", strlen(&arr[0] + 1));//随机值-1
printf("%d\\n", sizeof(arr));//6
printf("%d\\n", sizeof(arr + 0));//4/8
printf("%d\\n", sizeof(*arr));//1
printf("%d\\n", sizeof(arr[1]));//1
printf("%d\\n", sizeof(&arr));//4/8
printf("%d\\n", sizeof(&arr + 1));//4/8
printf("%d\\n", sizeof(&arr[0] + 1));//4/8
----------------------------------------------------------------
char arr[] = "abcdef";
//[a b c d e f \\0]
printf("%d\\n", strlen(arr));//6
printf("%d\\n", strlen(arr + 0));//6
//printf("%d\\n", strlen(*arr));//err
//printf("%d\\n", strlen(arr[1]));//err
printf("%d\\n", strlen(&arr));//6
printf("%d\\n", strlen(&arr + 1));//随机值
printf("%d\\n", strlen(&arr[0] + 1));//5
//[a b c d e f \\0] 
printf("%d\\n", sizeof(arr));//7
printf("%d\\n", sizeof(arr + 0));//4/8
printf("%d\\n", sizeof(*arr));//1
printf("%d\\n", sizeof(arr[1]));//1
printf("%d\\n", sizeof(&arr));//4/8  char(*)[7]
printf("%d\\n", sizeof(&arr + 1));//4/8 
printf("%d\\n", sizeof(&arr[0] + 1));//4/8
char *p = "abcdef";//p指针指向a
printf("%d\\n", sizeof(p));//p 4/8
printf("%d\\n", sizeof(p+1));//b地址 4/8
printf("%d\\n", sizeof(*p));//a 1
printf("%d\\n", sizeof(p[0]));//a 1
printf("%d\\n", sizeof(&p));//指向p指针的首的地址 4/8
printf("%d\\n", sizeof(&p+1));//指向p指针的末端的地址 4/8
printf("%d\\n", sizeof(&p[0]+1));//b地址 4/8
//strlen接收的是地址
printf("%d\\n", strlen(p));//6
printf("%d\\n", strlen(p+1));//5
printf("%d\\n", strlen(*p));//传入'a'的ASCII码97 error
printf("%d\\n", strlen(p[0]));//传入'a'的ASCII码97 error
printf("%d\\n", strlen(&p));//从p首往后 随机值1
printf("%d\\n", strlen(&p+1));//从p尾往后 随机值2 
//随机值2与随机值1无关,原因:p内可能有'\\0'
printf("%d\\n", strlen(&p[0]+1));//5

二维数组

int a[3][4] = {0};
printf("%d\\n", sizeof(a));//48 = 3*4*sizeof(int)
printf("%d\\n", sizeof(a[0][0]));//4 - a[0][0] - 是第一行第一个元素
printf("%d\\n", sizeof(a[0]));//16
printf("%d\\n", sizeof(a[0] + 1));
//4 解释:a[0]作为数组名并没有单独放在sizeof内部,
//也没取地址,所以a[0]就是第一行第一个算的地址
//a[0]+1,就是第一行第二个元素的地址
printf("%d\\n", sizeof(*(a[0] + 1)));//4 - 解释:*(a[0] + 1)是第一行第二个元素
printf("%d\\n", sizeof(a + 1));//4 - 解释:a是二维数组的数组名,并没有取地址
//也没有单独放在sizeof内部,所以a就表示二维数组首元素的地址,即:第一行的地址
//a + 1就是二维数组第二行的地址
printf("%d\\n", sizeof(*(a + 1)));//16 解释:a+1是第二行的地址,所以*(a+1)表示第二行
//所以计算的就是第2行的大小
printf("%d\\n", sizeof(&a[0] + 1));//4 解释:a[0]是第一行的数组名,
//&a[0]取出的就是第一行的地址,&a[0]+1 就是第二行的地址
printf("%d\\n", sizeof(*(&a[0] + 1)));//&a[0]+1 就是第二行的地址
//*(&a[0]+1) 就是第二行,所以计算的第二行的地址
printf("%d\\n", sizeof(*a));//16 解释:a作为二维数组的数组名,没有&,没有单独放在sizeof内部
//a就是首元素的地址,即第一行的地址,所以*a就是第一行,计算的是第一行的大小
printf("%d\\n", sizeof(a[3]));//16 解释:a[3]其实是第四行的数组名(如果有的话)
//所以其实不存在,也能通过类型计算大小的



两段有趣的代码–出自《C陷阱和缺陷》

代码1

(*(void (*)())0)();//调用0地址处的函数,该函数无参,返回类型为void
//从0开始分析:
//()0即将0强制类型转换为void (*)()函数指针类型,0当作这个函数指针类型的函数地址
//再解引用操作调用函数(*(void (*)())0)(),
//由于函数为void所以最后一个括号什么都没有

代码2

void (*signal(int , void(*)(int)))(int);
//1.signal和()先结合说明是函数名
//2. signal第一个参数是int,第二个参数是函数指针,
//该函数指针,指向一个参数为int返回类型为void的函数
//3. 将“signal(int , void(*)(int))”提出,剩下void(*)(int),是他的返回值
//所以signal函数的返回类型也是一个函数指针,
//该函数指针,指向一个参数为int返回类型为void的函数,signal是一个函数的声明

为方便理解我们可以先这样写:
void (*)(int) signal(int , void(*)(int));
但是语法不允许,(*)必须和名字signal放在一起,所以(int)只能放在后面

//但是以上代码可简化为:
注意:不能写为typedef void(*)(int) pfun_t;上面已说明
//应写为:
typedef void(*pfun_t)(int);//对void (*)(int)的函数指针类型重命名为pfun_t
pfun_t signal(int, pfun_t);//相当于上面的错误代码表示方式,更易理解,且语法支持



更多

深入理解c指针:C语言-八道题深入理解c指针

以上是关于C----指针进阶的主要内容,如果未能解决你的问题,请参考以下文章

C语言进阶笔记深入了解进阶指针

C语言进阶笔记深入了解进阶指针

C语言进阶笔记深入了解进阶指针

C语言进阶学习笔记二指针的进阶(重点必看+代码演示+练习)

C语言进阶学习笔记二指针的进阶(重点必看+代码图解+练习)

C语言指针进阶第五站,函数指针