目录
- 一、指针数组
- 二、数组指针
- 三、数组参数、指针参数
- 3.1 一维数组传参
- 3.2 二维数组传参
- 3.3 一级指针传参
- 3.4 二级指针传参
- 四、函数指针
- 五、函数指针数组
- 六、指向函数指针数组的指针
- 七、回调函数
- 八、指针和数组的试题解析
一、指针数组
使用指针数组实现二维数组
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6};
int arr3[] = { 3,4,5,6,7};
//指针数组
int* arr[3] = { arr1,arr2,arr3 };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
二、数组指针
类比
整型指针–指向整型变量的指针,存放整型变量的地址的指针变量
字符指针–指向字符变量的指针,存放字符变量的地址的指着变量
数组指针–指向数组的指针,存放的是数组的地址的指针变量
数组名的理解
数组名是数组首元素的地址
有两个例外:
1.sizeof(数组名),这里的数组名不是数组首元素的地址,数组名表示整个数组。sizeof(数组名)计算的是整个数组的大小,单位是字节。
2.&数组名,这里的数组名表示整个数组,&数组名取出的是整个数组的地址。
除此之外,所有地方的数组名都是数组首元素的地址
int main()
{
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;
}
数组指针的使用
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int(*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p,类型是int(*)[10]
return 0;
}
void print(int (*p)[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 ", *(*(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 };
//arr 是数组名
//二维数组的每一行可以理解为二维数组的一个元素,每一行又是一个一位数组,所以呢?二维数组其实是一维数组的数组
//二维数组的数组名,也是数组名,数组名就是数组首元素的地址
//arr-首元素的地址
//-第一行的地址
//-一维数组的地址-数组的地址
print(arr, 3, 5);
return 0;
}
三、数组参数、指针参数
3.1 一维数组传参
#include <stdio.h>
void test(int arr[])//ok 数组传参用数组接收 大小可以省略
{}
void test(int arr[10])//ok 数组传参用数组接收 大小没有省略
{}
void test(int* arr)//ok arr数组名表示首元素地址,可以用指针接收
{}
void test2(int* arr[20])//ok 数组传参每个类型是int*,用数组接收
{}
void test2(int** arr)//ok arr2每个元素都是int*,传的是int*类型的地址,可以用二级指针接收
{}
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test(arr);
test2(arr2);
}
3.2 二维数组传参
void test(int arr[3][5])//ok 数组传参用数组接收
{}
void test(int arr[][])//ok?不行! //形参部分,行可以省略,但是列不能省略(只能省略第一个[]的数字)
{}
void test(int arr[][5])//ok //形参部分,行可以省略,但是列不能省略(只能省略第一个[]的数字)
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr)//ok? 不行! //arr数组名是一行的地址,不是整型的地址
{}
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);
}
3.3 一级指针传参
#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;
}
思考:当一个函数的参数部分分为一级指针的时候,函数能接收什么参数?
void test(char *p)
{}
char ch='2';
char *ptr=&ch;
char arr[]="abcdef";
test(&ch);
test(ptr);
test(arr);
3.4 二级指针传参
#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;
}
思考:当函数的参数为二级指针的时候,可以接收什么参数?
void test(char** p)
{}
char n='a';
char* p=&n;
char** pp=&p;
char* arr[5];
test(&p);
test(pp);
test(arr);
四、函数指针
函数指针–指向函数的指针
int Add(int x, int y)
{
return x + y;
}
int main()
{
//int arr[10] = { 0 };
//&arr;
//int(*p)[10] = &arr; //不写&表示数组首元素的地址(int的地址:int *),不是数组的地址(数组指针:int* [10])
printf("%p\n", &Add);
printf("%p\n", Add);
//函数名是函数的地址
//&函数名也是函数的地址
int (*pf)(int, int) = &Add;//pf是函数指针变量
//int (*)(int,int)是函数指针类型
return 0;
}
函数名是函数的地址。
&函数名也是函数的地址。
void test(char* pc, int arr[10])
{}
int main()
{
void (*pf)(char*,int [10])=&test;
//void (*pf)(char*, int*); // 或者使用 int[],但不需要大小 test的&也可以去掉
return 0;
}
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf)(int, int) = &Add;
//int (*pf)(int, int) = Add;
int r = Add(3, 5);
printf("%d\n", r);
int m = (*pf)(4, 5);//*也可以去掉
printf("%d\n", m);
return 0;
}
void(*p)()-p是函数指针
void (*) ()是函数指针类型
int main()
{
//调用0地址处的函数
//1.将0强制类型转换void (*) ()类型的函数指针
//2.调用0地址处的这个函数
( *( void (*) ())0 ) ();
return 0;
}
int main()
{
//signal 是一个函数声明
//signal 函数有2个参数,第一个参数的类型是int,第二个参数的类型是void(*)(int)函数指针类型
//该函数指针指向的函数有一个int类型的参数,返回类型是void
//signal 函数的返回类型也是void(*)(int) 函数指针类型,该函数指针指向的函数有一个int类型的参数,返回类型是void
typedef void(*pf_t)(int);
pf_t signal(int, pf_t);
void (* signal(int, void(*)(int)) )(int);
return 0;
}
五、函数指针数组
int(*p[4])(int x, int y) = { add, sub, mul, div };
六、指向函数指针数组的指针
int (*pf)(int, int);//函数指针
int (*pfArr[4])(int, int);//函数指针数组
int(*(*p[4])(int, int)) = &pfArr;//函数指针数组的地址
//p就是指向函数指针数组的指针
七、回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
void的指针-无具体类型的指针
void 类型的指针可以接收任意类型的地址
这种类型的指针是不能直接解引用操作的
也不能直接进行指针运算的
八、指针和数组的试题解析
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//4*4=16
printf("%d\n", sizeof(a+0));//数组名a是数组首元素的地址,a+0还是首元素的地址,地址打大小4/8
printf("%d\n", sizeof(*a));//数组名a是数组首元素的地址,*a就是首元素,大小就是4个字节
printf("%d\n", sizeof(a+1));//数组名a是数组首元素的地址,a+1是第二个元素的地址,地址的大小4/8
printf("%d\n", sizeof(a[1]));//第二个元素的大小就是4个字节
printf("%d\n", sizeof(&a));//&a是数组的地址,数组的地址也是地址,是地址4/8个字节
printf("%d\n", sizeof(*&a));//16个字节
//sizeof(*&a)-->sizeof(a)-16
//&a-->int(*)[4]
//*&a-->访问的是一个数组
printf("%d\n", sizeof(&a+1));//&a+1相对于&a是跳过了整个数组,但是即使跳过了整个数组,&a+1依然是地址,是地址就是4/8字节
printf("%d\n", sizeof(&a[0]));//&a[0]是首元素的地址,4/8个字节
printf("%d\n", sizeof(&a[0]+1));//&a[0]是首元素的地址,&a[0]+1就是第二个元素的地址,是地址就是4/8个字节
return 0;
}
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));//因为字符数组arr中没有\0,所以在求字符串长度的时候,会一直往后找,产生的结构就是随机值
printf("%d\n", strlen(arr+0));//arr+0是首元素的地址,和第一个一样,也是随机值
//printf("%d\n", strlen(*arr));//err //arr是数组首元素的地址,*arr就是数组首元素,就是'a'-97
//strlen函数参数的部分需要传一个地址,当我们传递的是'a'时,‘a’的ASCII码值是97,那就是将97作为地址传参
//srelen就会从97这个地址开始统计字符串长度,这就非法访问内存了
//printf("%d\n", strlen(arr[1]));//err (同上,这里传的是98)
printf("%d\n", strlen(&arr));//&arr是数组的地址,数组的地址和数组首元素的地址,值是一样的,那么传递给strlen函数后,
//依然是从数组的第一个元素的位置开始往后统计。 是随机值
printf("%d\n", strlen(&arr+1));//随机值
printf("%d\n", strlen(&arr[0]+1));//&arr[0]+1是第二个元素的地址,结果也是随机值
printf("%d\n", sizeof(arr));//数组名单独放在sizeof内部,这里的arr表示整个数组,计算的是整个数组的大小,单位是字节,总共6个字节
printf("%d\n", sizeof(arr+0));//arr表示数组首元素的地址,arr+0还是数组首元素的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*arr));//arr表示数组首元素的地址,*arr就是首元素,大小1个字节
printf("%d\n", sizeof(arr[1]));//arr[1]就是第二个元素,大小是1个字节
printf("%d\n", sizeof(&arr));//&arr是数组的地址,但是数组的地址也是地址,是地址就是4/8
printf("%d\n", sizeof(&arr+1));//&arr+1是跳过整个数组后的地址,是地址就是4/8个字节
printf("%d\n", sizeof(&arr[0]+1));//第二个元素的地址,是4/8个字节
return 0;
}
int main()
{
char arr[] = "abcdef";//[a b c d e f \0]
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
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr+1));//随机值
printf("%d\n", strlen(&arr[0]+1));//5
printf("%d\n", sizeof(arr));//7
//char [7]
printf("%d\n", sizeof(arr+0));//arr+0是首元素的地址 4/8
printf("%d\n", sizeof(*arr));//*arr其实就是首元素 1个字节
//*arr-->*(arr+0)==arr[0]
printf("%d\n", sizeof(arr[1]));//arr[1]是第二个元素,1个字节
printf("%d\n", sizeof(&arr));//&arr是数组的地址,是地址就是4/8个字节
printf("%d\n", sizeof(&arr+1));//&arr+1是跳过一个数组的地址 4/8
printf("%d\n", sizeof(&arr[0]+1));//&arr[0]+1 是第二个元素的地址 4/8
return 0;
}
int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p));//6
printf("%d\n", strlen(p+1));//5
//printf("%d\n", strlen(*p));//err (传的是a-97)
//printf("%d\n", strlen(p[0]));//err(传的是a-97)
printf("%d\n", strlen(&p));//从p的地址开始数,随机值
printf("%d\n", strlen(&p+1));//随机值
printf("%d\n", strlen(&p[0]+1));//5 'b'的地址开始向后数
printf("%d\n", sizeof(p));//p是一个指针变量//大小就是4/8
printf("%d\n", sizeof(p+1));//p+1是‘b’的地址,是地址大小就是4/8个字节
printf("%d\n", sizeof(*p));//*p就是‘a’,就是1个字节
printf("%d\n", sizeof(p[0]));//p[0]-->*(p+0)-->*p 1个字节
printf("%d\n", sizeof(&p));//4/8
//&p--char**
printf("%d\n", sizeof(&p+1));//4/8
printf("%d\n", sizeof(&p[0]+1));//4/8, &p[0]+1得到的是‘b’的地址
return 0;
}
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//3*4*4=48
printf("%d\n", sizeof(a[0][0]));//4
printf("%d\n", sizeof(a[0]));//a[0]是第一行一维数组的数组名
//数组名算是单独放在sizeof内部了,计算的是整个数组的大小,大小是16个字节
printf("%d\n", sizeof(a[0]+1));
//a[0]作为第一行的数组名,没有单独放在sizeof内部,没有&
//a[0]表示数组首元素的地址,也就是a[0][0]的地址
//所以a[0]+1是第一行第二个元素的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*(a[0]+1)));//4
//计算的就是第一行第2个元素的大小
printf("%d\n", sizeof(a+1));//4/8
//a是数组首元素的地址,是第一行的地址 int(*)[4]
//a+1就是第二行的地址
printf("%d\n", sizeof(*(a+1)));//16
//*(a+1)-->a[1]-->sizeof(*(a+1))-->sizeof(a[1])计算的是第二行的大小
//a+1-->是第二行的地址,int(*)[4]
//*(a+1)访问的是第二行的数组
printf("%d\n", sizeof(&a[0]+1));//4/8
//&a[0]是第一行的地址 int(*)[4]
//&a[0]+1 是第二行的地址 int(*)[4]
printf("%d\n", sizeof(*(&a[0]+1)));//16 计算的是第二行的大小
printf("%d\n", sizeof(*a));//计算的是第一行的大小-16
//a是数组首元素的地址,就是第一行的地址
//*a就是第一行
//*a-->*(a+0)-->a[0]
printf("%d\n", sizeof(a[3]));//16
//a[3]-->int [4]
return 0;
}