在初阶指针中,我们知道了指针的概念:
1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。
2.指针的大小是固定的4/8个字节(32位平台/64位平台)。
3.指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
4.指针的运算。
接下来我们继续学习指针的进阶知识
char*
一般使用:
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'w';
return 0;
}
还有一种使用方式如下:
这里是把一个常量字符串的首字符‘h’的地址放在pstr指针变量里面,通过pstr就可以访问整个字符串(只要提供首字符的地址就可以打印整个字符串)
int main()
{
const char* pstr = "hello world";
printf("%s\n",pstr);
return 0;
}
常量字符串里面的字符是不可以被改变的,在字符指针的前面加上const,如果常量字符串被改变就会报警告
一道题:
int main()
{
char str1[] = "hello world";
char str2[] = "hello world";
const char* str3 = "hello world";
const char* str4 = "hello world";
if (str1 == str2)
printf("str1 and str2 are same\n");//x
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");//x
return 0;
}
这⾥str3和str4指向的是⼀个同⼀个常量字符串。C / C++会把常量字符串存储到单独的⼀个内存区域,当几个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。
整型数组 - 存放整形的数组
字符数组 - 存放字符的数组
指针数组-存放指针(地址)的数组
指针数组变量:
int* arr1[10];//整型指针的数组
char* arr2[4];//一级字符指针的数组
char** arr3[5];//二级字符指针的数组
int main()
{
//存放字符指针的数组
char* arr[4] = {"abcdef","qwer","hello","hehe"};
int i = 0;
for(i = 0;i < 4; i++)
{
printf("%s\n",arr[i]);
}
return 0;
}
模拟二维数组:
int main()
{
int arr1[5] = {1,2,3,4,5};
int arr2[5] = {2,3,4,5,6};
int arr3[5] = {3,4,5,6,7};
int arr4[5] = {0,0,0,0,0};
//存放整形指针的数组
int* arr[4] = {arr1,arr2,arr3,arr4};
int i = 0;
for(i = 0;i < 4; i++)
{
int j = 0;
for(j = 0;j < 5; j++)
{
printf("%d ",*(arr[i] + j));//arr[i][j]
}
printf("\n");
}
return 0;
}
字符指针 - 存放字符地址的指针 - 指向字符数据的指针 char*
整形指针 - 存放整形地址的指针 - 指向整型数据的指针 int*
浮点型指针 - 指向浮点型数据的指针 float* double*
数组指针 - 存放数组地址的指针 - 指向数组的指针
数组指针变量:
int (*p)[10];
int arr[10] = {0};
&arr;//得到的就是数组的地址
int (*p)[10] = &arr;
通过调试可知,p和&arr的类型完全一致:int (*)[10] - 数组指针类型
int (*p) [10] = &arr;
| | |
| | |
| | p指向数组的元素个数
| p是数组指针变量名
p指向的数组的元素类型
int main()
{
char ch = 'w';
char* pc = &ch;
int num = 10;
int* pi = #
int arr[10];
int (*pa)[10] = &arr;//pa就是一个数组指针变量
return 0;
}
对于以下的数组:
int arr[10];
arr是数组名,数组名表示数组首元素的地址
&arr则表示数组的地址
先看以下代码:
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
printf("%p\n",arr);//类型是int*
printf("%p\n",arr + 1);//跳过4个字节
printf("%p\n",arr[0]);//类型是int*
printf("%p\n",arr[0] + 1);//4
printf("%p\n",&arr);//类型是int (*)[10]
printf("%p\n",&arr + 1);//40
return 0;
}
根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义是不一样的,实际上:&arr表示的是数组的地址,而不是数组首元素的地址。
&arr取出的是数组的地址,只有数组的地址才需要数组来接收
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int (*p)[10] = &arr;
既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址
一个数组指针的使用:
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = arr;
int i = 0;
for(i = 0;i < 10; i++)
{
printf("%d ",*(p + i));
}
//使用数组指针实现
int (*p)[10] = &arr;//对数组的访问需要循环来完成,不能直接打印,因为没有像字符串那样有‘\0’作为结束标志
int i = 0;
for(i = 0;i < 10; i++)
{
printf("%d ",(*p)[i]);
//printf("%d ",arr[i]); // *p = * &arr = arr
}
return 0;
}
void print1(int arr[3][4],int row,int col)
{
int i = 0;
for(i = 0;i < row; i++)
{
int j = 0;
for(j = 0;j < col; j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
void print2(int (*p)[4],int row,int col)//将二维数组首元素的地址,即第一行的地址传给p,则p指向第一行的元素,p+1就跳过4个元素,即指向第二行
{
int i = 0;
for(i = 0;i < row; i++)
{
int j = 0;
for(j = 0;j < col; j++)
{
printf("%d ",(*(p + i))[j]);//p[i][j]
}
printf("\n");
}
}
int main()
{
int arr[3][4] = {{1,2,3,4},{2,3,4,5},{3,4,5,6}};//数组名是首元素的地址,而二维数组首元素地址是第一行的地址(int [4]),相当于一维数组的地址,将一维数组的地址放到一个数组指针中去,即int (*p)[4]
ptint1(arr,3,4);
ptint2(arr,3,4);
return 0;
}
指针数组与数组指针的辨别:
int arr[5];//整型数组,数组是5个元素
int* arr1[10];//指针数组,数组10个元素,每个元素是int*类型的
int (*p)[10];//数组指针,p是数组指针(变量),该指针指向一个数组,数组是10个元素,每个元素是int (*)[10]类型的
int (*p2[10])[5];//p2是数组,数组有10个元素,数组的每个元素的类型是:int (*)[5]的数组指针类型
void test(int arr[])//ok
{}
void test(int arr[10])//ok
{}
void test(int* arr)//ok
{}
void test2(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);//数组名是首元素的地址,而arr每个元素的类型是int,int的地址是int*
test2(arr2);//数组名表示首元素的地址,而arr2每个元素的类型是int*,是一级指针;int*的地址,就是int**(二级指针)
return 0;
}
void test(int arr[3][5])//ok
{}
void test(int arr[][])//error
{}
void test(int arr[][5])//ok
{}
//对于一个二维数组,可以不知道多少行,但必须知道多少列,即行数可以省略,列数不可以省略
void test(int* arr)//整型指针 error
/{}
void test(int* arr[5])//指针数组 error
{}
void test(int (*arr)[5])//ok
{}
void test(int** arr)//要接收的是数组的地址,而二级指针是接收一级指针的地址的 error
{}
int main()
{
int arr[3][5] = { 0 };
test(arr);//二维数组数组名是首元素的地址,即第一行的地址(一维数组的地址),类型是数组指针
return 0;
}
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
//一级指针p,传给函数
print(p, sz);
return 0;
}
思考:当一个函数的参数部分为一级指针的时候,函数能接收什么参数?
void test(int* p)
{}
int main()
{
int a = 10;
int* p = &a;
int arr[10];
test(&a);
test(p);
test(arr);
return 0;
}
函数可以接收的参数:变量的地址、一级指针变量、一维数组的数组名
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** ptr;
int* pp;
int* arr[10];
test(ptr);
test(&pp);
test(arr);
return 0;
}
函数可以接收的参数:二级指针变量、一级指针变量的地址、指针数组的数组名
看以下的代码:
void test()
{
printf("hehe\n");
}
int main()
{
printf("test:%p\n",test);
printf("&test:%p\n",&test);
return 0;
}
输出的结果:
test: 005913CA
&test: 005913CA
int (*p)(int,int) = &test;
int (*p)(int,int) = test;
int Add(int x,int y)
{
return x + y;
}
int main()
{
int (*pf)(int,int) = &Add;//pf是一个存放函数地址的指针变量 - 函数指针
//int (*pf)(int x,int y) = &Add;//x和y写上或者省略都是可以的
//int (*pf)(int,int) = Add;
int ret = (*pf)(2,3);
//int ret = Add(2,3);
//int ret = pf(2,3);
//int ret = (******pf)(2,3);//(*pf)中,*可以省略,或者无论加多少颗*都可以,结果都一样
printf("%d\n",ret);
return 0;
}
int (*pf3) (int x, int y)
| | ------------
| | |
| | pf3指向函数的参数类型和个数的交代
| 函数指针变量名
pf3指向函数的返回类型
int (*) (int x, int y) //pf3函数指针变量的类型
---------------------------------------------------------------------------------------------------------------------------------
int* arr[3][3] = {1};
int p = **arr;
printf("%d",p);
二维数组数组名是首元素的地址,即第一行的地址(一维数组的地址)
*arr = *(arr+0) = arr[0] ----> 第一行
**arr = *(arr[0]+0) = arr[0][0] -----> 第一行第一个元素
所以打印的结果为:p = 1
( *( void (*)() ) 0 )();
该代码是一次函数调用,调用0地址处的一个函数;首先代码中将0强制转化为类型为 void (*)() 的函数指针,然后去调用0地址处的函数
void ( *signal( int, void(*)(int) ) )(int);
该代码是一次函数的声明,声明的函数叫signal;signal函数的参数有2个,第一个是int类型,第二个是函数指针类型void (*)(int),该函数指针能够指向的那个函数的参数是int,返回类型是void
signal函数的返回类型是一个函数指针void (*)(int),该函数指针能够指向的那个函数的参数是int,返回类型是void【 void (*)(int) signal( int, void(*)(int) ,类似于 void test(int x,int y);】
typedef unsigned int uint;
//将unsigned int 重命名为uint
typedef int* ptr_t;
typedef int(*parr_t)[5]; //新的类型名必须在*的右边
typedef void(*pfun_t)(int);//新的类型名必须在*的右边
那么我们就可以简化5.3.2中的代码:
void ( *signal( int, void(*)(int) ) )(int);
typedef void (*pf_t)(int);//将函数指针类型void (*)(int)重命名为pf_t
pf_t signal(int,pf_t);