C语言——指针(进阶)

- 字符指针
- 数组指针
- 指针数组
- 数组传参和指针传参
- 函数指针
- 函数指针数组
- 指向函数指针数组的指针
- 回调函数
- 指针和数组题解析
一:字符指针
指针类型中字符指针 char*
#include
int main()
{
char* pa = "good bye";
//printf("%c\n",*pa);//本质是把字符串的首字符的地址存储在pa中
char* pa = "good bye";
char arr[] = "good bye";
printf("%s\n",pa);
printf("%s\n",arr);
return 0;
}
例题:
#include
int main()
{
char str1[] = "game";
char str2[] = "game";
//两个不同数组,新创造的空间不同
const char* str3 = "computer";
const char* str4 = "computer";
//常量字符串是不能改的,只会存一份,所以两个指针变量都指向同一个地址
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;
}
//str1 and str2 are not same
//str3 and str4 are same
二:数组指针(指向数组的指针)
1.数组指针
#include
int main()
{
int a = 10;
int* pa = &a;//整形指针
char b = 'q';
char* pb = &b;//字符指针
int arr[5] = {1,2,3,4,5};
int (*parr)[5] = &arr;//由于[]的优先级高于*,得带括号
}
从parr处开始,先与*结合,说明P 是一个指针 然后再与[]结合,说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以parr 是一个指向由整型数据组成的数组的指针
(怎么判断呢? 看变量先与谁结合)
2:&数组名和数组名
差分别为4 ,40。
&arr取的是整个数组的地址,而arr是数组首元素的地址
3:应用
//二维数组
#include
void print1(int arr[3][5], int m, int n)
{
int i = 0;
int j = 0;
for (i = 0; i < m; i++)
{
int j = 0;
for (j = 0; j < n; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
//数组指针
void print2(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 };
print1(arr, 3, 5);
print2(arr, 3, 5);
return 0;
}
//print1,2
//1 2 3 4 5
//2 3 4 5 6
//3 4 5 6 7
注意:
int(*parr[10])[5];
//首先parr[10]是个数组,去掉parr[10]后为指向的类型
//parr是以个存放数组指针的数组,能存放10个数组指针
//每个数组指针都能指向一个数组,数组有五个元素,均为int类型
三:指针数组(是一个存放指针的数组)
#include
int main()
{
//指针数组-数组中存放指针(地址)
int a = 10;
int b = 20;
int c = 30;
int d = 40;
int* arr[5] = {&a,&b,&c,&d};
int i = 0;
for (i=0;i<4;i++)
{
printf(" %d\n ", *(arr[i]));
}
}
#include
int main()
{
int a1[] = { 1,2,3,4,5 };
int a2[] = { 2,3,4,5,6 };
int a3[] = { 3,4,5,6,7 };
int* arr[3] = { a1,a2,a3 };//代表各数组首元素的地址
int i = 0;
for (i=0;i<3;i++)//行
{
int j = 0;
for (j=0;j<5;j++)//列
{
printf("%d ", *(arr[i] + j));
//可以写成 printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
//1 2 3 4 5
//2 3 4 5 6
//3 4 5 6 7
四:数组传参和指针传参
1:一维数组传参
#include
void test1(int arr[10])//可以
{}
void test1(int arr[])//可以,元素个数可以省略
{}
void test1(int* arr)//可以,指针
{}
void test2(int* arr[20])//可以,存放int* 的整型指针数组
{}
void test2(int** arr)//可以,二级指针
{}
int main()
{
int arr1[10] = {0};
int* arr2[10] = {0};//存放int*的数组
test1(arr1);
test2(arr2); //指向int* 的首元素地址
}
2:二维数组传参
实参 只用填数组名 就行
形参可以为 二维数组 或 指针
//二维数组
void test(int arr[3][5])//可以
{}
void test(int arr[][])//错误。行可省略,列是不能省的
{}
void test(int arr[][5])//可以
{}
//指针
void test(int* arr)//不行。接收第一行的地址,第一行是5个整形的数组,不能用指针接收
{}
void test(int* arr[5])//不行。这是指针数组,得要整型数组指针才行
{}
void test(int(*arr)[5])//可以。为整型数组指针
{}
void test(int** arr)//不行。
{}
int main()
{
int arr[3][5] = { 0 };
test(arr);
return 0;
}
3:一级指针传参
#include
void print(int* pstr, int sz) //用一级指针接收
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(pstr + i));
}
}
void test(char* p)
{
}
int main()
{
int arr[7] = { 2,0,2,2,10,0,2 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
print(p, sz);//p是一级指针
char ch = 'a';
test(&ch);//char*类
//char* p1 = &ch;
//test(p1);
return 0;
}
4:二级指针传参
#include
void test(int** pstr) //二级指针接收
{
printf("num = %d\n", **pstr);//a的值
}
int main()
{
int a = 10;
int* pa = &a;//pa是一级指针
int** ppa = &pa;//ppa是二级指针
test(ppa);//二级指针传参
test(&pa);取一级指针的地址就是二级指针
int* arr[10] = {0};
test(arr);//也可以,传存放一级指针的数组
//,传的是整形指针数组的首元素的地址,而首元素为int*类型,结果为二级指针传参
return 0;
}
五:函数指针(存放函数地址的指针)
&函数名——取出函数的地址
注意:数组名 != &数组名
函数名 == &函数名
int (*pf)(int,int) = &Add;
#include
int Add(int x,int y)
{
return x + y;
}
int main()
{
printf("%p\n", Add);
printf("%p\n", &Add);
//地址相同,意义一样,无区别
int (*pf)(int,int) = &Add;// 函数指针中,由于()优先级高于*,得带括号
int ret = (*pf)(7,5);//通过函数指针调用函数.1
//int ret = pf(7,5);//2
//int ret = Add(7,5);//3
//1,2,3都一样。*的作用不大,可有可无,但只能在函数指针中这样
//int ret = *pf(7,5);//这样是不行的,等于对函数的结果12进行解引用。若加*得括起来
printf("%d\n",ret);//12
return 0;
}
例子:
#include
void test(char* str)
{
}
int main()
{
void (*pt)(char*) = &test;//与函数要对应
return 0;
}
六:函数指针数组(存放函数指针的数组)
它可以存放多个参数相同,返回类型相同 的函数地址
int (*arr[3])(int, int) = { Add,Sub,Mul};
#include
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 main()
{
//parr就是 函数指针数组
int (*parr[3])(int, int) = { Add,Sub,Mul};
return 0;
}
写一个计算器(加,减,乘,除):
(函数指针数组的用途:转移表)
#include
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;
}
void menu()
{
printf("********************************\n");
printf("**** 1.Add 2.Sub ****\n");
printf("**** 3.Mul 4.Div ****\n");
printf("**** 0.exit ********\n");
printf("********************************\n");
}
int main()
{
int input = 0;
do{
menu();//菜单
//pfArr是函数指针数组
//转移表
int (*pfArr[5])(int, int) = { NULL, Add, Sub, Mul, Div };//
int x = 0;
int y = 0;
int ret = 0;
printf("请选择:>");
scanf("%d",&input);
if(input>=1 && input<=4)
{
printf("请输入2个数:>");
scanf("%d %d", &x, &y);
ret = (*pfArr[input])(x, y);
printf("%d\n",ret);
}
else if(input == 0)
{
printf("退出程序\n");
}
else
{
printf("选错了,重新选择");
}
}while(input);
return 0;
}
,
七:指向函数指针数组的指针
int(*p)(int,int);//函数指针
int(* p2[4])(int,int);//函数指针的数组
int(*(*p3)[4])(int,int);//取出函数指针数组的地址
//p3是一个指向函数指针数组的指针
补充:int arr[5];
数组元素类型:去掉数组名和元素个数 , 剩下的就是——>int
数组类型 :仅去掉数组名 ——>int[5]
八:回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针作为参数传给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
回调函数应用例子:
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;
}
void menu()
{
printf("***************************\n");
printf("***** 1.add 2. sub ****\n");
printf("***** 3.mul 4. div ****\n");
printf("***** 0.exit ****\n");
printf("***************************\n");
}
//回调函数
int Cald(int (*pf)(int, int))
{
int x = 0;
int y = 0;
printf("请输入2个值:>");
scanf("%d %d", &a, &b);
return pf(x,y);
}
int main()
{
int input = 0;
int x = 0;
int y = 0;
int ret = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
ret = Cald(Add);//把Add函数指针传过去
//Add函数并不是直接调用的,而是通过函数指针调用的
printf("%d\n", ret);
break;
case 2:
ret = Cald(Sub);//把Sub函数指针传过去
printf("%d\n", ret);
break;
case 3:
ret = Cald(Mul);//把Mul函数指针传过去
printf("%d\n", ret);
break;
case 4:
ret = Cald(Div);//把Div函数指针传过去
printf("%d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选错了,重新选择\n");
break;
}
} while (input);
}
qsort 库函数(进行快速排序的实现)
(得引用头文件 stdlib.h )
void qsort (void* base,//void*能接收任意类型的地址,应用更加广泛;base中存放的是 待排序的数据里的第一个对象的地址
size_t num,//元素个数
size_t size,//一个元素占几个字节
int (*compar)(const void*,const void*)//用来比较待排序数据中的2个元素的函数
);
//
注意:int compareMyType (const void * a, const void * b)
{
if ( *(MyType*)a < *(MyType*)b ) return -1;
if ( *(MyType*)a == *(MyType*)b ) return 0;
if ( *(MyType*)a > *(MyType*)b ) return 1;
}
例子:用qsort函数进行排序
#include
#include
int compar(const void* d1, const void* d2)
{
return *(int*)d1 - *(int*)d2 ;
}
void print_arr(int arr[],int sz)
{
int i = 0;
for(i=0;i<sz;i++)
{
printf("%d\n",arr[i]);
}
printf("\n");
}
int main()
{
int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
int sz = sizeof(arr)/sizeof(arr[0]);
qsort(arr,sz, sizeof(arr[0]), compar);//排序
print_arr(arr, sz);
return 0;
}
九.指针和数组题解析
//在32位平台上
#include
int main()
{
//32位平台上,指针的大小都是4个字节
//一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//16 计算的是数组的大小
printf("%d\n", sizeof(a + 0));//4 数组首元素地址的大小
printf("%d\n", sizeof(*a));//4 首元素的大小
printf("%d\n", sizeof(a + 1));//4 第二个元素地址的大小
printf("%d\n", sizeof(a[1]));//4 第二个元素的大小
printf("%d\n", sizeof(&a));//4 数组的地址
printf("%d\n", sizeof(*&a));//16 数组的大小
printf("%d\n", sizeof(&a + 1));//4 数组后面空间地址的大小
printf("%d\n", sizeof(&a[0]));//4 第一个元素的地址
printf("%d\n", sizeof(&a[0] + 1));//4 第二个元素的地址
//字符数组
1.
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//6 数组的大小
printf("%d\n", sizeof(arr + 0));//4 首字符地址的大小
printf("%d\n", sizeof(*arr));//1 数组首元素地址的解引用,即a字符的大小
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4 取出字符数组的地址的大小
printf("%d\n", sizeof(&arr + 1));//4 字符数组后面的地址的大小
printf("%d\n", sizeof(&arr[0] + 1));//4 b字符的地址
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));//随机值
printf("%d\n", strlen(&arr[0] + 1));//随机值
2.
char arr[] = "abcdef";//a b c d e f \0
printf("%d\n", sizeof(arr));//7 数组大小
printf("%d\n", sizeof(arr + 0));//4 首元素地址的大小
printf("%d\n", sizeof(*arr));//1 首元素的大小
printf("%d\n", sizeof(arr[1]));//1 第二个元素的大小
printf("%d\n", sizeof(&arr));//4 取出整个数组的地址
printf("%d\n", sizeof(&arr + 1));//4 数组后面的地址
printf("%d\n", sizeof(&arr[0] + 1));//4 第二个元素的地址
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 传的是字符的ASCLL值,会报错
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr + 1));//随机值
printf("%d\n", strlen(&arr[0] + 1));//5
char* p = "abcdef";// a b c d e f \0
//把字符串abcdef的第一个字符的地址存放在p变量中
printf("%d\n", sizeof(p));//4
printf("%d\n", sizeof(p + 1));//4
printf("%d\n", sizeof(*p));//6
printf("%d\n", sizeof(p[0]));//4
printf("%d\n", sizeof(&p));//4 取p的地址,即二级指针,也是4
printf("%d\n", sizeof(&p + 1));//4
printf("%d\n", sizeof(&p[0] + 1));//4
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p + 1));//5
printf("%d\n", strlen(*p));//err a的ASCLL值97,传入会进行访问冲突,进行报错
printf("%d\n", strlen(p[0]));//err
printf("%d\n", strlen(&p));//随机值
printf("%d\n", strlen(&p + 1));//随机值
printf("%d\n", strlen(&p[0] + 1));//5
//二维数组
int arr[3][4] = { 0 };
printf("%d\n",sizeof(arr));//计算整个二维数组的大小,48 = 12*4
printf("%d\n",sizeof(arr[0][0]));//4
printf("%d\n",sizeof(arr[0]));//16=4*4 .arr[0]表示整个第一行,可以理解为第一行的数组名,
sizeof(arr[0])计算的是第一行的大小
printf("%d\n",sizeof(arr[0]+1));//arr[0]作为数组名并没有单独在sizeof()内部,也没取地址,
//所以在这个例子arr[0]为第一行首元素的地址
//所以arr[0]+1 为第一行第二个元素的地址,大小为 4
printf("%d\n",sizeof(arr + 1));//sizeof()内部有运算,arr是二维数组的首地址,表示第一行的地址
//arr + 1 就是第二行的地址,是地址大小就是4个字节
printf("%d\n",sizeof(*(arr + 1)));//第二行元素的解引用,16
printf("%d\n",sizeof(&arr[0]+1));//取第一行的地址再+1,就是第二行的地址。大小为4
printf("%d\n",sizeof(*arr));//第一行的地址的解引用。大小为 16
printf("%d\n",sizeof(arr[3]));//虽然我们没有arr[3],不过是进行类型的大小计算,不会真实访问它
//所以即使不存在,并不会进行越界访问 ,也能通过类型计算大小,大小为16
}