C语言中指针的定义和在不同环境下作为形参的用法以及指针类型的强转

来源:互联网 发布:mac qq怎么退出讨论组 编辑:程序博客网 时间:2024/04/29 03:08

1. 指针的基础知识

指针的定义:指针的全称是指针变量。

指针本质上是一种类型,和int型,float型,double型,char型一样的,这种类型叫指针类型,定义的变量叫指针变量。

int *p;      这句表达式的意思是定义了一个变量p,*符号表示我定义的这个变量p是指针类型的,而前面的int表示我定义的这个指针变量p指向一个整型,指针变量p的值就是p所指向的这个整型变量的地址,所以给指针赋值时是一个地址值。


&: 取地址符号,在变量前面加上&,则这个整体表示这个变量的地址

*:在变量定义时使用*表示我定义的是一个指针类型变量   在其他地方使用时表示我对指针变量所指向的那个变量地址中存储的数据进行操作。例如 int a = 5; int *p; *p =& a


int a = 5;            定义了一个整型变量a,a初始化赋值为5

p = &a;               a是一个整型变量,a前面加上&取地址符号,则&a这个整体表示这个变量a的地址,将这个地址赋值   给指针变量p,也就是说指针变量p的值就是指针变量p所指向的int型变量a

2.指针变量的运算

指针本质上是一个变量,只不过这个变量是指针类型的,和int型等变量类型并没有什么本质的区别。既然是变量就可以 + - * /  % 这些算数运算。只不过指针类型变量的值是一个地址 * / % 等这些运算可以,但没有实际意义的。最常用的使用方法是对指针的+  - 操作。结合数组值操作的方法看最明显:

int a[4] = {100,200,300,400};    定义一个整型数组,这个数组有五个整型类型数据

int *p;   定义一个指向整型变量的指针变量p,p的值是指向的那个整型变量的地址。

p = a; 数组名做右值时的含义是数组名就是数组首元素的地址,也就是a[0]的地址,a和&a[0]等价的都表示数组元素的首地址

p 指向 a[0]的首地址                *p的值就是a[0]

p+1指向a[1]的地址     *(p+1)的值就是a[1]      

p+2指向a[2]的地址     *(p+2)的值就是a[2]

以*(p+1)来说,()优先级最高,所以先运算()的表达式,p+1表示对p所指向的那个变量的地址进行+1操作,这里+1并不是对地址值进行+1,而是对地址进行增加了4,这个1的含义是增加了p指向的变量类型的大小,p所指向的变量是整型类型的,整型类型的大小是占用4个字节数据大小,所以增加了4。以下表为例:

*p=a[0]            *(p+1)=a[1]   *(p+2)=a[2]    *(p+3)=a[3]    

a[0] =100a[1]=200a[2]=300a[3]=4000x1000        0x1004         0x1008          0x100c

p                  p+1              p+2                p+3


通过上表可以看出 p=&a[0]; &a[0]的地址值是0x1000,则p的值是0x1000。 p+1的值是0x1004,不是0x1001,所以这个+1的值其实是int在内存中所占用的长度,也就是4字节。

p = &a[0]; 

printf("*p=%d\n",*p);  *p的值是p所指向的变量的地址存放的值。p所指向的变量是a[0],p所指向的变量的地址是0x1000,0x1000地址存放的值是100,所以*p = 100;

*p = 200; 相当于修改了p所指向的变量的地址存放的值,也就是0x1000存放的100的值改为200,也就是a[0]的值是200.

*(p+1)和*p的含义是一样的。首先判断优先级最高的也就是()里的,p是一个指针变量,p的值是p所指向的变量的地址,p所指向的变量是a[0],a[0]的地址是0x1000,所以p的值就是0x1000, p+1就是p的值在原来的基础上增加了sizeof(int)个字节也就是4字节,所以p+1=0x1004,也就是说p+1的值就是&a[1],所以p+1指向整型变量a[1],p+1的值就是a[1]的地址。*在运算表达式中的含义是对指针所指向的变量的地址的值进行操作,而现在p+1指向整型变量a[1],所以

*(p+1) = a[1],也就是说*(p+1) = 200;后面的运算以此类推。


3.指针类型的强制转换

int a = 0x11223344;

char *p;

p = &a; 

分析上面这段代码。指针变量p是指向一个char型的变量,而在给指针变量赋值时p所指向的变量a的类型是int型的,这种赋值方法是不合理的,编译器会报警告或者错误,在实际项目中如果使用这种方法赋值,则会出现不可预估的风险。只有重新定义指针变量p,使指针指向一个整型变量。如下

int *p;

p = &a;

则*p的值就是0x11223344;


加入我们按一开始的方法定义,只不过我们需要单独取出0x11,0x22,0x33,0x44这四个值该咋么操作呐?这就涉及到指针类型的强制转换了。如下操作:

int a = 0x11223344;

char *p;

p=(char *)&a;

分析:()的优先级是最高的,所以最先运算是从()内部开始的 ,char *的意思是将指针所指向的变量类型强制转换为char型,指针p所指向的变量是a,a的类型是int型,将int型强制转换为char型。指针变量p的值本身是没有变化的,还是a的地址值&a;唯一发生变化的是指针变量p所指向的变量a的类型给强制转换了。那*p的值是多少?

强制转换后p所指向的是一个char类型的变量,所以*p的值是一个char型的值。通过下表分析:


0x110x220x330x44

0x1000        0x1001        0x1002        0x1003

p                  p+1             p+2              p+3


p = (char*)&a;     p的值还是a的地址,就是p的值是0x1000;但是p所指向的变量也就是a的类型强制转换为char型,a在内存中还是按照整型数据存储的,但我们取值的时候是按照char型取出的。

说以*p取值时取的是一个char型变量,也就是0x11,

*(p+1) 这里+1的意思是地址+sizeof(char)个字节,不是+sizeof(int)个字节,因为我们在指针赋值时强制将指针所指向的变量类型转换为char型,所以p+1的值是0x1000+1就是0x1001,而0x1001的地址存放的值是0x22,所以

*(p+1) = 0x22;

*(p+2) = 0x33;

*(p+3) = 0x44;

这种操作一般用在函数传参时的类型强制转换,一般用在内存读写上。


注:一定要明白指针类型的强制转换的含义,强制转换的是指针所指向的变量类型,而不是指针类型,因为指针本来就是一个类型了。对指针进行+ -操作时,每次+1的值含义是地址偏移sizeof(char)字节,而不是sizeof(int)字节。


如何将一个有意义的整型变量转换为一个地址?


char *p;

int a = 0x1200;

p = (char* )a;

分析:将a强制转换为指针类型,则a的值由一个int型的值转换为一个地址值了,这个p指针变量指向char型变量。

举例:

void ReadFlash(uint32_t addr, uint8_t *ptr, int len)

{

int i;

for(i = 0; i < len; i++)
{

*ptr++ = (char *)addr++; //将整型addr的值赋值给ptr指针,则ptr的地址值就是addr的值,ptr所指向的变量

//char型的,也就是ptr取值时是按照char数据类型大小来取值的。

}

}

#define BUFF_LEN      (100)

int main(void)

{

uint8_t buff[BUFF_LEN] = {0};

ReadFlash(0x10000,buff, BUFF_LEN);

}


整型变量强转为地址值时用的很少,但在内存中取值时这种用法有时候是必须的。


4.函数传参的俩种方式,传值和传地址

调用函数时经常要给函数的形参列表来传递参数,将实参传递给形参有俩种方法

第一种:传值

调用函数时将实参的值传递给形参,实际上是将实参的值拷贝一份传递给形参,而实参并没有参与调用函数的运算,所以传值传参是不会改变实参的值的,因为是将实参值拷贝了一份传给形参。

第二种:传地址

实参向形参传地址,则形参是指针类型的,将地址赋值给形参,则形参和实参共用同一片内存地址,也就是说通过传进来的地址对形参进行操作,会改变实参的值,因为形参和实参的地址是同一个,通过指针操作修改形参值,也就修改了实参的值。











阅读全文
0 0
原创粉丝点击