指针进阶部分
1、指针的概念
内存单元的编号叫做地址,地址又被称为指针
指针的本身就是地址
1、指针就是一个变量,用来存放地址 我们口语中所说的指针其实就是指针变量 地址
2、指针的大小是固定的,是4/8个字节 大小
3、指针是具有类型的,指针的类型决定了指针走一步的步长 指针类型
4、指针的运算是我们最需要掌握的 🚩🚩🚩指针运算
2、字符指针
字符指针类型:char*
int main()
{
char ch = 'w';
char* pc = &ch;
printf("%c\n", *pc);
//这里的pc是一个字符指针变量,保存的就是字符w的地址
// 只要对pc进行解引用,就能够获取pc中的内容
// *代表的 pc是个指针
//*pc = 'w';
return 0;
}
这里的pc是一个字符指针变量,保存的就是字符w的地址,
只要对pc进行解引用,就能够获取pc中的内容
还有另外一种使用方法
int main()
{
char* pstr = "hello world";
//这里是把hello world 这个字符串放到pstr指针变量中了吗?
puts(pstr);
return 0;
}
通过对pstr中的内容进行监视,可以看到,pstr指向的是char类型的字符h,也就是说,在pstr中存放的是hello world中 首字符的地址
int main()
{
const char* pstr = "hello world";
//这里是把hello world 这个字符串放到pstr指针变量中了吗?
puts(pstr);
return 0;
}
因为hello world是一个常量字符串,是不可以被修改的,强行修改会报错,加const可以防止被修改
在char arr[10]=“abcdef”中 是把abcdef的内容全部放到arr中
有一道这样的面试题
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
char *str3 = "hello bit.";
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;
}
str3和str4都是常量字符串,常量字符串没必要存在多份,在内存中只占一个空间,所以str3和str4指向同一块空间
str1和str2是两个独立的数组,想要村的下内容,就要分别开辟对应的内存空间
3、指针数组
整型数组是存放整型的数组
字符数组是存放字符的数组
而指针数组就是存放指针的数组
数组名是首元素地址,将数组名存入到整型指针数组
1、*(parr[i]+下标)<==>parr[i][j]
2、不是二维数组,因为创建的三个数组不一定是连续存放的
4、数组指针
数组指针是指针
数组指针和指针数组很好分辨,就是看哪个在后面
int* p 是指向整型的指针
char * p是指向char类型的指针
数组指针就是指向数组的指针
int *p1[];----先和[]结合 ,p是数组名,剩下的是类型 所以p1的类型就是int [10]
int (*p2)[10];----先和*结合,说明p2是一个指针变量,然后指针指向的是一个大小为10个整型的数组,所以p是一个指针,指向一个数组,叫数组指针
---因为[]的优先级要高于*,所以必须要加上()来保证p先和*结合
再论数组名
&数组名和数组名
int arr[10];
1、sizeof(arr)单独放一个数组名,表示整个数组,计算的是整个数组的数组大小。
2、&数组名,这里的数组名表示的仍然是整个数组,所以&数组名取出的是整个数组的地址。
实际上,&arr表示的是数组的地址,而不是数组首元素的地址 &arr+1跳过的是整个数组的大小
数组指针的使用
既然数组指针指向的是数组,那么数组指针中存放的应该是数组的地址。
栗子🌰:把一维数组的地址存放到数组指针中去
🚩数组指针错误示范
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
int i = 0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{
printf("%d ",*(*p+i));
//p指向数组,*p其实相当于数组名,数组名又是数组首元素的地址,所以*p本质上就是数组首元素的地址
}
return 0;
}
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int*p = arr;//把数组arr的首元素的地址赋值给数组指针变量p
int i = 0;
int sz=sizeof(arr)/sizeof(arr[0]);
for(i=0;i<sz;i++)
{
printf("%d ",p[i]);
}
return 0;
}
相比较于第一种把一维数组的数组地址放到数组指针中去的做法,第二种把数组首元素的地址放到一个指针变量中去的做法是比较合理的 第一种方法实现起来就有些别扭
所以,我们一般都是把数组指针运用到二维以上数组的应用中去。
对二维数组的数组指针的使用
#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print_arr2(int (*arr)[5], int row, int col)
{
int i = 0;
for(i=0; i<row; i++)
{
for(j=0; j<col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10};
print_arr1(arr, 3, 5);
//数组名arr,表示首元素的地址
//但是二维数组的首元素是二维数组的第一行
//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
//可以数组指针来接收
print_arr2(arr, 3, 5);
return 0;
}
小练习
下面代码表示什么
int arr[5];
int *parr1[10];
int (*parr2)[10];
int (*parr3[10])[5];
1、一维数组,数组名为arr,数组有5个元素,每个元素是int类型。
2、指针数组,数组名为parr1,数组有10个元素,每个元素是int*类型。
3、数组指针,指针变量名为parr2,类型为int (*)[10]。
4、数组指针数组,parr3[10]是一个数组,数组元素的类型为int (*)[5]
数组传参
一维数组传参
int arr[10];
可以用什么参数接收?
int arr[];✓
int arr[10];✓
int *arr;✓
int *arr2[20];
可以用什么参数接收?
int *arr[20];✓
int **arr;✓
二维数组传参
int arr[3][5];
可以用什么参数接收?
int arr[3][5];✓
int arr[][5];✓
int (*arr)[5];✓
不可以用什么参数接收?
int arr[][];----只能省略第一维的大小,因为二维数组在内存中是连续存放的,不关心它有多少行,只关心它一行能够放多少个元素
int arr[5];----只是一个指针数组,和二维数组的类型都不一样,所以不能这样传
int **arr;----一维数组的地址不能存放到二级指针中去
思考1:当一个函数的参数部分为一级指针的时候,函数能够接收什么参数?
int a = 10;
当传过去的是整型变量a的地址的时候,函数的参数部分可以为一级指针
int arr[10];
当传过去的是数组的地址的时候,函数的参数部分可以为一级指针
思考2:当函数的参数为二级指针的时候,可以接收什么参数?
int*p1;
当传过去的是一个一级指针变量的地址的时候,函数的参数部分可以为二级指针
int **p2;
当传过去的是一个二级指针的时候,函数的参数部分可以为二级指针
int *arr[10];
当传过去的是一个指针数组的数组首元素地址的时候,函数的参数部分可以为二级指针
函数指针
函数指针,顾名思义就是指向函数的指针
在对函数指针进行间接访问之前,必须要把它初始化为指向某个函数
int f(int);
int (*pf)(int) = &f;
有三种方法可以调用这个函数
int ans;
ans = f(25);----简单使用f调用函数f
ans = (*pf)(25);对pf执行间接访问操作
ans = pf(25);
//代码1
( * (void (*)() )0 ) ();
----把0强制类型转换为函数指针类型 void(*)() 无参,返回值是void的函数的地址
调用0地址处的这个函数
//代码2
void ( *signal(int , void(*)(int) ) )(int);
signal是一个函数名,以上代码试一次函数声明
声明的signal 第一个参数类型为int,第二个参数的类型为函数指针,该函数指针指向的参数为int,返回值类型为void,signal函数的返回值也是一个函数指针,该函数指针的参数为int,返回值为void
函数写出来就会指派地址
如何简化
typedef void(*pfun_t)(int);
---=这个代码相当于把pfun_t重新定义为void (*)(int)
----下面是简化以后的代码
pfun_t signal(int, pfun_t);
函数指针数组通常用作转移表
回调函数涉及qsort函数的实现
指针和数组笔试题解析