序言:
前面概要:
1. 指针就是个变量,用来存放地址,地址唯一标识一块内存空间。2. 指针的大小是固定的4/8个字节(32位平台/64位平台)。3. 指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。4. 指针的运算
1.字符指针
一般情况有:
int main()
{char ch = 'a';char *pc = &ch;*pc = 'a';return 0;
}
还有这种:
#include<stdio.h>
int main()
{const char* pc = "abcdef";printf("%c\n", *pc);return 0;
}
这里并不是把abcdef这个字符串放到字符指针pc里,而是将字符串的首地址放到字符指针pc里
#include <stdio.h>
int main()
{const char str1[] = "abcdef";const char str2[] = "abcdef";const char* str3 = "abcdef";const char* str4 = "abcdef";if (str1 == str2)printf("str1 == str2\n");elseprintf("str1 != str2\n");if (str3 == str4)printf("str3 == str4\n");elseprintf("str3 != str4\n");return 0;
}
str1[]创建了一个数组,存放了 a b c d e f \0
str2[]创建了一个数组,存放了 a b c d e f \0
创建了2个数组,所以它们的首地址的地址是不一样的。
这里str1和str2指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当 几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。
const char* str3 = "abcdef";
是一个常量字符串,是无法改变的,这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域当几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。所以结果应该是:
所以str1和str2不同,str3和str4不同。
2.指针数组
指针数组是一个存放指针的数组。int* arr1[10]; //存放整形指针的数组char *arr2[4]; //一级字符指针的数组char **arr3[5];//二级字符指针的数组
#include<stdio.h> int main() {int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 3,4,5,6,7 };int arr3[5] = { 5,6,7,8,9 };int* pc[3] = { arr1,arr2,arr3 }; //将arr1,arr2,arr3的首地址存放在指针数组里int i = 0;for (i = 0; i < 3; i++) {int j = 0;for (j = 0; j < 5; j++){printf("%d", *(pc[i] + j));//通过pc[i] + j 可以访问到某个数组里的某个数据,这就成了二维数组//printf("%d", pc[i][j]);这两种打印结果是等价的}printf("\n");}return 0; }
3.数组指针
1.定义
数组指针是指针?还是数组?答案是:指针。那数组指针应该是:能够指向数组的指针。
例如:下面p1 和p2哪个是数组指针,哪个是指针数组?int *p1[10];int (*p2)[10];注:int (*p1)[10];解释:p1先和*结合,说明p1是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p1是一个 指针,指向一个数组,叫指针数组。这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p2先和*结合。p2是指针,指向数组,所以int(*p2)[10]是数组指针 。#include<stdio.h> int main() {int a = 10;int* p = &a;int arr[10] = { 0 };printf("%p\n", arr); //类型:int*printf("%p\n", arr+1);printf("%p\n", &arr[0]); //类型:int*printf("%p\n", &arr[0]+1);printf("%p\n", &arr); //类型:int(*)[10]printf("%p\n", &arr+1);return 0; }
可以看出arr和&arr[0]的打印结果一样的,也说明了数组名是首元素的地址。
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该不一样的。实际上: &arr 表示的是 数组的地址,而不是数组首元素的地址。本例中 &arr 的类型是: int(*)[10] ,是一种数组指针类型数组的地址+1,跳过整个数组的大小,所以 &arr+1 相对于 &arr 的差值是40.
2.&数组名 和 数组名 的区别
数组名该怎么样理解?
通常情况下我们说的数组名都是数组首元素的地址。
但是有两个例外:
1.sizeod(数组名),这里的数组名表示整个数组,sizeof(数组名)是计算的整个数组的大小。
2.&数组名,这里的数组名是表示整个数组,&数组名,取出的是整个数组的地址。
3.数组指针的使用
数组指针指向的是数组,那数组指针中存放的应该是数组的地址。#include <stdio.h> int main() {int arr[10] = {1,2,3,4,5,6,7,8,9,0};int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量preturn 0; }
int arr [ 5 ]; //整型数组 数组5个元素int * parr1 [ 10 ]; //指针数组,数组10个元素,每个元素是int*类型int ( * parr2 )[ 10 ]; //parr2是数组指针,该指针指向一个数组,数组是10个元素,每个元素是int类型的int ( * parr3 [ 10 ])[ 5 ]; //parr3是指针数组,数组10个元素,数组的每个元素类型是int(*)[5](判断该类型就把该名称去掉)
4.数组传参,指针传参
一维数组传参 :
#include <stdio.h> void test(int arr[]) //ok? 可以 传的数组参数,数组接收 {} void test(int arr[10]) //ok? 可以 数组只是指定了10个元素 {} void test(int *arr) //ok? 可以 数组传参可以用数组首元素地址 {} void test2(int *arr[20]) //ok? 可以 形参写成数组也可以 {} void test2(int **arr) //ok? 可以 arr2是int*类型,传int **arr也可以 {} int main() {int arr[10] = {0}; int *arr2[20] = {0};test(arr);test2(arr2); }
二维数组传参:
void test(int arr[3][5])//ok? 可以 {} void test(int arr[][])//ok? 不可以 {} void test(int arr[][5])//ok? 可以 {} //二维数组传参,函数形参的设计只能省略第一个[]的数字。 //因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。 void test(int *arr)//ok? 不可以,数组3行5列,数组名是首元素的地址 {} 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); } //传参得保证和要传的类型一样
指针传参
#include <stdio.h>
void print(int *p, int sz)
{int i = 0;for(i=0; i<sz; i++){printf("%d\n", *(p+i));}
}
int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9};int *p = arr;int sz = sizeof(arr)/sizeof(arr[0]);//一级指针p,传给函数print(p, sz);return 0;
}
//二级指针
#include <stdio.h>
void test(int** ptr)
{printf("num = %d\n", **ptr);
}
int main()
{int n = 10;int*p = &n;int **pp = &p;test(pp);test(&p);return 0;
}
5.函数指针
int Add(int a,int b) {return a+b; } int main() {int (*pf)(int ,int)=&Add;int (*pf)(int ,int)=Add;return 0; } //&函数名和函数名都是函数的地址 //pf是一个存放函数的指针变量——函数指针
#指针数组,数组指针,函数指针的区分
int main()
{
//指针数组 本质上是数组,里面存放的是指针char* ch[5]
int arr[10]={0};
//数组指针 本质上是指针,存的是地址
int (*pa)[5]=&arr;
//pf是函数指针
int my_strlen(const char *str)
{
return 0;
}
int (*pf)(const char*)=&my_strlen;
}
6函数指针数组
是用来存放 函数指针 的数组
int main()
{//pf是函数指针
int my_strlen(const char *str)
{
return 0;
}
int (*pf)(const char*)=&my_strlen;
//函数指针数组,那么在函数指针上加个数组呗!
int (*pfA[5])(const char*)={&my_strlen};
}
那么能干什么???
下面是一个简易的计算机小代码:
#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a*b;
}
int div(int a, int b)
{return a / b;
}
int main()
{int x, y;int input = 1;int ret = 0;do{printf( "*************************\n" );printf( " 1:add 2:sub \n" );printf( " 3:mul 4:div \n" );printf( "*************************\n" );printf( "请选择:" );scanf( "%d", &input);switch (input){case 1:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = add(x, y);printf( "ret = %d\n", ret);break;case 2:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = sub(x, y);printf( "ret = %d\n", ret);break;case 3:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = mul(x, y);printf( "ret = %d\n", ret);break;case 4:printf( "输入操作数:" );scanf( "%d %d", &x, &y);ret = div(x, y);printf( "ret = %d\n", ret);break;case 0:printf("退出程序\n");breark;default:printf( "选择错误\n" );break;}} while (input);return 0;
}
用函数指针数组改一下
#include <stdio.h>
int add(int a, int b)
{return a + b;
}
int sub(int a, int b)
{return a - b;
}
int mul(int a, int b)
{return a*b;
}
int div(int a, int b)
{return a / b;
}
int main()
{
int x, y;int input = 1;int ret = 0;
//从这开始变化int(*p[5])(int x, int y) = { 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);}elseprintf( "输入有误\n" );printf( "ret = %d\n", ret);}return 0;
}
这样就不需要do while循环中的一直case!
7指向函数指针数组的指针
int main()
{int arr[10]; //整型数组int (*pa)[10]=&arr; int (*pf[5])(int ,int);int(*(*ppf)[5](int ,int)=&pf;
//pf是函数指针数组,&pf后,ppf再指向它,那ppf就是指向函数指针数组的指针
}
8回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个 函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
void calc(int (*pf)(int ,int)){int x = 0;int y = 0;int ret = 0;printf("请输入两个操作数:");scanf( "%d %d", &x, &y);ret = pf(x, y);printf( "%d\n", ret);
}int main(){int x, y;int input = 1;int ret = 0;do{printf( "*************************\n" );printf( " 1:add 2:sub \n" );printf( " 3:mul 4:div \n" );printf( "*************************\n" );printf( "请选择:" );scanf( "%d", &input);switch (input){case 1:calc(Add);case 2:calc(Sub);break;case 3:calc(Mul);break;case 4:calc(Div);break;case 0:printf("退出程序\n");breark;default:printf( "选择错误\n" );break;}} while (input);return 0;
}
通过定义一个calc函数,把Add,Sub,Mul,Div的函数的地址作为参数传给calc函数,去调用这些函数就是回调函数!