首先,我们可以回顾一下指针的基础知识:
①指针是用来存放地址的变量,地址唯一标识一块内存空间。
②指针的大小取决于操作系统,在32位平台是4个字节,64位平台是8个字节。
③指针有不同的类型,指针可以进行运算。
接下来,就可以进行指针进阶内容的学习了
字符指针
指针类型中,有一种指针的类型是char *
可以这样使用:
#include <stdio.h>
int main()
{
char ch='a';
char *p=&ch;
*p='a';
return 0;
}
还有一种使用方法是这样的:
# include <studio.h>
int main()
{
char *p="abcdefg";
*p='w';
printf("%s\n",p);
return 0;
}
需要注意的是:这里是将字符串“abcdefg”的首字符的地址存放在了字符指针p中,而不是将整个字符放在p中。
那么我们的*p='w’这句代码就出现了问题,为了避免这个问题,我们可以将代码进行改进:
#include <studio.h>
int main()
{
const char*p="abcdefg";
return 0;
}
代表字符指针p指向的内容是不可更改的,从而减少错误代码的产生。
指针数组
指针数组是指存放指针的数组。
可以类比整型数组,存放整型的数组
#include <studio.h>
int main()
{
int *arr1[10]; /*一级整型指针的数组*/
int **arr2[10]; /*二级整型指针的数组*/
return 0;
}
由于操作符*的优先级小于[ ],所以arr先和[ ]结合,确定了arr是一个数组。
再和 *结合,确定数组元素的类型是整型指针。
数组指针
数组指针的定义
首先应该明确,数组指针是一种指针。
数组指针是能够指向数组的指针
可以类比整型指针,是指向整型数据的指针
通过两行代码来辨别数组指针与指针数组:
int *arr1[10];
int (*arr2)[10];
/*这样两句代码有什么区别?*/
在操作符的相关知识中,我们了解到:[ ]的优先级是要高于 *号的。
当加上括号()以后,改变了p的结合顺序
p先和 *结合,说明p是一个指针变量,这个变量指向了大小为10个整型的数组。
所以p是一个指针,指向了一个数组,叫做数组指针。
&数组名和数组名
int arr[10];
通过前面的学习,我们了解到:
arr是数组名,数组名表示首元素的地址。
那么&arr又代表了什么?
看一段代码:
#include <stdio.h>
int main()
{
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}
运行结果:
008FF904
008FF904
数组名和&数组名打印的地址相同
再来一段代码:
#include <stdio.h>
int main()
{
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
printf("%p\n", arr);
printf("%p\n", &arr);
printf("%p\n", arr + 1);
printf("%p\n", &arr + 1);
return 0;
}
运行结果:
012FF908
012FF908
012FF90C
012FF930
通过代码的运行结果,我们可以发现:
当&arr+1时,相较于&arr的差值是40
数组arr有10个元素,整型元素在内存中占4个字节。
看到&arr+1,跳过了整个数组的大小
所以,&arr表示的是数组的地址,而不是数组首元素的地址
从而,&arr 就与arr有了本质的区别
数组参数和指针参数
一维数组传参
以代码为例:
#include <stdio.h>
void test(int arr[])
{}
void test(int arr[10])
{}
void test(int *arr)
{}
void test2(int *arr2[20])
{}
void test2(int **arr2)
{}
int main()
{
int arr[10]={0};
int *arr2[20]={0};
test(arr);
test2(arr2);
return 0;
}
上面代码所提供的传参方法是否都可以呢?
void test(int arr[ ])数组传参,数组接收,这样的传参方法自然是正确的
void test(int arr[10])相比第一种传参方法,这里指定了数组的大小,自然也是正确的。因为整个传参过程并不会创建数组,只是需要传入参数的类型和参数名
void test(int *arr)由于传入的形参是数组名,它代表了首元素的地址,那么用指针来接收自然也没有问题。
void test2(int *arr[20])与第一种传参方法一样,传入指针数组,用指针数组接收,正确
void test2(int **arr2)数组名arr2的首元素类型是int *,是一个一级指针,最后用二级指针来接收,正确
二维数组传参
依然以代码为例:
#include <stdio.h>
void test(int arr[3][4])
{}
void test(int arr[][])
{}
void test(int arr[][4])
{}
void test(int *arr)
{}
void test(int* arr[4])
{}
void test(int(*arr)[4])
{}
void test(int **arr)
{}
int main()
{
int arr[3][4] = { 0 };
test(arr);
return 0;
}
上面代码所提供的传参方法是否都可以呢?
void test(int arr[3][4])数组传参,数组接收,这样的传参方法自然是正确的
void test(int arr[ ][ ])当二维数组的行列都不确定时,是不足以成为一个数组的,所以这种方法是错误的
void test(int arr[ ][4])给定了二维数组的列,这个数组就是可以确定的,正确
void test(int *arr)数组名代表首元素的地址,对于二维数组而言,数组名相当于第一行元素的地址,地址用指针接收,它的类型就应该是 int ( *)[4]。所以这种传参方法及下一行代码的传参方法就是错误的。
void test(int(*arr)[4])这样的二维数组传参方法自然就是正确的。
void test(int **arr)这里是一个二级指针,而只有传递一级指针的地址时才需要二级指针,所以它也是错误的。
一级指针传参
首先是一种最简单的情况:
#include <stdio.h>
void test(int* p) //用指针接收
{}
int main()
{
int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
int* p = arr;
test(p); //将一级指针p传入
return 0;
}
那么当一个函数的参数部分为一级指针的时候,函数又可以接收什么参数呢?
例如,当函数是这样时:
void test1(int *p)
可能传入的参数是:
int main()
{
int a=10;
int *p=&a;
int arr[10]={0};
test1(&a);
test1(p);
test1(arr);
return 0;
}
二级指针传参
来看一段代码:
#include <stdio.h>
void test(int** t)
{}
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
test(pp);
test(&p);
return 0;
}
可以发现,二级指针可以用二级指针来接收;一级指针的地址也可以用二级指针来接收
那么当函数的参数为二级指针的时候,可以接收什么参数呢?
#include <stdio.h>
void test(char** t)
{}
int main()
{
char ch = 'a';
char* p = &ch;
char* pp = &p;
char* arr[10];
test(&p);
test(pp);
test(arr);
return 0;
}
可见,当函数的参数为二级指针时,可以接收二级指针,一级指针的地址,指针数组
函数指针
类比数组指针的概念,函数指针就是指向函数的指针
有了函数指针,自然就代表函数是有地址的,那么函数的地址怎么表示呢?
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}
运行结果如下:
004E13B1
004E13B1
两句代码的运行结果是一样的,代表这样两种方法都是可以得到函数的地址的
那么函数指针又怎么表示呢?
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
int (*pf)(int,int)=&Add;
return 0;
}
int (*pf)(int,int)=&Add; pf就是一个函数指针,指向函数Add的指针
接下来,怎么使用函数指针呢?
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
int (*pf)(int,int)=&Add;
int sum=(*pf)(a,b);
int sum2=(pf)(a,b); //可见, *在这里是可以省略的
printf("%d\n",sum);
printf("%d\n",sum2);
return 0;
}
看运行结果:
30
30
需要注意的是,这里的举例只是为了学习函数指针的使用方法,并不代表实际的应用场景。在真正的编程过程中,还是要以方便,简洁,高效为主。
函数指针数组
首先什么是函数指针数组呢?
数组是存放相同类型数据的存储空间。
和指针数组的定义一样,把函数的地址存放到一个数组中,这个数组就是函数指针数组
如何定义函数指针数组呢?
int (*parr[10])();
parr首先和[ ]结合,确定了数组;
剩下来的int ( *) ( )就是数组的数据类型,int ( *) ( )类型的函数指针
指向函数指针数组的指针
如何理解指向函数指针数组的指针这样一个概念呢?
首先,指向函数指针数组的指针无疑是一个指针
这个指针指向了一个数组
这个数组的元素是函数指针
指向函数指针数组的指针的定义方法:
#include <stdio.h>
int Add(int a, int b)
{
return a + b;
}
int main()
{
int (*pa)(int, int) = Add; /*函数指针*/
int (* ppa[5])(int, int); /*函数指针数组*/
int (*(*pppa)[5]) (int, int) = &ppa; /*指向函数指针数组的指针*/
return 0;
}
回调函数
回调函数就是一个通过函数指针调用的函数;
当我们把函数的地址(指针)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们称其为回调函数。
回调函数不是由该函数的实现方法直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
以上就是C语言指针进阶的全部内容啦,
主要是指针进阶概念的理解和基本定义方法啦!