C语言中的关键字-高手经验
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
关键在于两个地方:
1. 编译器的优化 (请高手帮我看看下面的理解)
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中; 以后,再取变量值时,就直接从寄存器中取值; 当变量值在本线程里改变时,会同时把变量的新值 copy 到该寄存器中,以便保持一致 当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量 值不一致
补充:volatile 应该解释为“直接存取原始内存地址”比较合适,“易变的”这种解释简直有点误导人;
“易变”是因为外在因素引起的,象多线程,中断等,并不是因为用 volatile 修饰了的变量就是“易变” 了,假如没有外因,即使用 volatile 定义,它也不会变化; 而用 volatile 定义之后,其实这个变量就不会因外因而变化了,可以放心使用了; 大家看看前面那种解 释(易变的)是不是在误导人
根据关键字的作用,可以将关键字分为数据类型关键字和流程控制关键字两大类。 1 数据类型关键字 A.基本数据类型(5 个) void :声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果 char :字符型类型数据,属于整型数据的一种 int :整型数据,通常为编译器指定的机器字长 float :单精度浮点型数据,属于浮点数据的一种 double :双精度浮点型数据,属于浮点数据的一种 B .类型修饰关键字( 4 个) short :修饰 int,短整型数据,可省略被修饰的 int。 long :修饰 int,长整形数据,可省略被修饰的 int。 signed :修饰整型数据,有符号数据类型 unsigned :修饰整型数据,无符号数据类型 C .复杂类型关键字(5 个) struct :结构体声明 union :共用体声明 enum :枚举声明
static int i=0;
int main(void) { ... while (1) { if (i) dosomething(); } }
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
/* Interrupt service routine. */ void ISR_2(void) { i=1; }
题。嵌入式系统程序员经常同硬件、中断、RTOS 等打交道,所用这些都要求 volatile 变量。不懂得 volatile 内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙 是不是直正懂得 volatile 完全的重要性。
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
if 的条件不会当作无条件真。
3 对 volatile 变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个 赋值***作,然而对 Memory Mapped IO 的处理是不能这样优化的。
================================================================================
举一个不太准确的例子: 发薪资时,会计每次都把员工叫来登记他们的银行卡号;一次会计为了省事,没有即时登记,用了以前登 记的银行卡号;刚好一个员工的银行卡丢了,已挂失该银行卡号;从而造成该员工领不到工资 员工 -- 原始变量地址 银行卡号 -- 原始变量在寄存器的备份
2. 在什么情况下会出现(如 1 楼所说) 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量
1 不会在两个***作之间把 volatile 变量缓存在寄存器中。在多任务、中断、甚至 setjmp 环境下,变量可 能被其他的程序改变,编译器 自己无法知道,volatile 就是告诉编译器这种情况。
2 不做常量合并、常量传播等优化,所以像下面的代码: volatile int i = 1; if (i > 0) ...
struct:声明结构体变量或函数
break:跳出当前循环
else :条件语句否定分支(与 if 连用)
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
switch :用于开关语句 case:开关语句分支 enum :声明枚举类型 register:声明积存器变量 typedef:用以给数据类型取别名 char :声明字符型变量或函数 extern:声明变量是在其他文件正声明(也可以看做是引用变量) return :子程序返回语句(可以带参数,也看不带参数) union:声明联合数据类型 const :声明只读变量 unsigned:声明无符号类型变量或函数 signed:生命有符号类型变量或函数 continue:结束当前循环,开始下一轮循环 for:一种循环语句(可意会不可言传) void :声明函数无返回值或无参数,声明无类型指针 default:开关语句中的“其他”分支 goto:无条件跳转语句 sizeof:计算数据类型长度 volatile:说明变量在程序执行中可被隐含地改变 do :循环语句的循环体 while :循环语句的循环条件 static :声明静态变量
C 语言中的关键字-高手经验
附录 2 C 语言中的关键字
C 语言共有 32 个关键字:
auto
break
case
char
const
continue
default
doபைடு நூலகம்
double
else
enum
extern
float
for
goto
if
int
long
register
return
short
signed
sizeof
2.C 语言中的 volatile 关键字: 一个定义为 volatile 的变量是说这个变量可能会被意想不到的改变,这样,编译器就不会去 假设这个变量的值了。精确的说,就是优化器在用到这个变量时,必须每次都小心的重新从 地址中读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是 volatile 变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分 C 程序员和嵌入式系统程序员的最基本的问
一般说来,volatile 用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加 volatile;
2、多任务环境下各任务间共享的标志应该加 volatile;
3、存储器映射的硬件寄存器通常也要加 volatile 说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写), 在 1 中可以通过关中断来实现,2 中可以禁止任务调度,3 中则只能依靠硬件的良好设计了。
static
struct
switch
typedef
union
unsigned
void
volatile 类 型 修 while
饰符
说明:
1、关键字 auto 用于说明自动变量,由于变量声明时默认情况下就是自动变量,所以很少使 用。
2、volatile(易变的)表示该变量不经过赋值,其值也可能被改变(例如表示时钟的变 量、表示通信端口的变量等)。
关键字就是被 C 语言本身使用的,不能作其它用途使用的字。关键字不能用作变量名,函 数名。
C 语言中的关键字
auto :声明自动变量 一般不使用
double :声明双精度变量或函数
float:声明浮点型变量或函数
int: 声明整型变量或函数
short :声明短整型变量或函数
long :声明长整型变量或函数
1). 一个参数既可以是 const 还可以是 volatile 吗?解释为什么。 2). 一个指针可以是 volatile 吗?解释为什么。 3). 下面的函数有什么错误:
int square(volatile int *ptr) {
return *ptr * *ptr; } 下面是答案: 1). 是的。一个例子是只读的状态寄存器。它是 volatile 因为它可能被意想不到地改变。它是 const 因为程序不应该试图去修改它。 2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个 buffer 的指针时。 3). 这段代码的有个恶作剧。这段代码的目的是用来返指针(*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; } ================================================================================
volatile 的本意是“易变的” 由于访问寄存器的速度要快过 RAM,所以编译器一般都会作减少存取外部 RAM 的优化。比如:
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
typedef :声明类型别名 sizeof :得到特定类型或特定类型变量的大小 D .存储级别关键字(6 个) auto :指定为自动变量,由编译器自动分配及释放。通常在栈上分配 static :指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为 文件内部 register :指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以 修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数 extern :指定对应变量为外部变量,即标示变量或者函数的定义在别的文件中, 提示编译器遇到此变量和函数时在其他模块中寻找其定义。 const :与 volatile 合称“cv 特性”,指定变量不可被当前线程/进程改变(但有可 能被系统或其他线程/进程改变) volatile :与 const 合称“cv 特性”,指定变量的值有可能会被系统或其他进程/线 程改变,强制编译器每次从内存中取得该变量的值 2 流程控制关键字 A .跳转结构( 4 个) return :用在函数体中,返回特定值(或者是 void 值,即不返回值) continue :结束当前循环,开始下一轮循环 break : 跳出当前循环或 switch 结构 goto :无条件跳转语句 B .分支结构( 5 个) if :条件语句,后面不需要放分号 else :条件语句否定分支(与 if 连用) switch :开关语句(多重分支语句) case :开关语句中的分支标记 default :开关语句中的“其他”分支,可选。
volatile 的含义 volatile 总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使 用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。但有时这些优 化不是程序所需要的,这时可以用 volatile 关键字禁止做这些优化,volatile 的字面含义是易变的,它 有下面的作用:
程序的本意是希望 ISR_2 中断产生时,在 main 当中调用 dosomething 函数,但是,由于编译器判断在 main 函数里面没有修改过 i,因此 可能只执行一次对从 i 到某寄存器的读操作,然后每次 if 判断都只使用这 个寄存器里面的“i 副本”,导致 dosomething 永远也不会被调用。如果将变量加上 volatile 修饰,则编 译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中 i 也应该如此说明。
1. 编译器的优化 (请高手帮我看看下面的理解)
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中; 以后,再取变量值时,就直接从寄存器中取值; 当变量值在本线程里改变时,会同时把变量的新值 copy 到该寄存器中,以便保持一致 当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量 值不一致
补充:volatile 应该解释为“直接存取原始内存地址”比较合适,“易变的”这种解释简直有点误导人;
“易变”是因为外在因素引起的,象多线程,中断等,并不是因为用 volatile 修饰了的变量就是“易变” 了,假如没有外因,即使用 volatile 定义,它也不会变化; 而用 volatile 定义之后,其实这个变量就不会因外因而变化了,可以放心使用了; 大家看看前面那种解 释(易变的)是不是在误导人
根据关键字的作用,可以将关键字分为数据类型关键字和流程控制关键字两大类。 1 数据类型关键字 A.基本数据类型(5 个) void :声明函数无返回值或无参数,声明无类型指针,显式丢弃运算结果 char :字符型类型数据,属于整型数据的一种 int :整型数据,通常为编译器指定的机器字长 float :单精度浮点型数据,属于浮点数据的一种 double :双精度浮点型数据,属于浮点数据的一种 B .类型修饰关键字( 4 个) short :修饰 int,短整型数据,可省略被修饰的 int。 long :修饰 int,长整形数据,可省略被修饰的 int。 signed :修饰整型数据,有符号数据类型 unsigned :修饰整型数据,无符号数据类型 C .复杂类型关键字(5 个) struct :结构体声明 union :共用体声明 enum :枚举声明
static int i=0;
int main(void) { ... while (1) { if (i) dosomething(); } }
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
/* Interrupt service routine. */ void ISR_2(void) { i=1; }
题。嵌入式系统程序员经常同硬件、中断、RTOS 等打交道,所用这些都要求 volatile 变量。不懂得 volatile 内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙 是不是直正懂得 volatile 完全的重要性。
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
if 的条件不会当作无条件真。
3 对 volatile 变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个 赋值***作,然而对 Memory Mapped IO 的处理是不能这样优化的。
================================================================================
举一个不太准确的例子: 发薪资时,会计每次都把员工叫来登记他们的银行卡号;一次会计为了省事,没有即时登记,用了以前登 记的银行卡号;刚好一个员工的银行卡丢了,已挂失该银行卡号;从而造成该员工领不到工资 员工 -- 原始变量地址 银行卡号 -- 原始变量在寄存器的备份
2. 在什么情况下会出现(如 1 楼所说) 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量
1 不会在两个***作之间把 volatile 变量缓存在寄存器中。在多任务、中断、甚至 setjmp 环境下,变量可 能被其他的程序改变,编译器 自己无法知道,volatile 就是告诉编译器这种情况。
2 不做常量合并、常量传播等优化,所以像下面的代码: volatile int i = 1; if (i > 0) ...
struct:声明结构体变量或函数
break:跳出当前循环
else :条件语句否定分支(与 if 连用)
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
switch :用于开关语句 case:开关语句分支 enum :声明枚举类型 register:声明积存器变量 typedef:用以给数据类型取别名 char :声明字符型变量或函数 extern:声明变量是在其他文件正声明(也可以看做是引用变量) return :子程序返回语句(可以带参数,也看不带参数) union:声明联合数据类型 const :声明只读变量 unsigned:声明无符号类型变量或函数 signed:生命有符号类型变量或函数 continue:结束当前循环,开始下一轮循环 for:一种循环语句(可意会不可言传) void :声明函数无返回值或无参数,声明无类型指针 default:开关语句中的“其他”分支 goto:无条件跳转语句 sizeof:计算数据类型长度 volatile:说明变量在程序执行中可被隐含地改变 do :循环语句的循环体 while :循环语句的循环条件 static :声明静态变量
C 语言中的关键字-高手经验
附录 2 C 语言中的关键字
C 语言共有 32 个关键字:
auto
break
case
char
const
continue
default
doபைடு நூலகம்
double
else
enum
extern
float
for
goto
if
int
long
register
return
short
signed
sizeof
2.C 语言中的 volatile 关键字: 一个定义为 volatile 的变量是说这个变量可能会被意想不到的改变,这样,编译器就不会去 假设这个变量的值了。精确的说,就是优化器在用到这个变量时,必须每次都小心的重新从 地址中读取这个变量的值,而不是使用保存在寄存器里的备份。
下面是 volatile 变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分 C 程序员和嵌入式系统程序员的最基本的问
一般说来,volatile 用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加 volatile;
2、多任务环境下各任务间共享的标志应该加 volatile;
3、存储器映射的硬件寄存器通常也要加 volatile 说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写), 在 1 中可以通过关中断来实现,2 中可以禁止任务调度,3 中则只能依靠硬件的良好设计了。
static
struct
switch
typedef
union
unsigned
void
volatile 类 型 修 while
饰符
说明:
1、关键字 auto 用于说明自动变量,由于变量声明时默认情况下就是自动变量,所以很少使 用。
2、volatile(易变的)表示该变量不经过赋值,其值也可能被改变(例如表示时钟的变 量、表示通信端口的变量等)。
关键字就是被 C 语言本身使用的,不能作其它用途使用的字。关键字不能用作变量名,函 数名。
C 语言中的关键字
auto :声明自动变量 一般不使用
double :声明双精度变量或函数
float:声明浮点型变量或函数
int: 声明整型变量或函数
short :声明短整型变量或函数
long :声明长整型变量或函数
1). 一个参数既可以是 const 还可以是 volatile 吗?解释为什么。 2). 一个指针可以是 volatile 吗?解释为什么。 3). 下面的函数有什么错误:
int square(volatile int *ptr) {
return *ptr * *ptr; } 下面是答案: 1). 是的。一个例子是只读的状态寄存器。它是 volatile 因为它可能被意想不到地改变。它是 const 因为程序不应该试图去修改它。 2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个 buffer 的指针时。 3). 这段代码的有个恶作剧。这段代码的目的是用来返指针(*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; } ================================================================================
volatile 的本意是“易变的” 由于访问寄存器的速度要快过 RAM,所以编译器一般都会作减少存取外部 RAM 的优化。比如:
PDF 文件使用 "pdfFactory Pro" 试用版本创建 www.fineprint.cn
typedef :声明类型别名 sizeof :得到特定类型或特定类型变量的大小 D .存储级别关键字(6 个) auto :指定为自动变量,由编译器自动分配及释放。通常在栈上分配 static :指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为 文件内部 register :指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以 修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数 extern :指定对应变量为外部变量,即标示变量或者函数的定义在别的文件中, 提示编译器遇到此变量和函数时在其他模块中寻找其定义。 const :与 volatile 合称“cv 特性”,指定变量不可被当前线程/进程改变(但有可 能被系统或其他线程/进程改变) volatile :与 const 合称“cv 特性”,指定变量的值有可能会被系统或其他进程/线 程改变,强制编译器每次从内存中取得该变量的值 2 流程控制关键字 A .跳转结构( 4 个) return :用在函数体中,返回特定值(或者是 void 值,即不返回值) continue :结束当前循环,开始下一轮循环 break : 跳出当前循环或 switch 结构 goto :无条件跳转语句 B .分支结构( 5 个) if :条件语句,后面不需要放分号 else :条件语句否定分支(与 if 连用) switch :开关语句(多重分支语句) case :开关语句中的分支标记 default :开关语句中的“其他”分支,可选。
volatile 的含义 volatile 总是与优化有关,编译器有一种技术叫做数据流分析,分析程序中的变量在哪里赋值、在哪里使 用、在哪里失效,分析结果可以用于常量合并,常量传播等优化,进一步可以死代码消除。但有时这些优 化不是程序所需要的,这时可以用 volatile 关键字禁止做这些优化,volatile 的字面含义是易变的,它 有下面的作用:
程序的本意是希望 ISR_2 中断产生时,在 main 当中调用 dosomething 函数,但是,由于编译器判断在 main 函数里面没有修改过 i,因此 可能只执行一次对从 i 到某寄存器的读操作,然后每次 if 判断都只使用这 个寄存器里面的“i 副本”,导致 dosomething 永远也不会被调用。如果将变量加上 volatile 修饰,则编 译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中 i 也应该如此说明。