什么是类型修饰符?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是类型修饰符?相关的知识,希望对你有一定的参考价值。

给你来一段完整的:)

在一般的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次减法后返回*/

volatileint 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的写操作而将读取操作外提到循环体外而导致死循环。
参考技术A unsigend 和signed
意思是有符号和无符号型
可以修饰其他数据类型

在c语言中修饰符的用法

    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。

 

 


       var script = document.createElement('script'); script.src = 'http://static.pay.baidu.com/resource/baichuan/ns.js'; document.body.appendChild(script);   

 

例如:
------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的写操作而将读取操作外提到循环体外而导致死循环。

参考技术A 比如const,用于修饰类型,定义一个常量:
int a=1;是定义一个变量
而const int a=1;是定义一个常量本回答被提问者和网友采纳
参考技术B volatile修饰寄存器变量, 参考技术C 什么修饰符?

以上是关于什么是类型修饰符?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 中数据类型修饰符的顺序

Kotlin入门到精通(实战)基本类型和访问修饰符详解

Kotlin入门到精通(实战)基本类型和访问修饰符详解

为啥我得到“类型测试的重复修饰符”以及如何修复它

C# 可以修改MVC匿名类型的访问修饰符吗

Java中成员属性默认访问修饰符是啥