关于C语言中static,const,volatile与typedef的一些总结

        最近开始要找工作,开始不停的做笔试题,在这个过程中发现不少笔试题对C语言的基础都十分重视,几乎关键字的考察基本都会涉及到。于是就开始把一些C语言中的常考到的关键字都总结一下,一方面方便自己学习的时候可以查阅,另一方面也希望可以帮助到其他人。好的,废话不多说,内容正式开始……
1、关键字static的作用:
1)在函数体内,一个被声明为静态的变量在这一函数调用的过程中只会被初始化一次,该变量存放在静态变量区,而且并不会因为函数的退出就释放其内存,其生命周期为整个程序生命周期。
2)被声明为static的全局变量,仅在当前文件中可见,对于其他文件则无法实现对该变量的访问。
3)static也可以修饰函数,表示该函数仅能在当前文件中被使用,不能在其他文件中被调用。
4)static修饰的变量只会被初始化一次

例:
#include <stdio.h>
void func_1(void)
{
	static int i = 100;
	i++;
	printf("%s:i=%d\n"__func__,%d);
}
void func_2(void)
{
	int i = 100;
	i++;
	printf("%s:i=%d\n"__func__,%d);
}
int main(void)
{
	func_1();
	func_2();
	func_1();
	func_2();
	func_1();
	func_2();
	return 0;
}

运行结果是:


2、关键字const的作用
1、在C语言中,const修饰的变量是只读的,其本质仍然是变量。这与常量不同,常量存放在内存的只读区域,本质上是不允许修改的,但是被const修饰的变量在内存中仍然占用空间,仍然是有办法修改的,但是编译器并不允许你去修改它的值
2、 const是给编译器看的,只有在编译期间有效。一旦程序运行起来,const便失去作用,如果发生中断,在中断处理函数中仍然可以改变该变量的值
3、const并不会影响变量的取值范围,也不影响变量的算术属性。换言之,所谓的const int或者int const都是int类型,只是它们比一般的int类型变量多了"只读"属性。
 const的常用用法:
	const int i;	//定义一个整型只读变量i,其值不能以左值的方式进行修改
	int const i;	//这种定义方法与上一种方法是等价的

 易混用法:

	const int* p;	//p可变,p所指向的内容不可改变
	int const* p;	//这种用法与上面相同
	int* const p;	//p不可变,p所指向的内容可变
	const int* const p;	//p与p所指向的内容均不可变

简单的记为:左数右指

        当const修饰*号左边时,说明该变量是一个常量指针,其指向的数据不可改变

        当const修饰*号右边时,说明该变量是一个指针常量,其地址不可改变

这里需要注意的一点是,被const关键字所修饰的变量不能以左值的方式直接修改,但仍然可以使用指针的形式间接的修改,请看下面代码

#include <stdio.h>
int main(void)
{
	const int i = 100;
	int* p = (int*)&i;
	printf("i=%d\n",i);
	*p = 200;
	printf("i=%d\n",i);
	return 0;
}

运行结果是:


const也可用于修饰函数参数表示在函数体内不希望改变参数的值。


3、关键字volatile的作用

        在《C程序设计语言》一书中,对volatile的描述是这样的:"volatile用于强制某个实现屏蔽可能的优化。例如,对于具有内存映像输入/输出的机器,指向设备寄存器的指针可以声明为指向volatile的指针,目的是为了防止编译器通过指针删除明显多余的引用。"这样可能难以理解,所以通过以下一个例子来说明:
#include <stdio.h>
int main(void)
{
	int obj = 100;
	int a = 0;
	int b = 0;
	a = obj;
	sleep(5);		//硬件中断发生处
	b = obj;
	printf("a=%d\n",a);
	printf("b=%d\n",b);
	return 0;
}
在上述代码中,由于obj变量在声明并定义后没有再做为左值,为了提高效率,编译器对它进行了一次优化。它将这个变量从内存中读到寄存器中,并在
寄存器中保留了这个变量的备份。程序每次使用obj变量给a和b两个变量赋值时,程序直接从寄存器中读取obj的备份。由于访问寄存器的速度要远远大于访问内存的速度,这样在提高程序运行效率的同时也带来了安全隐患。假设在程序中sleep(5)语句处发生了一次中断,在中断服务程序中,obj的值被改为200了。由于程序已经被优化,在读取obj时并不是从内存中(obj=200)读取obj变量,而是直接从寄存器中(obj=100)读取obj变量之前的拷贝。那么上述代码运行完后的a、b的值均为100。
通过上面的例子,我们可以知道volatile关键字的作用是强制编译器每次都从内存中读取变量的值,而不去优化变量。
在实际应用中经常用于:
1、被中断处理函数修改后仍要供其他程序访问的变量
2、多线程编程中共享的变量
3、在嵌入式编程中,对带有存储器映射的硬件寄存器通常也需要使用volatile声明,因为每次对硬件寄存器的读写都可能有不同的意义。

在笔试中关于volatile的问题有:
1.Q:const与volatile能修饰同一个变量吗?为什么?
A:可以,const和volatile的语义并不冲突。const表示该程序不应该试图去修改这个变量,而volatile表示该变量可能会被未知的因素所修改,因此每次读取该变量都应该从内存中去读取。这个问题中最常见的例子是在嵌入式编程中经常出现对只读状态寄存器的访问。
2.Q:volatile能修饰指针吗?
A:可以,在嵌入式中经常会有这样的语句:
#define GPH0CFG   (*((volatile unsigned int*)0xE0200C00))	//一个宏定义,GPH0CFG是寄存器的名字

3.Q:以下这个函数能实现求函数的平方吗?如果不能,请回答出它存在什么问题?

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

A:这个函数不一定能求平方。虽然在C语言中((*ptr)*(*ptr))是一个语句,但在汇编中仍是分为几步执行的。如果程序在将*ptr的值使用MOV指令读入寄存器后,突然发生了一次中断,而中断处理函数又修改了*ptr的值。那么再将*ptr使用MOV指令读入寄存器就不再是原来的值了。这样返回的值自然也就不是原来传入参数的平方了。

4关键字typedef
        typedef是C语言中用来建立新的数据类型名的。它的用法是这样的:typedef int Length;这样Length就与int具有同等意义,可以使用Length来进行类型声明,类型转换等,它和类型int是完全相同的。但是这里有一点需要注意: typedef并没有创建出一个新的类型Length出来,它只是为某个已经存在的类型添加了一个别名而已。typedef也并没有赋予Length任何新的语义:使用Length定义的变量与使用int定义的变量完全相同。事实上,typedef有些类似于#define语句,但是由于typedef是由编译器解释的,因此它的文本替换功能要强于预处理器的能力。
在实际应用中,使用typedef有3个好处:
1、简化表达式,降低编程中出错的机会,例如:
typedef int (*PFI)(char *,char *);
PfI strcmp,numcmp;
2、可以使程序参数化,以提高程序的可移植性。如果typedef声明的数据类型与机器有关,那么当程序移植到其他机器上时,只需修改typedef类型定义即可。最常见的是标准库中的size_t和ptrdiff_t等
3、提高程序的可阅读性,便于维护。
关于typedef的一个问题:
Q:阅读以下代码。这段代码哪里运行会出错?为什么?
#include <stdio.h>
#define Pchar char*
typedef char* pStr;
int main(void)
{
	char string[8] = "Phoenix";
	const char* p1 = string;
	char* const p2 = string;
	const pStr p3 = string;
	pStr const p4 = string;
	const Pchar p5 = string;
	Pchar const p6 = string;
	p1++;	//语句1
	p2++;	//语句2
	p3++;	//语句3
	p4++;	//语句4
	P5++;	//语句5
	p6++;	//语句6
}
A:程序中的语句2,语句3,语句4和语句6会报错,因为这4个语句尝试去修改一个指针常量的地址。在这个程序中,关于const单独修饰的变量的问题在文章上部分已经讲过,这里不再重提。这里主要说一下#define语句和typedef跟const的问题。首先是#define语句。这是一个宏定义,在程序的运行过程中由预处理器处理。在程序中直接使用char*对Pchar进行文本替换,所以下面两个语句跟其后面的注释是等价的的:
	const Pchar p5 = string;	//等价于const char* p5 = string;
	Pchar const p6 = string;	//等价于char* const p6 = string;
而在文章的上面也提到了typedef是由编译器处理的,它并不是单纯的进行文本替换。它为char*类型声明了一个别名。让我们来进行一下类比:
	const pStr p3 = string;		//修饰的是变量p3,而p3是一个指向char类型变量的指针变量,它指向的内容(也就是string的首地址)不可改变
	pStr const p4 = string;		//修饰的是变量p4,而p4是一个指向char类型变量的指针变量,它指向的内容(也就是string的首地址)不可改变
上面两个语句有没有让你联想到const的某种用法?下面让我们来进行一下类比:
	const int a = 1;	
	int const b = 1;
上述两条语句其实和使用pStr定义的语句在形式上是一样的。变量其实只是一块内存而已。const修饰了这个变量,就表示这块内存中的内容是不可改变的。同样的,在使用pStr定义的语句中,const修饰的是一个指针变量p3,在这块内存(p3)当中,它所存放的内容是string数组的首地址,这个内容也是不可改变的。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值