10.指针进阶
这里写目录标题
- 1. 字符指针
- 2. 指针数组
- 3. 数组指针
- 3.1 int (\*p)\[10\];
- 3.2 arr; &arr\[ \]; &arr.
- 3.3 数组指针的使用
- 4. 数组传参和指针传参
- 4.1 一维数组传参
- 4.2 二维数组传参
- 4.3 一级指针传参
- 4.4 二级指针传参
- 5. 函数指针
1. 字符指针
int main()
{
char ch = 'w';
char *pc = &ch;//pc就是字符指针
*pc = 'a';
return 0;
}
int main()
{
char arr[] = "abcdef";
//[a b c d e f \0]
const char* p = "abcdef";//常量字符串
//把首字符a的地址赋值给p
//p指向a的地址
printf("%s\n", p);
printf("%c\n", *p);
return 0;
}
abcdef
a
分析代码
int main()
{
char str1[] = "hello RuaRua.";
char str2[] = "hello RuaRua.";
const char* str3 = "hello RuaRua.";
const char* str4 = "hello RuaRua.";
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
- C/C++会把常量字符串存储到单独的一个内存区域,
str3
和str4
是指向常量字符串的指针,它们指向的是存储在静态存储区只读部分的字符串常量"hello RuaRua."
,这个字符串常量与str1
和str2
中存储的字符串虽然内容相同,但在内存中是不同的存储位置。
2. 指针数组
int* arr1[10]; //存放 整形指针 的数组
char *arr2[10]; //存放 一级字符指针 的数组
char **arr3[10];//存放 二级字符指针 的数组
eg.模拟二维数组
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 2 3 4 5
2 3 4 5 6
3 4 5 6 7
3. 数组指针
3.1 int (*p)[10];
int (*p)[10];
//p是数组指针变量,指向的是数组
3.2 arr; &arr[ ]; &arr.
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
printf("%p\n", &arr);
printf("%d\n", sizeof(arr));
return 0;
}
008ff858 arr 是数组首元素地址
008ff858 &arr[0] 是数组首元素地址
008ff858 &arr 是整个数组地址
40 sizeof(arr) 计算整个数组大小,arr表示整个数组
名称 | 值 | 类型 |
---|---|---|
arr | 0x008ff858 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} | int[10] |
&arr[0] | 0x008ff858 {0} | int * |
&arr | 0x008ff858 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} | int(*)[10] |
int main()
{
int arr[10] = { 0 };
printf("%p\n", arr);
printf("%p\n", arr+1);
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0]+1);
printf("%p\n", &arr);
printf("%p\n", &arr+1);
return 0;
}
00F3F840
00F3F844
00F3F840
00F3F844
00F3F840
00F3F868-->0x28-->40
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
//把数组arr的地址赋值给数组指针变量p
//int[10] * p = &arr;//err
int(*p)[10] = &arr;//p是数组指针,存放数组地址
return 0;
}
arr 的类型是 int [10]
p 的类型是 int(*)[10]
&arr 的类型是 int(*)[10]
3.3 数组指针的使用
一般在二维数组中使用,常规写法:
void Print(int arr[3][5], int r, int c)//row,column
{
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");
}
}
int main()
{
int arr[3][5] = { 1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7 };
Print(arr, 3, 5);
return 0;
}
二维数组,每一行可以理解为数组的一个元素
每一行是一个一维数组
二维数组是 一维数组的数组
arr -->首元素地址
-->第一行地址
-->一维数组地址
void Print(int(*p)[5], int r, int c)
{
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 5; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
(p + i):指针移动到i数组
*(p + i):i数组的地址
(*(p + i) + j):i数组j元素的地址
*(*(p + i) + j):得到元素
4. 数组传参和指针传参
4.1 一维数组传参
void test1(int arr[5], sz)
{}
void test2(int* p, sz)
{}
int main()
{
int arr[5] = { 0 };
test1(arr, 5);
test2(arr, 5);
return 0;
}
4.2 二维数组传参
void test1(int arr[3][5], int r, int c)
{}
void test2(int(*p)[5], int r, int c)
{}
int main()
{
int arr[3][5] = {0};
test1(arr, 3, 5);
test2(arr, 3, 5);
return 0;
}
int arr[3][5]
:是二维数组类型的形参,虽然写成 int arr[3][5]
,但实际上会退化为指向包含 5
个 int
类型元素的数组的指针,即 int (*)[5]
类型。
int (*p)[5]
:是指针类型的形参,p
是一个指向包含 5
个 int
类型元素的数组的指针。和 int arr[3][5]
本质上是等价的,都用于接收二维数组的首地址,即第一行的地址。
4.3 一级指针传参
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(int* p)//函数能接收什么参数?
{ }
int n = 0;
int *ptr = &n;
int arr[] = "abcdef";
test (ptr);
test (&n);
test (arr);
4.4 二级指针传参
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(int** p)
{ }
int main()
{
int n = 10;
int* p = &n;
int** pp = &p;
int* arr[10];
test(&p);
test(pp);
test(arr);
return 0;
}
5. 函数指针
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
006013B6
006013B6
test和&test都是函数地址
eg
void test(char* pc, int arr[10])
{ }
int main()
{
void (*pf)(char*, int arr[]) = test;
return 0;
}
eg
int Add(int x, int y)
{
return x + y;
}
int main()
{
int arr[10] = { 0 };
int(*pa)[10] = &arr;//类比
int(*pf)(int, int) = &Add;
//pf是函数指针变量
return 0;
}
使用指针调用函数
int Add(int x, int y)
{
return x + y;
}
int main()
{
int(*pf)(int, int) = &Add;
int r = Add(3, 5);
printf("%d\n", r);
int m = (*pf)(4, 5);
//--> int m = pf(4, 5);
//pf和Add一回事
printf("%d\n", m);
return 0;
}
eg1.代码分析
(*(void (*)())0)();
void (*p)()-->p是函数指针
void (*)() -->函数指针类型
(void (*)())0-->强制类型转换, 0转换成 void (*)(), 0的地址中存放着这个函数
*(void (*)())0-->解引用,调用这个函数
(*(void (*)())0)()-->调用时无参数
(*(void (*)())0)();-->调用0地址处的函数
eg2.代码分析
void (*signal(int , void(*)(int)))(int);
void (*p)(int); -->函数指针
void(*)(int)-->函数指针指向一个int类型的参数,返回类型void
signal(int , void(*)(int))--> signal 函数(类型 int ,函数指针类型 void(*)(int))
简化
//typedef重定义
typedef unsigned int uint;
typedef int* ptr_t;
typedef int(*parr_t)[10];
typedef int(*pf_t)(int,int);
int main()
{
uint u1;
ptr_t p1;
int* p2;
return 0;
}
void (*signal(int , void(*)(int)))(int);
--->
typedef void(*pf_yt)(int);
//void (*signal(int, void(*)(int)))(int);
pf_yt signal(int, pf_yt);