1.指针是一个变量,用来存放地址,一个地址标记唯一一块内存空间;
2.指针的大小是固定的,4/8个字节(32/64位机);
3.指针是有类型的,指针的类型决定了指针±整数的步长(跨几个字节),以及指针解引用时可访问的内存空间(几个字节);
字符指针
字符指针的两种用法:
1.指向某个字符变量;
2.指向某个字符串首字符的地址。
面试题:
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
char *str3 = "hello bit.";
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;
}
str3与str4指向同一个常量字符串,C/C++会把常量字符串存到单独一块内存空间,当几个指针指向同一个字符串时,他们实际会指向同一个内存空间,因此str3 == str4,但使用相同字符串去初始化不同的数组时,会开辟不同的内存空间。
指针数组
指针数组是存放一组指针的数组;
int* arr1[2];//一级指针数组
int** arr2[2];//二级指针数组
数组指针
数组指针是能够指向数组的指针
int (*p) [2];
p先和 * 结合,说明p是一个指针([]的优先级要比*高,所以要加括号),然后指针p指向一个具有两个元素的数组,数组元素类型为整形
int arr[10];
&arr与arr区别:
arr是数组名,数组名表示数组首元素的地址;
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}
说明数组名和&数组名指向的地址是一样的。
int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("arr + 1 = %p\n", arr+1);
printf("&arr = %p\n", &arr);
printf("&arr + 1 = %p\n", &arr+1);
return 0;
}
arr+1加了一个数组中元素所占的字节数
&arr+1加了一个数组所占的字节数(以上结果跳过了40)
实际上,&arr表示的是数组的地址,而不是数组首元素的地址。
数组指针的使用
数组指针中存放数组的地址
void print2(int (*p) [5], int x, int y)
{
int i = 0;
int j = 0;
for (i = 0; i<x; i++)
{
for (j = 0; j<y; j++)
{
printf("%d ", *(*(p+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 };
// print1(arr, 3, 5);
print2(arr, 3, 5);
return 0;
}
数组名arr是数组首元素的地址,arr是二维数组,所以arr表示二维数组第一行的地址,是一维数组的地址,可用数组指针来接收。
p+i 是二维数组第i行的地址,*(p+i)*表示第 i 行(一维数组), ( *(p+i)+j)表示第 i 行第 j 个元素的地址,再解引用得到该元素。
数组参数 指针参数
一维数组传参
void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int *arr)//ok?
{}
void test2(int *arr[20])//ok?
{}
void test2(int **arr)//ok?
{}
int main()
{
int arr[10] = {0};
int *arr2[20] = {0};
//test(arr);
test2(arr2);
}
以上(int* arr)表示指针arr指向一个元素为int类型的数组
(int* *arr)表示指针arr指向一个整型指针
二维数组传参
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);
}
(int* arr)无法确定arr是几行几列的二维数组
(int* arr[5])中arr是指针数组
(int(*arr)[5])中arr为数组指针,指向具有五个元素的一维数组
(int **arr)arr是二级指针
一级指针传参
#include <stdio.h>
void print_arr(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
printf("\n");
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
print_arr(p, sz);
return 0;
}
当函数参数部分为一级指针时,可以传递哪些参数?
void test(int* p)
p可以指向一个整型变量,也可以指向一个一维整形数组的首元素;
void test(char* p)
p可以指向一个字符型变量,也可以指向一个字符数组的首元素;
二级指针传参
void print(int** ptr)
{
printf("%d\n", **ptr);
}
int main()
{
int x = 10;
int* p = &x;
int** pp = &p;
print(pp);
print(&p);
return 0;
}
当函数参数为二级指针时,可以接收哪些参数?
void test(char** p)
{
}
int main()
{
char c = 'b';
char* pc = &c;
char** ppc = &pc;
char* arr[10];
test(&pc);
test(ppc);
test(arr);
return 0;
}
二级指针可以指向一级指针,也可以指向一维指针数组;
函数指针
看以下代码:
void test()
{
printf("test\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
输出的地址为test函数的地址;
函数指针可以保存函数地址,函数指针怎么写呢?
void (*pfun) ();
pfun先和 * 结合,则pfun是指针,指向一个函数,该函数没有参数,返回类型为void
看一下这两个
代码1:
(*(void(*)())0)();
void(*)()是一个函数指针类型,将0强制类型转换为这个函数指针类型,再对地址0处的这个函数指针解引用得到这个函数,后面加上括号表示调用这个函数。
代码2:
void (*signal(int, void(*)(int)))(int);
signal一个函数名,signal函数的第一个参数为int型,第二个参数为一个函数指针,现在就缺少函数的返回类型,void ( *
signal(int, void(*)(int)))(int),剩余部分为函数的返回类型,返回类型为函数指针类型,所以这是一个函数声明。
注意:C语言语法不支持以下写法:
void(*)(int) signal(int, void(*)(int));
函数名必须和 * 结合。
代码2难以理解。可以简化为:
typedef void(*pfun)(int);
//typedef void(*)(int) pfun;(语法不支持)
pfun signal(int, pfun);
利用typedef重命名 void (*) (int)函数指针类型为pfun;
则signal函数的函数返回类型和第二个参数都为pfun类型。
函数指针数组
把函数的地址存到一个数组中,那么这个数组就叫函数指针数组。
函数指针数组定义:
int (*parr[10])();
parr先和[]结合,说明parr是一个数组,int (*
parr[10])(),数组元素类型为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)
{
return x / y;
}
int main()
{
int x = 0;
int y = 0;
int input = 1;
int ret = 0;
int(*p[5])(int, int) = { 0, add, sub, mul, div }; //转移表
while (input)
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
if (input <= 4 && input >= 1)
{
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);
}
else
{
printf("输入有误\n");
}
printf("ret = %d\n", ret);
}
return 0;
}
指向函数指针数组的指针
一个指针指向一个数组,该数组的元素类型为指针数组;
如何定义:
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
void (*pfun)(const char*) = test;//函数指针pfun
void(*pfunarr[5])(const char*); //函数指针数组pfunarr
pfunarr[0] = test;
void(*(*p_pfunarr)[5])(const char*);//指向函数指针数组pfunarr的指针p_pfunarr
return 0;
}
p_pfunarr先和 * 结合说明p_pfunarr是一个指针,指向一个具有5个元素的函数指针数组
回调函数
回调函数就是通过函数指针调用的函数。如果把一个函数指针作为参数传递给另一个函数,通过该函数指针调用其指向的函数,就叫做回调函数。回调函数不是由函数实现方直接调用,而是在特定的条件或事件发生时由另一方调用的,用于对事件或条件作出回应。
库函数qsort的声明:
void qsort(void *base, size_t num, size_t width, int(__cdecl *compare)(const void *elem1, const void *elem2))
qsort的使用演示:
int arr_cmp_by_int(const void* p1, const void* p2)
{
return *(int*)p1 - *(int*)p2;
}
int main()
{
int arr[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), arr_cmp_by_int);
print_arr(arr, sz);
return 0;
}
arr_cmp_by_int作为函数指针,在qsort函数内部被回调
模拟实现bubble_sort
void Swap(char* buf1, char* buf2, int width)
{
int i = 0;
for (i = 0; i < width; i++)
{
char ret = *buf1;
*buf1 = *buf2;
*buf2 = ret;
buf1++;
buf2++;
//因为类型未知,所以逐个字节交换
}
}
void bubble_sort(void *base, int num, int width, int(*cmp) (const void *e1, const void *e2))
{
int i = 0;
int j = 0;
for (i = 0; i < num-1; i++)
{
for (j = 0; j < num - i - 1; j++)
{
//类型未知,已知width,先强制类型转换为char* ,+width跳到下一值
if (cmp((char*)base + width*j, (char*)base + width*(j + 1))>0)
{
Swap((char*)base + width*j, (char*)base + width*(j + 1), width);
}
}
}
}
末尾有指针相关练习:
https://github.com/QIYU1024/C-C-_Work/blob/master/test_11_8/test_11_8/test.c
qsort的使用,以及模拟实现bubble_sort:
https://github.com/QIYU1024/C-C-_Work/blob/master/test_11_5/test_11_5/test.c