0一、指针的概念
二、指针和指针类型
1. 为什么指针有多种类型?
2. 指针+-整数的意义
3. 指针+-指针的意义
4. 得到了变量的地址有什么用呢?(指针解引用)
三、野指针
1. 野指针是什么?
2. 什么情况会造成野指针?
四、指针和数组
五、二级指针
六、字符指针
七、指针数组
1. 指针数组的定义
2. 指针数组的使用
八、数组指针
1. 如何定义数组指针?
九、函数指针
1. 函数指针的定义
2. &(函数名)
十、函数指针数组zhizh
1. 函数指针数组的定义
2. 函数指针数组的使用
十一、指向函数指针数组的指针
函数指针数组的指针的定义
十二、回调函数
1. 回调函数的定义
2. 回调函数的使用
指针的概念
- 指针是内存中一个最小单元的编号,也就是地址
- 指针通常指指针变量,一般存放内存地址的变量
我们可以通过取地址操作符(&)取出变量的内存与实地址,把地址可以存放到一个变量中,这个变量就是指针变量。#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> int main() { int a = 20; int* pa = &a;//将a的地址放入pa中 printf("%p", pa); return 0; }
在x86环境下打印出a存放在pa中的地址
指针的类型
有很多不同类型的变量,不同类型的变量应放在对应类型中!!
int main()
{
char a = 'a';
char* ch = &a;
double b = 20.22;
double* d = &b;
return 0;
}
指针±整数
int main()
{
int a = 0;
int* pa = &a;
printf("%p %p\n", pa, pa + 1);
double b = 0.0;
double* d = &b;
printf("%p %p\n", d, d + 1);
char c = 'a';
char* ch = &c;
printf("%p %p\n", ch, ch + 1);
return 0;
}
int*类型的指针 + 1 是跳过四个字节
double*类型的指针 + 1 是跳过八个字节
char*类型的指针 + 1 是跳过一个字节
指针±指针
指针-指针
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = &arr + 1;
printf("%d", pa - arr);
return 0;
}
这里数组取地址加1跳过整个数组,arr表示数组首元素地址等价于&arr[0]
运行结果为两个指针之间所隔的元素个数
指针 + 指针
是没有意义的!!!
得到了变量的地址有什么用呢?(指针解引用)指针的作用就是通过地址取访问指针指向的变量。
指针的类型决定了指针解引用能够访问的字节数。
野指针
野指针是指指向未知内存位置或者已经释放内存的指针。
未初始化的指针都会导致野指针
int main()
{
int* pa = NULL;
printf("%d", *pa);
return 0;
}
指针和数组
int main()
{
int arr[] = { 1,2,3};
int* pa = &arr[0];
printf("%p\n", arr);
printf("%p\n", pa);
return 0;
}
前面提到
arr表示数组首元素地址等价于&arr[0],所以arr于pa打印的值相等
例外:
sizeof(数组名)
,这里的数组名是数组的地址&(数组名)
,这里的数组名也是数组的地址
int main()
{
int arr[] = {1,2,3,4,5,6,7};
int* pa = &arr[0];
for (int i = 0; i < 7; i++)
{
printf("arr[%d]: %d <-> *(pa + %d): %d\n", i, arr[i], i, *(pa + i));
}
return 0;
}
从上图中可以得知:下标和指针(地址)±整数都能够遍历数组。
!!!arr[i]=*(pa+i)!!!
数组首元素地址可以代替数组名。
二级指针
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int* pa = &arr[0];
int** ppa = &pa;
printf("ppa : %p\n", ppa);
printf(" pa : %p\n", pa);
printf("*ppa: %p\n", *ppa);
return 0;
}
二级指针存储的是一级指针的地址,而指针解引用可以找到被指针指向的变量,那么这里二级指针解引用也可以找到一级指针。
字符指针
字符指针我们常见的使用方法是先创建一个字符变量,再创建一个字符指针变量,将字符变量的的地址赋给字符指针变量。
int main()
{
char c = 'v';
char* ch = &c;
printf("%p", ch);
return 0;
}
另一种使用方法则是创建一个字符指针变量,将字符串的首元素地址赋给他。、
int main()
{
char* ch = "sjcjsdhvj";
printf("%p", ch);
return 0;
}
指针数组
1.指针数组的定义
指针数组是一种存储指针的数组。
int main()
{
int arr1[] = {1,2,3,6,5,4,7,8,9,45,1};
int arr2[] = {2,3,6,5,4,8,8,4,5,4,5};
int arr3[] = {3,6,45,5,4,5};
int* arr[] = { arr1 , arr2 , arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
printf("%p\n", arr[i]);
}
return 0;
}
2. 指针数组的使用
指针数组是存储一级指针的数组,而一级指针指向的是数组
数组指针
1. 如何定义数组指针?
数组指针是用来存放数组地址的指针。
数组指针变量如何定义:(指针变量是 p ,数组存放的 int 类型的变量)
首先它是指针那么就不能与[ ]先结合----(*p)
其次它指向的内容是数组 ---- (*p)[ ] ----[ ]中为数组的元素个数
最后它指向数组存储变量的类型为什么 ---- int(*p)[]
int main()
{
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9 ,10};
int(*p)[10] = &arr;
printf("%p", p);
return 0;
}
测试
int main()
{
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9 ,10};
printf("%p\n", &arr); //取出数组的数组
printf("%p\n", arr); //取出数组首元素的地址
printf("%p\n", &arr[0]); //取出数组首元素的地址
printf("%p\n", &arr + 1);
printf("%p\n", arr + 1);
printf("%p\n", &arr[0] + 1);
return 0;
}
1)数组首元素地址 +1 :这里的地址在十六进制的状态下,相比之下地址的大小增加了 4 ,十进制下也是 4 ,数组储存变量是 int 内存大小也是 4 个字节 ,这里我们可以得到数组首元素地址 +1 ,也就是跳过了一个变量大小的字节数。
(2)数组地址 +1 :这里的地址在十六进制的状态下,相比之下地址的大小增加了 28 ,十进制下就是 40 ,数组储存变量是 int 内存大小也是 4 个字节并且数组元素个数是 10 个元素 ,这里我们可以得到数组首元素地址 +1 ,也就是跳过了一个数组大小的字节数。
结论:
- 数组首元素地址
+1
,跳过一个变量的大小。 - 数组地址
+1
, 跳过一个数组的大小
函数指针
1. 函数指针的定义
函数指针是一种指向函数的指针变量,它存储着函数的地址。函数指针的类型由函数的返回值类型和参数类型组成,可以用以下语法定义:
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*p)(int, int) = Add;
int a = 10, b = 30;
int c = 0;
c = (*p)(a, b);
printf("%d", c);
return 0;
}
!!!这里add=&add!!!
函数指针数组
1. 函数指针数组的定义
函数指针数组是一个数组用来存储函数指针的。
如何定义一个函数指针数组:假设数组存储的是 int (*)(int ,int)
首先是一个数组:那么就要先于[]结合 - pf[]
然后数组存储的是函数指针:int(*pf[])(int ,int)
2. 函数指针数组的使用
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;
}
cmp(int (*pf)(int, int))
{
int x = 0, y = 0;
printf("请输入两个操作数\n");
scanf("%d%d", &x, &y);
printf("%d\n", pf(x, y));
}
int main()
{
int (*pf[4])(int, int) = { add , sub , mul , div };
int input = 0;
while (1)
{
printf("*************************\n");
printf("**** 1:add 2:sub ****\n");
printf("**** 3:mul 4:div ****\n");
printf("*************************\n");
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
cmp(add);
break;
case 2:
cmp(sub);
break;
case 3:
cmp(mul);
break;
case 4:
cmp(div);
break;
default:
printf("输入错误,请重新选择\n");
}
}
return 0;
}
这也是转移表
指向函数指针数组的指针
如何定义一个函数指针数组的指针:假设数组存储的是 int (*)(int ,int)
首先是一个指针:那么就要先于*结合 ---- (*pf)
然后指针指向的是函数指针数组:(*pf)[]----方块中的是元素个数
最后数组存储的元素类型是:int(*(*pf)[])(int ,int) ----方块中的是元素个数
回调函数
这里使用回调函数实现 qsort()
函数的模拟(原理不同,这里使用冒泡排序的底层原理)
void bubble_sort(void* base, size_t sz, size_t width, int(*cmp(const void* p1, const void* p2)))
{
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);
}
}
}
}
int main()
{
int arr[10] = { 1,5,6,8,7,9,46,2,5,4 };
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz,sizeof(arr[0]),cmp);
for (int i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
int cmp(void const* p1, void const* p2)
{
return *(int*)p1 - *(int*)p2;
}
void print(int* arr, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(arr + i));
}
}
int main()
{
int arr[10] = { 2,5,6,3,2,7,8,9,4,5 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp);
print(arr, sz);
return 0;
}