第七节--指针专题二

一、字符指针与字符串

C语言通过使用字符数组来处理字符串
通常,我们把char数据类型的指针变量称为字符指针变量。字符指针变量与字符数组有着密切关系,它也被用来处理字符串。


初始化字符指针是把内存中字符串的首地址赋予指针,并不是把该字符串复制到指针中
char str[] = “Hello World”;
char *p = str;
在C编程中,当一个字符指针指向一个字符串常量时,不能修改指针指向的对象的值
char * p = “Hello World”;
*p = ‘h’; // 错误, 字符串常量不能修改


程序举例
不利用任何字符串函数,编程实现字符串连接函数的功能。


总结与思考
主要介绍了如何用字符指针操作字符串以及字符串常量相关内容
思考
字符指针操作字符串有什么注意事项

二、指针数组

所谓指针数组是指由若干个具有相同存储类型
和数据类型的指针变量构成的集合
指针数组的一般说明形式:
<存储类型> <数据类型> *<指针数组名>[<大小>];
指针数组名表示该指针数组的起始地址


声明一个指针数组:
double * pa[2] ,a[2][3];
把一维数组a[0]和a[1]的首地址分别赋予指针变量数组的数组元数pa[0]和pa[1]:
pa[0]=a[0] ; // 等价pa[0] = &a[0][0];
pa[1]=a[1]; // 等价pa[1] = &a[1][0];
此时pa[0]指向了一维数组a[0]的第一个元素a[0][0], 而pa[1]指向了一维数组a[1]的第一个元素a[1][0]。


初始化与赋值
在这里插入图片描述
指针数组处理二维数组示意图
在这里插入图片描述
程序举例
编程:利用指针数组处理一个二维数组,要求求出二维数组所有元素的和。

三、多级指针

多级指针的定义
把一个指向指针变量的指针变量,称为多级指针变量
对于指向处理数据的指针变量称为一级指针变量,简称一级指针
而把指向一级指针变量的指针变量称为二级指针变量,简称二级指针
二级指针变量的说明形式如下
<存储类型> <数据类型> ** <指针名> ;


多级指针的运算
指针变量加1,是向地址大的方向移动一个目标数据。类似的道理,多级指针运算也是以其目标变量为单位进行偏移。
比如,int **p;p+1移动一个int *变量所占的内存空间。再比如int ***p,p+1移动一个int **所占的内存空间。


多级指针和指针数组
指针数组也可以用另外一个指针来处理。

        例如:有一个一维字符指针数组ps[5]char *ps[5]= {  "Beijing city",
               	……
               	"London city"  } ; 
定义另一个指针变量pps,并且把指针数组的首地址赋予指针pps
 char *ps[5]={……}; char ** pps = ps;

在这里插入图片描述


总结与思考
主要介绍了多级指针,包括定义、初始化、运算以及多级指针与指针数组
思考
多级指针在内存中占几个字节?
多级指针加1,移动多少字节?


四、void指针和const

void指针-掌握
const修饰符-掌握


void指针
void指针是一种不确定数据类型的指针变量,它可以通过强制类型转换让该变量指向任何数据类型的变量
一般形式为: void * <指针变量名称> ;
对于void指针,在没有强制类型转换之前,不能进行任何指针的算术运算


程序举例
编程实现:使用void指针遍历一维数组

案例
void * malloc(size_t size);
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine)(void *), void *arg);
void qsort(void *base,  size_t nmemb,  size_t size,   int(*compar)(const void *, const void *));

const变量
常量化变量的值
一般说明形式如下:
const <数据类型> 变量名 = [<表达式>] ;
常量化变量是为了使得变量的值不能修改
变量有const修饰时,若想用指针间接访问变量,指针也要有const修饰。const放在指针声明的什么位置呢?


常量化指针目标表达式
一般说明形式如下:
const <数据类型> * <指针变量名称>[= <指针运算表达式>] ;
常量化指针目标是限制通过指针改变其目标的数值 ,但<指针变量>存储的地址值可以修改


常量化指针变量
一般说明形式如下:
<数据类型> * const <指针变量名称>[= <指针运算表达式>] ;
使得<指针变量>存储的地址值不能修改。但可以通过 *<指针变量名称> 可以修改指针所指向变量的数值


常量化指针变量及其目标表达式
一般说明形式如下:
const <数据类型> * const <指针变量名> = <指针运算表达式> ;
常量化指针变量及其目标表达式,使得既不可以修改<指针变量>的地址,也不可以通过*<指针变量名称>修改指针所指向变量的值


总结与思考
主要介绍了const修饰指针的用法,有三种情况。还介绍了void指针的用法
思考
void指针有什么作用?
main函数是否可以带参数(涉及到const指针)?

六、补充-解读关键字volatile

一.volatile是什么?

volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
二.编译器优化是什么意思?

int i = 5;
int a = i;
……
int b = i;

编译器发现两次从i读数据的代码之间,并没有对i进行过操作,它会自动把上次读的数据放在b中,而不是重新从i里面读;
三.哪些情况,改变了内存中变量的值,编译器不知道?

1.多线程应用中被几个任务共享的变量;
2.一个中断服务子程序中会访问到的非自动变量;
3.并行设备的硬件寄存器(如:状态寄存器);

四.何时需要使用volatile这个关键字?

嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用会经常要求定义为volatile类型的变量。

五.使用volatile带来什么好处?
volatile int nCount;
当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。

六.3个经典的volatile问题
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:

int square(volatile int *ptr)
{
return *ptr * *ptr;
}

下面是答案:
1). 可以是。例如对于只读的状态寄存器而言,如果它仅仅是volatile类型,那么它还是可能被意想不到的改变。但它还是const的时候,程序就不应该试图去修改它
2). 可以是的。
尽管这种情况并不常见,但它还是可以。一个例子就是:
当一个中断服务子程序企图去修改一个指向一个buffer指针的时候。
3). 这段代码可能有点恶作剧的味道。但它很好说明volatile类型参数的含义和作用。
这段代码的目的是用来返指针ptr所指向的值的平方,
但是,由于
ptr指向一个volatile型参数,编译器将产生类似下面的代码:

int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}

由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不
是你所期望的平方值!正确的代码如下:

long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}

七.总结

1.使用volatile关键字修饰的变量,可以避免编译器优化;
2.使用volatile关键字修饰的变量,每次都是重新读取内存中的值,而不是使用保存在寄存器里的值了;
3.编译器优化的做法是:
int i = 5;
int a = i;
……
int b = i;
编译器发现两次从i读数据的代码之间,并没有对i进行过操作,它会自动把上次读的数据放在b中,而不是重新从i里面读。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值