一、字符指针
- 什么是指针呢?顾名思义指针是用来指向唯一的一块内存空间的(所以是用来存放地址的),在64位机器下是8个字节,在32位机器下是4个字节。
- 指针类型:指针类型决定了指针加减跳过几个字节,还有解引用时访问权限。
- 字符指针,当然顾名思义是存放字符地址的,例如
int main()
{
char ch='W';
char*p=&ch;//取出字符W的地址并将它放入p中
//还有另外一种
char*p="hello world!";
//这种使用是将首字符的地址存入的p中,不是将整个字符串的内容都放入p中
//这种字符串一般是放在只读数据区的,而且是不能更改的
const char*p="hello world!";
return 0;
}
二、指针数组
概念:这是一个数组数组的每一个元素都是指针类型,可以用来存字符串这种,当然并不是将整个字符串都存入数组中,而是将每一个字符串的首字母地址存入数组中。
int main()
{
char* arr[] = { "zhangsan","lisi","wangwu" };
printf("%s\n%s\n%s\n", arr[0], arr[1], arr[2]);
return 0;
}
三、数组指针
概念:将整个数组的地址取出,这样的地址用数组指针来存放。但是数组指针一般用于二维数组。
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9,0};
int (*ptr)[10]=&arr;//ptr首先和*先结合表示它是一个指针,
//指针向外一看,发现有数组,所以是指针数组,这个10必须写,还有
//由于[]的优先级高于*所以要用括号先让ptr和*先结合确保是指针
return 0;
}
一般用到的场景(二维数组)
void print(int(*arr)[5],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][5]={1,2,3,4,5,2,3,4,5,6,3,4,5,6,7};
print(arr,3,5);//我们打印这个二维数组
//arr是数组名,数组名表示表示首元素地址,
//而二维数组的首元素地址就是,第一行的地址。(所以用数组指针来接收)
return 0;
}
运行结果如下:
四、数组名和取地址数组名
五、数组传参
1.一维数组传参
2.二维数组传参
3.当参数部分是指针的情况,函数可以接收的参数有哪些?
六、函数指针
概念:顾名思义,就是指向函数的指针,用来存放函数名的地址的,形如int (*ptr)(int ,int),这个可以理解为,ptr首先和*结合所以它是指针,指针向外一看是(),所是函数,返回类型是int
一般用于回调函数,例如qsort库函数
回调函数:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递 给另一个函数 ,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。、
下面是模拟实现qosrt函数,里面将涉及回调函数。
int cmp_int(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
//由于是void*的指针,所以必须将其强制类型转换后才能进行操作
//强制类型转换目的是让编译器认为这是一个什么类型的数据并进行处理
//改变的是编译器读取的字节数
}
//交换
void swap(char* buf1, char* buf2, int width)
{
int i = 0;
while (width--)
{
char tmp = *buf1;
*buf1 = *buf2;//对指针操作需要解引用
*buf2 = tmp;
buf1++;
buf2++;
}
}
void my_qsort(void* base, int sz, int width, int(*cmp)(const void* e1, const void* e2))
{
//此处用的是冒泡排序的思想
int i = 0;
//趟数
for (i = 0; i < sz - 1; i++)
{
int j = 0;
for (j=0;j<sz-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);
}
}
}
}
void print(int arr[], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void test1()
{
int arr[] = { 2,1,4,3,5,6,8,7,9,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
my_qsort(arr, sz, sizeof(arr[0]), cmp_int);
//此处的cmp_int就是将函数作为参数进行传参
print(arr, sz);
}
int main()
{
test1();//以整形数据为例子
return 0;
}
结果如下:
七、两段有趣的代码
int main()
{
//代码1
(*(void(*)())0)();
//分析:
//首先是 void(*)(),这个是一个函数指针
//其次就是0前面有个括号,所以就是0被强制类型转换为void(*)()类型的函数指针
//然后被调用,这是一个无参的函数
void (*signal(int, void(*)(int)))(int);
//分析:
//signal函数的第一个参数是int类型
//第二个参数函数指针类型,参数是int,返回类型是void
//signal函数返回类型是void(*)(int),参数是int,返回类型是void
//所以这是一次函数声明
return 0;
}
八 、函数指针数组
概念:这是一个数组,数组的每个元素都是函数指针,形如int (*arr[10])(int ,int),这个可以这样来理解,首先arr和[]先结合所以这是一个数组,数组的每个元素是什么呢,将arr[10]拿掉, 剩下的 int(*)(int ,int) 就是元素的类型,即函数指针。
//函数指针数组
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)
{
if (y != 0)
return x / y;
else
return 0;
return 0;
}
int main()
{
//将函数地址放入函数指针数组中
int (*arr[4])(int, int) = { Add,Sub,Mul,Div };
//如需使用某个函数直接调用即可
int ret = arr[0](3, 2);
printf("%d\n", ret);//这将会在屏幕上打印5
return 0;
}
总结
1.注意,指针数组,数组指针的区别,
指针数组,是数组,数组的每个元素类型是指针
数组指针,是指针,指向的是整个数组的地址
2.关于数组的传参,一维数组传参可以用数组或者是指针接收,
二维指针传参,可以用二维数组接收但是列数一定不能省略
当然也可以用数组指针接收,因为二维数组的数组名就是二维数组地一样的地址。
3.数组名只有在取地址数组名和sizeof(数组名),时才表示整个数组,函数名和取地址函数名是一样的效果。
4.回调函数,函数指针调用的函数。如果你把函数的指针(地址)作为参数传递 给另一个函数 ,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
5.注意函数指针数组的书写,形如int (*arr[5])(int ,int ),需要注意arr的位置和需要不需要和星号先结合问题。