❤️博客主页: 小镇敲码人
欢迎关注:点赞 留言 收藏
回来4天了,加油!!!
当你的能力匹配不上你的梦想,当你需要实现的目标匹配不上你的圈子的时候,你就会出现错位。当我们的能力还匹配不上我们的梦想时,我们就需要沉淀学习。✡️✡️✡️
字符指针,就是储存字符变量地址的指针,这是一种指针的类型。
int main()
{
char ch = 'c';
// ch = 'a';//直接通过变量赋值更改ch的值
char *p = &ch;//将字符ch的地址存入p中
//*p = 'a';//通过对p里面的地址解引用找到ch,间接的改变ch的值
}
int main()
{
const char* ptr = "abcdef";
printf("%s\n",ptr);
return 0;
}
"abcdef"
不可修改,为常量字符串ptr
只储存了字符'a'
的地址。 (可类比字符数组理解,实际上字符串是字符数组的一种形式,在C语言中,字符串是由字符数组表示的,以空字符(‘\0’)作为字符串的结束标志,又因为数组名表示首元素地址,所以字符指针也只储存了首元素的地址)指针数组是由指针组成的数组。在C和C++等编程语言中,指针是一个变量,用于存储内存地址。指针数组是一个数组,其每个元素都是指针类型。
int *arr1[10];
整形指针数组
char *arr2[10];
一级字符指针的数组
char **arr3[10];
二级字符指针的数组
int main()
{
int a = 0;
int b = 1;
int c = 2;
int* arr[] = { &a,&b,&c };
for (int i = 0; i < 3; i++)
{
printf("%d ", *arr[i]);
}
}
int main()
{
int arr1[] = { 1,2,3,4,5 };//arr1-int*
int arr2[] = { 2,3,4,5,6 };//arr2-int*
int arr3[] = { 3,4,5,6,7 };//arr3-int*
//指针数组
int* p[] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
//这两种方式打印都可以,两者择一
printf("%d ", *(*(p + i) + j));
printf("%d ",p[i][j]);
}
printf("\n");
}
}
我们可以通过类比来理解
整型指针:
int * a;
能够指向整形数据的指针
浮点型指针:float* b;
能够指向浮点型数据的指针。
那么数组指针应该是:能够指向数组的指针。
数组的类型表示是什么?通过简单的类比可以知道。
int a = 3;
类型为除变量名以外的部分int
就是整形的类型表示形式。char b = 'a';
类型为除变量名以外的部分char
就是字符类型的的表示形式。int arr[10] = {0};
类型为除变量名以外的部分int [10]
就是这个数组的变量类型表示形式。- 而数组指针的类型是指针,指针的类型是数组,通过前面的学习我们知道,变量类型+变量名为一个变量,所以一个数组指针变量就是,指向数组的指针变量,
int [10] *p = &arr;
,看起来似乎是这样,但是通过下面符号的优先级我们可以知道[]
的优先级要比*
号高,所以我们要给*p
加上括号,让其成为指针,int [10] (*p) = &arr
,那这样是否正确呢?还是不对,放进vs编译器会报错,数组的类型比较特殊,应该是这样int (*p) [10] = &arr;
才正确。
对于下面的数组
int arr[10];
arr
与&arr
分别是什么呢?int main()
{
int arr[10] = { 0 };
printf("arr = %p\n", arr);
printf("arr+1 = %p\n", arr+1);
printf("&arr[0] = %p\n", &arr[0]);
printf("&arr[0]+1 = %p\n", &arr[0]+1);
printf("&arr = %p\n", &arr);
printf("&arr+1 = %p\n", &arr+1);
return 0;
}
运行结果如下:
可以看到数组名和&数组名打印的地址和首元素打印的地址是一样的,这是不是意味着它们是一样的呢?很明显通过代码我们也可以发现,它们并不是相同的,因为&arr+1
打印的地址与arr+1
打印的地址相差了40个字节,而arr+1
打印的地址和首元素&arr+1
的仍然一样,进一步说明,arr就表示首元素地址。
#include
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//int[10] *p = &arr;err
int (*p)[10] = &arr;
for (int i = 0; i < 10; i++)
{
//两种打印方式择一
printf("%d ",*((*p)+i));
printf("%d ", (*p)[i]);
}
//int* p2 = &arr;//err
return 0;
}
#include
void Print(int arr[][5], int r, int c)
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; 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;
}
#include
Print(int(*p)[5], int r, int c)
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
//两种打印方式择一
printf("%d ", *(*(p + i) + 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表示数组名,数组名表示首元素的地址,也就是那一行的地址
Print(arr, 3, 5);
return 0;
}
#include
void test1(int arr[])
{}
void test1(int arr[10])
{}
void test1(int* arr)
{}
void test2(int* arr[20])
{}
void test2(int** arr)
{}
int main()
{
int arr1[10] = { 0 };
int* arr2[20] = { 20 };
test1(arr1);
test2(arr2);
return 0;
}
#include
void test(int arr[3][5]) ✅
{}
void test(int arr[][])//err,二维数组传参,如果是传递数组的副本,需要给出一行有多少数字,因为二维数组其实是一维数组的扩展,所以需要提供一行有多少元素,来正确进行内存访问
{}
void test (int arr[][5]) ✅
{}
void test(int *arr)//err,传的是一行的地址,指针的类型是数组,int(*)[5]。
{}
void test(int *arr[5])//err,传的是第一行的地址,用指针数组接收不对,因为传的是一行的地址。
{}
void test(int (*arr)[5]) ✅
{}
void test(int **arr)//err,传的是数组的地址(第一行的地址),不是一级指针的地址,不能用二级指针接收。
{}
int main()
{
int arr[3][5] = { 0 };
test(&arr);
return 0;
}
int (*)[5]
即用数组指针接收。当函数形参为一级指针时,实参可以是同类型一维数组数组名、同类型变量的地址、同类型指针。
#incldue<stdio.h>
void print(int* p, int sz)
{
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
printf("%d ", p[i]);
}
}
int main()
{
int arr[5] = { 0,1,2,3,4 };
int sz = sizeof(arr) / sizeof(arr[0]);
print(arr, sz);
}
#include
void test(char *p)
{}
int main()
{
char ch = 'a';
char *str = "abcdef";
char* ptr = &ch;
test(str);
test(&ch);
test(ptr);
return 0;
}
当函数形参为二级指针时,实参可以是一级指针的地址、二级指针。
#incldue<stdio.h>
void test(int **ptr)
{
printf("num = %d\n",**ptr);
}
int main()
{
int a = 3;
int* p = &a;
int **pp = &p;
test(pp);
test(&p);
return 0;
}
函数指针和数组指针、字符指针一样都是指针,只不过指针所指向的对象的类型不同,其中函数指针与数组指针最为相似。
我们先看下面代码,函数名的地址,类比数组的地址
int Add(int a, int b)
{
return a + b;
}
int main()
{
printf("%p\n", &Add);
printf("%p\n", Add);
return 0;
}
运行的结果:
可以看见输出的是一段地址,这两个地址都是函数Add的地址。既然函数的地址我们知道了,就可以用指针去保存它,我们只需要知道指针指向的对象的类型就行了。
请看如下代码:
void test()
{
printf("i love programming!\n");
}
//除函数名以外的部分,就是的类型
void (*pf)();//函数指针,指向一个返回值为void,无参数的函数。
void* pf();//表示返回值类型为void*,无参数的函数。。
#incldue<stdio.h>
int Add(int a, int b)
{
return a + b;
}
int main()
{
int arr[10] = { 0 };
int(*p)[10] = &arr;//数组指针
//int (*)[10]是数组指针类型
int(*pf) (int, int) = &Add;
//int (*)(int,int)是函数指针类型
return 0;
}
再给出一组代码帮助理解:
#include
int Add(int x, int y)
{
return x + y;
}
int main()
{
//&Add和Add都可以
int (*pf)(int, int) = &Add;
int (*pf)(int, int) = Add;
int r = Add(3, 5);
printf("%d\n", r);
int m = pf(3, 5);
printf("%d\n", m);
return 0;
}
void (*p)() -p是函数指针
void (*)() -是函数指针类型
(* ( ( void(*)() )0 ) )();
//(((void(*)())0)是将0强制转换为函数至真
//(*(函数指针))是将函数指针解引用
//然后不传参数调用(*(函数指针))();
#include
typedef int* ptr_t;//一级指针别名
typedef unsigned int uint;//无符号整形的别名
typedef int(*parr_t)[10];//数组指针的别名
typedef int (*pf_t)(int, int);//函数指针的别名
int main()
{
ptr_t p1;
uint u2;
parr_t p2;
pf_t p3;
}
请看如下代码:
void(* signal ( int, void(*)(int) ) )(int);
解析:
我们可以观察到signal
函数的返回类型和其中一个参数是相同的都是函数指针类型,下面我们利用别名typedef
来使这段代码看起来更加易懂:
typedef void (*pf_t)(int);
pf_t signal(int,pf_t);