嵌入式C语言中指针的应用(下)

深入学习数组

编译器角度理解数组

在编译器看来数组也是一个变量,在编译的时候会将一个变量名字和这段内存空间的第一个字节地址绑定,变量的类型决定了这段空间的字节数。在访问这段空间的时候一个方法就是使用变量名访问,但是变量名会受到作用域的限制,还有一种方式就是直接使用地址访问,并且不会受到作用域的限制。

从内存角度理解数组

首先明白一点,就是数组的空间之间是连续的,这样就代表了虽然数组中的内容也只能一个一个进行访问,但是由于其是连接在一起的,所以使用指针进行操作非常方便。实际上数组就是高效利用指针的例子。

一维数组中几个关键符号的理解

举例:int buf [100] = {0}为例,buf、buf[0]、&buf[0]、&buf这四个符号的内涵。
buf:一是数组名,二是元素的首字节地址,是一个常量值。
buf[0]:就是数组的首地址
&buf[0]:等价于buf,是首地址的地址值,是一个常量值。
&buf:标志数组首地址,是一个地址常量,同样只能作为一个右值。

printf(“%p/n”,&buf+1)和printf(“%p/n”,buf+1)是完全不一样的&buf代表的整个数组的首地址,加一是加的是整个数组空间,数组首地址主要用于构造多多维数组。buf代表的数组第一个元素的首字节地址,加一是加的一个元素空间的大小。

如何使用指针访问数组

1.利用下标进行访问

int i = 0;
for (i = 0;i<sizeof(buf)/sizeof(buf[0]);i++)

2.利用指针常量进行访问

int i = 0;
for (i = 0;i<sizeof(buf)/sizeof(buf[0]);i++)
{
    printf("%d\n", *(buf+i));
}

3.利用指针变量进行访问

int i = 0;
int *p = buf;
for (i = 0;i<sizeof(buf)/sizeof(buf[0]);i++)
{
    printf("%d\n", *(p+i));
}

*(p+1)可以写成p[i],也可以写成 * (p++),因为p是变量。
下标法访问数组,是C语言为了让那些指针基础较差的初学者快速使用数组的一种人性化设计,其实下标法本质上是使用地址进行访问。

从内存角度理解指针访问数组的实质

因为数组中各个元素空间在内存中是相连的,因此空间地址也是连续的,而且每个元素的类型相同,因此每个元素的空间大小是一样的。以int buf [6]为例,每个元素都是int型,元素空间大小都是4个字节。因为数组的这个特点,就决定了只要知道其中一个元素的首地址,很容易就能推算出其他元素空间的首地址。

指针与数组类型的匹配问题

int buf[5]; int *p = NULL; p = buf;

上面写法都是正确的,因为p 是int *类型,而buf等价于&buf[0],地址类型也是 int ,int类型地址buf放在int *类型的指针变量p中,当然没有问题。

int buf[5]; int *p = NULL; p = &buf;

这个做法是不对的,因为p是*int类型,但是&buf是数组首地址,是int( *)[5]类型,所以是不对的。

总结:指针类型决定了指针是如何参与运算

1、指针在进行运算的时候,变量存放就是一个地址值,所以做的是地址运算。
2、地址+1,加的是一个数组元素空间的大小。

指针类型与强制类型转换

类型对于编译器来说,主要就是用于说明数据存储空间的大小以及数据的存储结构。对于我们的自定义结构,比如结构体,因为成员类型不同,成员排列顺序不同,成员数量不同,结构体类型的对齐方式的不同,需要的空间大小和存储的结构也是不同的。

指针变量数据类型的含义

指针的数据类型包含两个方面的类型:一是用于说明指针变量本身的类型,二是用于说明指向变量的类型。
举例:
int a = 10;
int *p = &a;
其中int *中的 *说明p是一个一级指针变量,用于存放地址值,其中的int表明p指针指向的空间是int类型的。

指针中的变量数据类型的强制转换

对于指针的强制转换主要涉及到两个方面,一是对指向空间的强制类型转换

int a ;
float b = 136.23;
int *pa = &a;
float *pb = &b;
*pa = (int)*pb;

这个是对于指向空间的强制转换类型

二是对指针变量本身进行强制转换
1、指针本身强制类型转换的本质,例子如下

int a;
int *pa = &a;
float *pb = NULL;
pb = (float *)pa;   //或者pb = (float *)&a;

本例中是将指针变量pa存放的地址转换为(float *)后,赋值给了pb。pb里面地址与pa里面的地址值是相等的,但是pa放的是(int *)而pb放的是(float )型。虽然它们都是指向同一个空间a,但不同的是当使用pb去使用变量空间a的时候,会以float型的空间大选哦和数据存储结构使用a空间的值。
因此总结起来就是,指针变量类型本身的强制转换,改变的是对其指向空间的引导方式,对于2级胡总和多级指针变量来说,道理是一样的。

2、指针本身强制类型转换需要注意一下问题
问题一:引用时空间大小发生变化

float a = 13.5;
double *pa = (double*) &a;
double b = *pa;     //只是读取空间内容的话,不会引起严重错误
*pa = 345.45;       //如果是写的话,很有可能引起严重错误

问题二:引用空间时,数据存储结构变化,例子如下 :

int a = 125;
float b;
float *pa = NULL;

pa = (float*) &a;
float b = *pa;
printf("%f\n" , b);  

指针、数组与sizeof运算符

sizeof的使用方法虽然和函数很像,但是实际上它只是c语言的一个运算符。所以使用sizeof运算符就是来判断当前变量/数据类型在当前环境中的内存字节数。
sizeof和strlen之间的区别:
char str[] = “hello”;
sizeof(str) :结果等于6,因为“hello”还包括‘\0’字符。
strlen(str) :结果等于5,这个strlen函数是遇到\0进行结尾,但是不包括‘\0’。

char str[] = “hello”;
char *p = str;
sizeof (*p) strlen( p)
sizeof(*p) : 返回结果为1,*p代表的是第一个元素str[0]的空间,空间大小为1.
strlen ( p) :返回结果为5,p里面存放的字符串首地址,strlen函数测试的是“hello”字符个数
sizeof ( p) : 为4,这个指针变量空间的大小。

int b[100];sizeof(b)
sizeof(b) : 返回结果为400(单位字节),,对于数组来说,b这个时候代表的就是整个数组空间,此处的含义不再是一个地址。

数组的传参

void fun (int buf[100])             //等价于int buf[] 和 int *buf
{
   printf("%d\n" , sizeof(buf));    //打印结果为4个字节,因为buf指的是一个指针变量
}

int main (void)
{
    int buf[100];
    printf ("%d\n" , sizeof(buf));  //打印结果为400,此时buf代表的是整个数组
    fun(buf);
}

对于数组来说,传参传递的都是数组的首元素首字节地址,其实就是一个普通变量的地址,目的是提高数组的传参效率。再C语言中,接收的数组形参允许int buf [100](100可有可无,只是起一个说明作用)来定义,原因是为了增加代码的可读性。传递数组本质上传递的是地址,与普通变量的地址没有任何区别。如果形参直接写成指针变量的形式,就无法区别传递的到底是数组还是一个普通变量的地址。

#define和typedef的区别

#define与typedef都可以用来给现有类型起别名,但是#define只是简单宏替换,而typedf
不是的,#define在预编译时被处理,typedef是在编译时被处理。

指针与函数传参

C语言中有两种传参方式,一种是传递普通值,两一种就是传递指针(地址)。实际上C语言中传递的都只是一个值,只是传递指针的时候,被传的值比较特殊,是一个地址,具有指向某个空间的作用。

普通传参

void fun(int val)  //本例中fun函数栈中开辟出val变量空间,存放传递过来的值
{
     printf("%d\n",val);
}
int main(void)
{
     int a = 100;
     fun(a);
     fun(1000);
     return 0;
}

在本例中,fun函数被调用的时候,会在自己的函数栈中开辟出名叫val整型参变量空间,调用fun函数时,被传值会写入val空间。需要注意的是,c语言中值传递的本质就是:被调函数将自己函数栈中开辟相同类型的形参空间,并且将传递过来的值写入形参空间保存

传递地址

C语言中地址传递的本质就是被调函数将自己函数栈中开辟相同的保存地址的空间,将传递过来的地址值写入空间中进行保存。

传递数组

数组传递没有普通值进行传递,只有地址传递。因为要进行普通值传递就要在函数栈中开辟相同空间斤进行存储,但是只传递成员的地址值就会节省好多空间。

传递结构体

四种传递方式:
1、成员值的传递
2、成员地址的传递
3、结构体整个的传递
4、结构体地址的传递

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值