目录
一.字符指针
1.程序组成
在指针的类型中我们知道有一种指针类型为字符指针 char*
int main()
{
char ch = 'w';
char *pc = &ch;
const char *p = "abcdef";//字符指针p内放的是字符串首字符地址
//等价写法:char arr[]={a,b,c,d,e,f,\0}
printf("%s\n",p)//%s打印字符串需要知道字符串的起始位置,而字符指针p就相当于字符串的起始位置
printf("%c\n",*p)//这里打印的只是字符a
printf("%c\n","abcdef"[3]);//结果为d,用来验证字符串的数组写法
//"abcdef"相当于首元素a的地址,就相当与拿到了数组名
return 0;
}
2.相关题目
- 下面代码输出的结果是什么?
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
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;
}
结果:
- 图示解析
二. 指针数组
1.知识先知
指针数组--存放指针的数组--存放在数组中的元素都是指针类型
int* arr[5];//存放整形指针的数组
char* str[6];//存放字符指针的数组
2.实际应用
使用指针数组模拟一个二维数组
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 2,3,4,5,6 };
int arr3[] = { 3,4,5,6,7 };
//创建指针数组arr存放上面三个数组
int *arr[] = { arr1,arr2,arr3 };
//int * int * int *
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]相当于拿到了arr中第i行的数组名——>数组首元素地址
//[j] 访问对应数组第j列的元素
}
printf("\n");
}
return 0;
}
结果:
- 图示解析
三.数组指针
1.知识先知
(1)数组名的理解
数组名是首元素的地址
两个例外:
1.sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
2.&数组名,这里的数组名表示整个数组 取出的数组的地址
int main()
{
int arr[10];
printf("%p\n", arr);
printf("%p\n", arr+1);//跳过1个元素(4个字节)
printf("%p\n", &arr[0]);
printf("%p\n", &arr[0] + 1);//跳过一个元素(4个字节)
printf("%p\n", &arr);
printf("%p\n", &arr+1);//跳过整个数组
//指针类型决定了指针+1,到底+几个字节
return 0;
}
结果:
2.实际应用
(1) 要点明晰
数组指针--指针--指向数组的指针
int main()
{
int arr[10] = { 0 };
int(*p)[10] = &arr;//p是用来存放数组的地址的,p就是数组指针
return 0;
}
- 理解
int(*p)[10]=&arr;
p是一个指针,指向一个数组,数组有10个元素,每个元素的类型是int
p+1
跳过10个元素的这个数组的大小
*p 可理解为(*&arr)
解引用操作拿到数组名
(2)二维数组传参
①普通写法
void print(int arr[3][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 ", 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;
}
- 图示解析
② 数组指针写法
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");
}
}
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;
}
- 图示解析
③拓展
看下面的一段代码,分析parr3是什么?
int (*parr3[10])[5];
parr3是一个数组,是存放数组指针的数组,数组有10个元素;存放的数组指针,每个指针指向的数组有5个元素,每个元素的类型是int类型
- 图示解析
四.数组参数和指针参数
1.一维数组传参
void test(int arr[])//ok?
{}
void test(int arr[10])//ok?
{}
void test(int* arr)//ok?
{}
void test2(int* arr[20])//ok?
{}
void test2(int** arr)//ok?
{}
int main()
{
int arr[10] = { 0 };
int* arr2[20] = { 0 };
test(arr);
test2(arr2);
}
- 图示解析
2. 二维数组传参
void test(int arr[3][5])//ok?
{}
void test(int arr[][])//ok?
{}
void test(int arr[][5])//ok?
{}
void test(int* arr)//ok?
{}
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.一级指针传参
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;
}
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;
}
二级指针可以接收的参数:
1.传二级指针变量,如pp
2.传一级指针变量的地址,如&p
3.传指针数组的数组名,如char* arr[6];test(arr)
五.函数指针
1.要点先知
函数指针--指向函数的指针--存放的是函数的地址
int Add(int x,int y)
{
return x + y;
}
int main()
{
//&函数名可以拿到函数的地址
printf("%p\n", &Add);
//函数名也是函数的地址
printf("%p\n", Add);
return 0;
}
结果:
2.实际运用
(1)使用注意
int Add(int x, int y)
{
return x + y;
}
int main()
{
//pf1,pf2都是函数指针变量
int (*pf1)(int, int) = &Add;
int (*pf2)(int, int) = Add;
//错误写法
int* pf2(int, int);
//pf2是函数名,函数有两个参数,类型为int,函数的返会类型为int*
return 0;
}
(2)具体示范
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf2)(int, int) = &Add;
//以下两种写法都可以
int ret = (*pf2)(2, 3);
int ret = pf2(2,3);
printf("%d\n", ret);
//输出结果为5
return 0;
}
(3)代码分析
//代码1
( *(void (*)())0 )();
//代码2
void (*signal(int , void(*)(int)))(int);
①第1段代码
( *(void (*)())0 )();
1.void (*)() 是函数指针类型,该函数没有参数,返回类型为void,( void (*)() )为强制类型转换
2.( void (*)()0 )--0本身为int类型,将其转化为函数指针类型,可认为是0地址处放置了一个函数
3.( *(void (*)())0 )--*(解引用操作)调用这个地址处的函数
4.( *(void (*)())0 )()--由于该函数没有参数,所以()内没有传参
该代码是在调用0地址处的函数,这个函数没有参数,返回类型为void
②第二段代码
void (* signal(int , void(*)(int) ) ) (int);
1.signal( int ,void(*)(int) )这是一个函数,有两个参数,类型分别为int和函数指针类型
2.将1拿出来得到1函数的返回类型:void(*)(int)
该代码是一次函数声明,声明的是signal函数,signal函数有两个参数,第一个是int类型,第二个是函数指针类型,该函数指针指向的函数,参数是int,返回类型是void
signal函数的返回类型也是函数指针类型,该类型是void(*)(int),该函数指针指向的函数,参数是int,返回类型是void