指针基本概念:
1.指针是变量,用于存放地址,地址唯一标识一块空间
2.指针类型的不同,决定指针变量++后跳过的步长
3.指针的大小固定,4/8个字节(32/64位平台)
4.指针运算
字符指针基本格式: char*
字符指针的一般使用:
这种使用比较常见,也比较简单
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
另一种是关于指针常量的用法:
int main()
{
const char* pstr = "hello,bit";
const char* pstr1 = "hello,bit";
char pstr2[] = "hello, bit ";
char pstr3[]="hello,bit";
printf("%s", pstr);
printf("%s", pstr2);
return 0;
}
两种方式通过打印起到的效果一样
pstr和pstr1,pstr2和pstr3类型相同,是为了后面方便观察与字符数组的区别
const char* pstr 中存放的只是“h”的地址
char pstr2 [] ,变量名是首元素地址,但是确是将整个字符内容存放到了
数组中。
两个常量字符指针的地址空间相同,而两个数组的空间不同。
需要说明,常量数据与普通变量存放的空间区域不同,常量数据存放在了常量区,而这里的数组数据放在了栈区,将相同的常量字符赋值给不同的字符变量指针,这两个不同的字符指针指向的是同一块地址,而对于普通变量,即使存放的内容相同,但是还会开辟不同的内存空间。
格式:Type* arr[]
首先指针数组是一个数组,其中的元素都是指针。
就像普通的数组类型,int [] 中的元素都是int,char [] 中的元素都是char。
指针数组的类型如char* [ ] 、int * [ ] 、double* [ ]等,区别就是数组元素类型的不同。
通过一段代码来通过一维指针数组模二维数组:
int main()
{
char str1[] = "zhangsan";
char str2[] = "lisi";
char str3[] = "wangwu";
char str4[] = "laoliu";
char* pstr[4] = { str1 ,str2,str3,str4 };
return 0;
}
数组名是首元素地址,这里将四个数组的首元素地址放到一个char类型的指针数组中,效果如下图:
需要说明,数组开辟的是一块连续空间,指针数组pstr中的空间是连续的,但是str1、str2、str3、str4之间的空间不是连续的。
就像普通的数组一样,可以通过下标对数组中的各个元素进行访问:
格式:int(*) [ ]
char(*)[ ].....
这么理解:
(*+变量名)表示我是一个指针
int (*+变量名)[]表示我指向的类型是int []
数组指针是一个指针,指向的整个数组的空间,开篇说到,指针类型的不同,
决定了指针++或者--跳过的步长。
两个地址相同,但是++后再看一下区别:
很显然,arr+1跳过了4个字节,而&arr+1跳过了40个字节
所以我们说&arr取的是整个数组的空间
那么用什么类型的变量保存这样的地址呢????
很显然就是这里所说的数组指针
效果相同!
数组指针的使用一般可用于二维数组传参:
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;
}
print_arr2(arr, 3, 5);
arr是一个二维数组,传递的参数arr其实是首元素地址,即arr[0]的地址;
arr[0]是一个一维数组,一维数组的地址传给函数,这时形参可以用一个数组
指针接收;
下面是数组传参的总结。
int arr[] 可以,可以不指定大小
int arr[10],当然可以
int* arr ,可以,一维数组传参,传递的实际上就是首元素地址,用一个指针当然可以接收
int* arr[20],可以,形参与实参类型相同
int** arr,可以,实参是一个指针数组,每个元素都是指针,我们传递的是首元素地址,即一个地址的地址,当然可以用二级指针接收。
int arr[3][5]:可以,形参与实参类型相同
int [][]:不可以,二维数组类型创建的时候,可以省略行,但是不能省略列
int[][5]:可以
int *arr:不可以,二维数组传递的实质是&arr[0],是一个一维数组,类型不匹配
int * arr[5]:不可以,arr实际上是一个数组指针,形参类型是指针数组,一个数组当然不可以接收一个指针
int (*arr)[5]:可以,形参是数组指针,实参也是数组指针
int **arr:不可以,int**的意思是arr变量的内容是一个地址,该地址指向一个int整型,而传递的实参是一个指向数组的地址,类型补匹配。
函数指针类型:returnType(* pf)(eType1,eType2,....);
returnType 是要指向的函数的返回值类型;
eType是指向的函数的参数类型;
先上一段代码:
int ADD(int x, int y)
{
return x + y;
}
int main()
{
ADD(3, 5);
printf("%p", ADD);
printf("%p", &ADD);
return 0;
}
从控制台可以看出来,函数名与&函数名是同一个东西,所以,函数名的实质就像数组名一样,是一段地址。
const int* p;
int const* p;
int* const p;
对于常量指针与指针常量不容故意分辨,这样理解:
总体分为两类:
const在*左边;
const在*右边;
const在*左边:
const int* p 或者 int const* p 是常量指针
把const和int去掉,原表达式只剩下*p,所以这里const修饰的*p,
*p,就是我们所说的解引用p,内容就是p中的地址所指向的内容,
所以常量指针,该指针的内容不可以改变,而p可以改变。
const在*右边:
把表达式中,const前的内容删去,int* const p,变成p
即const修饰的是p,p是一个指真,p的内容不能被修改,但是*p可以被修改。