关于C语言的类型修饰符

 分享网址

http://wenku.baidu.com/link?url=e-xWNn7f84rrEf_vhHz5CQh2LCVmaGBTA6iB0BC8zPv_8eXz5SKmRofsSuenh8wn_JjeQZBD103xCA6wqDkYo9SzlIvKgCpYwwrbyNvbxVS

 

在一般的

C

教科书中,可以见到

6

种类型修饰符,分别是

: auto, const, register,  

static, volatile, extern. 

     

局部变量除非显式指明为

static, 

否则默认为

auto

,所以一般不会在代码中使用类型

 

修饰符

auto. 

     

在后编译器时代,优化器可以合理的分配寄存器,所以一般不会在代码中使用类型修

 

饰符

register. 

     extern

只用于声明全局变量,用法单一。

 

     

本节将主要介绍

const, static

volatile. 

 

 

1. const 

     

首先需要注意的是,

const

修饰的是在它前面的类型,如果它前面没有类型,那它修

 

饰的是紧跟着它的那个类型。

 

例如:

 

(a)const int i = 0; 

 (b)int const i = 0; 

是完全一样的。

 

(a)

中,

const

前面没有类型,它就修饰它后面的那个

int

类型。在

(b)

中,

const

修饰它前

 

面的

int

类型,两者没有任何区别。

 

再看另一个稍复杂一点的例子,下面两条语句却不相同:

 

(c)const int *pi = 0; 

/* 

相当于

int const *pi = 0; pi

是一个指向

const int

的指针,复引用此运算符为得到一

 

const int

的类型,该类型不能作为左值,在该语句后使用类似于

*pi = 1

的操作将导致

 

编译错误。但该变量本身并不具备

const

属性,可以使用

pi = &i

的操作。可用于访问只读

 

存储器。

*/  

(d)int* const pi = 0; 

/* pi

是一个指向

int

类型的

const

指针,复引用此运算符为得到一个

int

类型,该类型可以

 

作为左值,在该语句可以使用类似于

*pi = 1

的操作,但该变量本身具备

const

属性,使用

 

pi = &i

的操作将导致编译错误。可用于访问固定位置的存储器。

*/ 

再看一个更复杂的例子:

 

(e)const int* const pi = 0; 

/* pi

*pi

均不能作为左值。它只适合于读取某个固定位置的只读存储器

 */ 

 

const

还有下列典型用法

     * 

用于参数列表,通常修饰的是指针类型,表明该函数不会试图对传入的地址进行写

 

操作。例如:

 

void *memcpy(void *, const void *, size_t); 

     * 

用于返回值,通常是一个指向只读区域的指针。例如:

 

const datatype_t *get_fixed_item(int index); 

     * 

给固定不变的数据

(

例如码表

)

加上只读属性,在某些情况下可以减小

ram

的开销。

 

 

 

 

2.static 

     static

用于全局变量声明和局部变量声明具有完全不同的语义,不得不说,这是

C

 

言设计中的一个不合理之处。当

static

用于修饰全局变量声明

(

或函数声明,可以认为函数

 

声明就是声明一个指向代码段的指针,该指针的值最后由链接时决定,从这个意义上说,

 

函数声明也是一种全局变量声明

)

,它表示该变量具有文件作用域,只能被该源文件的代码

 

引用,不能被其他源文件中的代码访问。在编译时引起的实际变化是被

static

修饰的变量

 

不会被写入目标文件的输出节,在链接时解析其他模块中的未定义符号时不会被引用到。

 

它的反义词是

extern

 

例如:

 

------main.c--- 

extern int a(void); 

int main(){ return a(); } 

------a.c------ 

/* link will fail unless remove 

static

 modifier */ 

static int a(void) { return 0; } 

 

 

 

     

static

用于修饰局部变量声明,它表示该变量不是分配在该函数的活动记录中,而

 

是分配在全局的数据段

(

bss

)

中。简单的说,就是被

static

修饰的局部变量实际上并不

 

是局部变量,而是具有函数作用域的全局变量,除了只能在定义它的函数内访问外

(

这是由

 

C

语法决定的

)

,它的运行时特征和全局变量完全一样,函数返回不会影响它的状态,它的

 

初始化仅有一次,发生在程序的装载时,而不是在每次函数调用的时候初始化。它的反义

 

词是

auto

 

例如

下面这段函数返回自己被调用了多少次:

 

int callee(void) {  

 static int times_called = 0; 

 return (++ times_called); 

 

 

3.volatile 

     volatile

修饰符的作用是告诉优化器不能优化这个变量的读写操作,一定要为这个变

 

量的读写操作生成代码。

 

例如:

 

/* 

延时操作

 */ 

int foo(void) {  

 /* 100

次减法后返回

 */ 

 volatile int i = 100; /*(a)*/ 

 while (i > 0) i--;  /*(b)*/ 

 return 0; 

     

在无

volatile

修饰的情况下,因为变量

i

的变化对上下文无影响,所以优化器很可能

 

会省略掉对

i

操作的代码,而只生成

return 0

的代码,加上

volatile

可以保证编译器一定为

 

语句

(a)

(b)

生成代码,达到延时的目的。

 

 

/* 

设备状态判定

 */ 

int uart_write_char(int c) {  

 /* 

向串口发送寄存器写入待发送字符

 */ 

 *(volatile unsigned int *)UART_TX_REG = c; 

 /* 

判断是否已发送

*/ 

 while ( (*(volatile unsigned int *)UART_STATUS_REG & TX_BIT) != 0); /*(c)*/ 

 

 return 0; 

     

在语句

(c)

中,如果不使用

volatile

,优化器可能会因为在两次读取

UART_STATUS_RE 

G

之间没有对

UART_STATUS_REG

的写操作而将读取操作外提到循环体外而导致死循环。

原文地址:https://www.cnblogs.com/DMDD/p/4994941.html