目录
指针初阶
字符指针
char* 一般使用
#include <stdio.h>
void main() {
char* ps = "hello bit";//本质上把字符串首字符的地址存入ps指针
char arr[] = "hello bit";//本质上把字符串放入数组arr里,但是arr数组名表示首元素地址
printf("%c\n", *ps);//h
printf("%c\n", *arr);//h
//给起始位置的地址就可以打印出来,因为以字符串形式打印,所以会从后面找“\0”
printf("%s\n", arr);
printf("%s\n", ps);
}
#include <stdio.h>
void main() {
char str1[] = "hello bit";//数组1
char str2[] = "hello bit";//数组2
*str1 = 'p';
printf("%s\n", str1);//可以更改
//下面的字符串为常量字符串在内存中仅存一份并且不可被更改
char* str3 = "hello bit";
char* str4 = "hello bit";
//*str3 = 'p';
//printf("%s\n", str3);//不可更改
printf("%p\n", str1);//0019F88C
printf("%p\n", str2);//0019F878
//下面地址与上面两个地址各不相同,独立出一个空间,为常量字符串地址
printf("%p\n", str3);//00257B30
printf("%p\n", str4);//00257B30
}
指针数组
定义:存放指针的数组
#include <stdio.h>
void main() {
int a = 10, b = 20, c = 30;
int* arr1[3] = { &a,&b,&c };//存放整形指针的数组
int i = 0;
for (i = 0; i < 3; i++) {
printf("%d\n", *(arr1[i]));//*(arr1[i])也可以写成*(*(arr1+i))
}
}
#include <stdio.h>
void main() {
int a[] = { 1,2,3,4,5 };
int b[] = { 2,3,4,5,6 };
int c[] = { 3,4,5,6,7 };
int* arr[] = {a,b,c};
int i = 0;
for (i = 0; i < 3; i++) {
int j = 0;
for (j = 0; j < 5; j++) {
printf("%d ", *(arr[i] + j));//*(arr[i] + j)也可以写成arr[i][j]
}
printf("\n");
}
}
arr[i]等价于:*(arr+i)
数组指针
整形指针:指向整形的指针
字符指针:指向字符的指针
数组指针:指向数组的指针
#include <stdio.h>
void main() {
int arr[] = { 1,2,3,4,5 };
//arr:数组首元素的地址——arr[0]的地址
//&arr:整个数组的地址
int(*parr)[10] = &arr;//parr就是一个数组指针,存放的是数组的地址
double* d[5];//指针数组
double* (*p)[5] = &d;//指针数组的数组指针
}
&数组名和数组名
#include <stdio.h>
void main() {
int arr[10] = { 0 };
int* p1 = arr;
int (*p2)[10] = &arr;
printf("%p\n", arr);
printf("%p\n", &arr);
printf("%p\n", arr+1);//地址比上面多4字节
printf("%p\n", &arr+1);//地址比上面多40字节
//由此观之,数组地址的步长为数组首元素地址步长的元素个数倍
}
#include <stdio.h>
void main() {
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int(*p)[10] = &arr;
int i = 0;
for (i = 0; i < 10; i++) {
printf("%d ", *((*p) + i));//p是整个数组的地址,*p代表arr
}
}
注意:数组名表示首元素的地址,二维数组的首元素是第一行,也就是第一行一维数组的地址也就是一位数组的指针
#include <stdio.h>
print(int(*p)[5], int r, int c) {
int i = 0, j = 0;
for (i = 0; i < r; i++) {
for (j = 0; j < c; j++) {
printf("%d ", *(*(p + i) + j));
//p+i——i+1行数组的地址,*(p+i)——i+1行的数组,*(p+i)+j——i+1行数组第j+1行元素的地址,*(*(p + i) + j)——i+1行数组第j+1行元素
}
printf("\n");
}
}
void main() {
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
print(arr, 3, 5);
}
关于数组代码含义
int arr[5];//整形数组5个元素,每个元素是int
int* p1[10];//指针数组里面可存10个int类型的指针
int(*p2)[10];//int类型10个元素数组的指针
int(*p3[10])[5];//存放数组指针(该指针指向int类型5个元素的数组)的数组(该数组能存10个数组指针)
数组参数与指针参数
一维数组传参
1.传过来一个10个元素的整形数组,用自动长度整形数组接受——ok
2.传过来一个10个元素的整形数组,用10个元素的整形数组接受——ok
3.传过来一个数组名(int类型首元素地址)用int类型指针接受——ok
4.传过来一个整形指针的数组可容纳20元素,用20元素整形指针数组接受——ok
5.传过来一个数组名(int*类型首元素地址),用整形二级指针接受——ok
二维数组传参
关于前3个由下面注释可知1,3都ok
4.二维数组传参,其数组名表示首元素的地址也就是第一行一维数组的地址,由此观之,只有6ok
注意:只有双重地址才可用二级指针接受,接受时你还得看类型对不对 。
一级指针传参
#include <stdio.h>
void print(int* p,int sz) {
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", *(p + i));
}
}
void main() {
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr1;
int sz = sizeof(arr1) / sizeof(arr1[0]);
print(p, sz);
}
二级指针传参
#include <stdio.h>
void test(int** p) {
**p = 20;
}
void main() {
int a = 10;
int* pa = &a;
int** ppa = &pa;
test(ppa);
int b = 7;
int* arr[10] = { &b };
test(arr);
printf("%d\n", a);
printf("%d\n", *(arr[0]));
}
函数指针
定义:指向函数的指针(存放函数地址的指针)
#include <stdio.h>
int Add(int a,int b) {
return a + b;
}
void main() {
//&函数名——取到函数的地址
printf("%p\n", &Add);//由此观之,可以得到函数的地址
//注意:数组名!=&数组名,但是函数名==&函数名(函数没首元素的地址)
printf("%p\n", Add);
//函数指针的保存
int (*p1)(int, int) = &Add;//当然,因为函数名==&函数名,所以这里面的&可省略
printf("%p\n", p1);
int (*p2)(int, int) = Add;
printf("%p\n", p2);
//函数的调用
int ret=(*p1)(3, 5);
int res=p2(3, 5);//直接拿函数名调用——p2==&Add而&Add==Add所以p2==Add
printf("%d\n",ret);
printf("%d\n",res);
}
函数指针类型:返回值类型 (*)(参数类型列表)
代码阅读
函数指针类型为返回值的声明
void (* signal(int,void(*)(int)))(int);
替代——
void(*)(int) signal(int,void(*)(int));
//简便写法
typedef void(*pfun_t)(int);//对void(*)(int)的函数指针类型重命名为pfun_t;
pfun_t signal(int, pfun_t);
函数指针数组
定义:存放函数指针的数组
#include <stdio.h>
int Add(int a,int b) {
return a + b;
}
int Sub(int a, int b) {
return a - b;
}
void main() {
int (*p1)(int, int) = Add;
int (*p2)(int, int) = Sub;
int (*parr[2])(int, int) = {p1,p2};//函数指针数组可存放2个返回值为int参数列表2个int的函数指针
int ret = (*parr[1])(8, 5);//通过函数指针数组进行函数调用1
int res = parr[0](8, 5);//通过函数指针数组进行函数调用2
printf("%d\n", ret);
printf("%d\n", res);
}
指向函数指针数组的指针
定义:取出函数指针数组的地址
int arr1[5];//整形数组
int (*p1)[5]=&arr1;//整形数组的指针
int* arr2[5];//整形指针的数组
int* (*p2)[5] = &arr2;//整形指针数组的指针
int (*p3)(int,int);//函数指针
int (*p4[5])(int, int);//函数指针的数组
int (*(*p4)[5])(int, int);//函数指针数组的指针
回调函数
定义:
举例:b函数里面有a函数的指针作为参数,当通过a函数的指针反过来调用a函数,这种机制叫回调函数机制,通过指针反过来调用的这个函数叫回调函数。
#include <stdio.h>
int Add(int a,int b) {
return a + b;
}
int AP(int(*p)(int,int)) {
return p(2, 3);//这里我直接省略了*
}
void main() {
int(*ap)(int(*)(int, int)) = &AP;
int ret=ap(&Add);
printf("%d\n", ret);
return;
}
#include <stdio.h>
int Add(int a,int b) {
return a + b;
}
int AP(int(*p)(int,int)) {
return p(2, 3);//这里我直接省略了*
}
void main() {
//写法2——反正&AP==ap==AP==*ap所以将*ap用AP取代
int ret=AP(&Add);
printf("%d\n", ret);
return;
}