C语言指针
参考
示例
int p; //p是整型变量
int *p; //首先从p 处开始,先与*结合,所以说明p 是一个指针,然后再与int 结合,说明指针所指向的内容的类型为int 型.所以p是一个指向整型数据的指针
int p[3]; //首先从p处开始,先与[]结合,说明p 是一个数组,然后与int 结合,说明数组里的元素是整型的,所以p是一个由整型数据组成的数组
int *p[3]; //首先从p 处开始,先与[]结合,因为其优先级比*高,所以p 是一个数组,然后再与*结合,说明数组里的元素是指针类型,然后再与int 结合,说明指针所指向的内容的类型是整型的,所以p 是一个由指向整型数据的指针所组成的数组
int (*p)[3]; //首先从p 处开始,先与*结合,说明p 是一个指针然后再与[]结合(与"()"这步可以忽略,只是为了改变优先级),说明指针所指向的内容是一个数组,然后再与int 结合,说明数组里的元素是整型的.所以p 是一个指向由整型数据组成的数组的指针
int **p; //首先从p开始,先与*结合,说是p 是一个指针,然后再与*结合,说明指针所指向的元素是指针,然后再与int 结合,说明该指针所指向的元素是整型数据.
int p(int); //从p 处起,先与()结合,说明p 是一个函数,然后进入()里分析,说明该函数有一个整型变量的参数,然后再与外面的int 结合,说明函数的返回值是一个整型数据
Int (*p)(int); //从p 处开始,先与指针结合,说明p 是一个指针,然后与()结合,说明指针指向的是一个函数,然后再与()里的int 结合,说明函数有一个int 型的参数,再与最外层的int 结合,说明函数的返回类型是整型,所以p 是一个指向有一个整型参数且返回类型为整型的函数的指针
int *(*p(int))[3]; //从p开始,先与()结合,说明p 是一个函数,然后进入()里面,与int 结合,说明函数有一个整型变量参数,然后再与外面的*结合,说明函数返回的是一个指针,,然后到最外面一层,先与[]结合,说明返回的指针指向的是一个数组,然后再与*结合,说明数组里的元素是指针,然后再与int 结合,说明指针指向的内容是整型数据.所以P 是一个参数为一个整数据且返回一个指向由整型指针变量组成的数组的指针变量的函数.
指针
指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清:指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、指针本身所占据的内存区。
指针的类型
int*ptr;//指针的类型是int*
char*ptr;//指针的类型是char*
int**ptr;//指针的类型是int**
int(*ptr)[3];//指针的类型是int(*)[3]
int*(*ptr)[4];//指针的类型是int*(*)[4]
指针所指向的类型
int*ptr; //指针所指向的类型是int
char*ptr; //指针所指向的的类型是char
int**ptr; //指针所指向的的类型是int*
int(*ptr)[3]; //指针所指向的的类型是int()[3]
int*(*ptr)[4]; //指针所指向的的类型是int*()[4]
指针的值----指针所指向的内存区或地址
这个值将被编译器当作一个地址.在32 位程序里,所有类型的指针的值都是一个32 位整数,因为32 位程序里内存地址全都是32 位长。STM32F系列属于中低端的32位ARM微控制器。
指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。我们说一个指针的值是XX,就相当于说该指针指向了以XX 为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
指针本身所占据的内存区
在32 位平台里,指针本身占据了4 个字节的长度。
指针运算
char a[20];
int *ptr=(int *)a; //强制类型转换并不会改变a 的类型
ptr++;
指针ptr被加了1,编译器是这样处理的:它把指针ptr 的值加上了sizeof(int),在32 位程序中,是被加上了4,因为在32 位程序中,int 占4 个字节。由于地址是用字节做单位的,故ptr 所指向的地址由原来的变量a 的地址向高地址方向增加了4 个字节。由于char 类型的长度是一个字节,所以,原来ptr 是指向数组a 的第0 号单元开始的四个字节,此时指向了数组a 中从第4 号单元开始的四个字节。
&是取地址运算符
*是间接运算符
数组和指针
char *str[3]={
"Hello,thisisasample!",
"Hi,goodmorning.",
"Helloworld"
};
char s[80];
strcpy(s,str[0]); //也可写成strcpy(s,*str);
strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));
strcpy(s,str[2]); //也可写成strcpy(s,*(str+2));
str 是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str 当作一个指针的话,它指向数组的第0 号单元,它的类型是char **,它指向的类型是char *。
指针和结构类型
struct MyStruct
{
int a;
int b;
int c;
};
struct MyStruct ss={20,30,40};
//声明了结构对象ss,并把ss 的成员初始化为20,30 和40。
struct MyStruct *ptr=&ss;
//声明了一个指向结构对象ss 的指针。它的类型是
//MyStruct *,它指向的类型是MyStruct。
int *pstr=(int*)&ss;
//声明了一个指向结构对象ss 的指针。但是pstr 和
//它被指向的类型ptr 是不同的。
怎样通过指针ptr 来访问ss 的三个成员变量?
ptr->a; //指向运算符,或者可以这们(*ptr).a,建议使用前者
ptr->b;
ptr->c;
怎样通过指针pstr 来访问ss 的三个成员变量?
*pstr; //访问了ss 的成员a。
*(pstr+1); //访问了ss 的成员b。
*(pstr+2) //访问了ss 的成员c。
这样使用pstr 来访问结构成员是不正规的,所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个"填充字节",这就导致各个成员之间可能会有若干个字节的空隙。即使pstr 访问到了结构对象ss 的第一个成员变量a,也不能保证(pstr+1)就一定能访问到结构成员b。因为成员a 和成员b 之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。指针访问结构成员的正确方法应该是使用指针ptr 的方法。
指针和函数
非专业人员略过,详情请看参考
指针类型转换
指针的赋值语句要求赋值号两边的类型一致,所指向的类型也一致
float f=12.3;
float *fptr=&f;
int *p;
p=(int*)&f;
强制类型转换的结果是一个新指针,该新指针的类型是TYPE *,它指向的类型是TYPE,它指向的地址就是原指针指向的地址。
指针的安全
char s='a';
int *ptr;
ptr=(int *)&s;
*ptr=1298;
指针ptr 是一个int *类型的指针,它指向的类型是int。它指向的地址就是s 的首地址。在32 位程序中,s 占一个字节,int 类型占四个字节。最后一条语句不但改变了s 所占的一个字节,还把和s 相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。