volatile详解

volatile详解
volatile详解

volatile

一:为什么要讲volatile

因为,很多”面试官”自己找不到能够测试应聘者的好的方式,所以就google了一下,发现了”嵌入式经典的0x10个面试题”,于是乎就拿来直接问了。我想第一个想到用这个来提问应聘者的人绝对是值得我们仰慕的。

二:V olatile官方说明

Indicates that a variable can be changed by a background routine.

Keyword volatile is an extreme opposite of const. It indicates that a variable may be changed in a way which is absolutely unpredictable by analysing the normal program flow (for example, a variable which may be changed by an interrupt handler). This keyword uses the following syntax: volatile data-definition;

Every reference to the variable will reload the contents from memory rather than take advantage of situations where a copy can be in a register.

翻译:

表示一个变量也许会被后台程序改变.

关键字volatile是与const绝然对立的。它指示一个变量也许会被某种方式修改,这种方式按照正常程序流程分析是无法预知的(例如,一个变量也许会被一个中断服务程序所修改)。这个关键字使用下列语法定义:

volatile data-definition;

变量如果加了volatile修饰,则会从内存重新装载内容,而不是直接从寄存器拷贝内容。

三:实例分析

V olatile应用比较多的场合,在中断服务程序和cpu相关寄存器的定义。

比如,下面就是lpc2136 相关寄存器定义

/**************************

/* Vectored Interrupt Controller (VIC) */

#define VICIRQStatus (*((volatile unsigned long *) 0xFFFFF000))

#define VICFIQStatus (*((volatile unsigned long *) 0xFFFFF004))

#define VICRawIntr (*((volatile unsigned long *) 0xFFFFF008))

#define VICIntSelect (*((volatile unsigned long *) 0xFFFFF00C))

#define VICIntEnable (*((volatile unsigned long *) 0xFFFFF010))

#define VICIntEnClr (*((volatile unsigned long *) 0xFFFFF014))

#define VICSoftInt (*((volatile unsigned long *) 0xFFFFF018))

#define VICSoftIntClear (*((volatile unsigned long *) 0xFFFFF01C))

#define VICProtection (*((volatile unsigned long *) 0xFFFFF020))

#define VICVectAddr (*((volatile unsigned long *) 0xFFFFF030))

#define VICDefVectAddr (*((volatile unsigned long *) 0xFFFFF034))

#define VICVectAddr0 (*((volatile unsigned long *) 0xFFFFF100))

#define VICVectAddr1 (*((volatile unsigned long *) 0xFFFFF104))

#define VICVectAddr2 (*((volatile unsigned long *) 0xFFFFF108))

#define VICVectAddr3 (*((volatile unsigned long *) 0xFFFFF10C))

#define VICVectAddr4 (*((volatile unsigned long *) 0xFFFFF110))

#define VICVectAddr5 (*((volatile unsigned long *) 0xFFFFF114))

#define VICVectAddr6 (*((volatile unsigned long *) 0xFFFFF118))

#define VICVectAddr7 (*((volatile unsigned long *) 0xFFFFF11C))

#define VICVectAddr8 (*((volatile unsigned long *) 0xFFFFF120))

#define VICVectAddr9 (*((volatile unsigned long *) 0xFFFFF124))

#define VICVectAddr10 (*((volatile unsigned long *) 0xFFFFF128))

#define VICVectAddr11 (*((volatile unsigned long *) 0xFFFFF12C))

也许有人看到这里有带出了跟本文无关的疑问,#define VICIntEnable (*((volatile unsigned long *) 0xFFFFF010))

是什么语法。

这里也一并介绍了:

先总结一句话,#define VICIntEnable (*((volatile unsigned long *) 0xFFFFF010))

其实就是定义一个指针变量。

那什么是指针变量呢,万变不离其宗!

我们看C里面对指针变量的定义:(大家可以去看谭浩强老师C语言第四版指针部分)

Int a;这里a是一个变量,是一个32位整形变量;

Int *p;同理,p也是一个变量,但他是一个指针变量,他可以存放一个地址,

如:p=&a,(p可以这样赋值),p存放的地址是变量a的地址。&是取地址符。

那么*p是什么呢?

*p就是p所指向的内容!!

比如:

{

Int a;

Int *p;

a=100;

p=&a

}

那么*p就等于100;

但是,这里还有个问题,p本身的地址。如果想到了这里,我们就好介绍

,#define VICIntEnable (*((volatile unsigned long *) 0xFFFFF010))

我们来做一个例比:

(*((volatile unsigned long *) 0xFFFFF010))=========>*p

那么这里((volatile unsigned long *) 0xFFFFF010)========>p

这里0xFFFFF010就对应到p本身的地址。

首先,大家应该知道这是一个C语言里面的宏定义;然后,0xFFFFF010这个32位数,这个数的来源在lpc2136 datasheet,52page/270

所以,这个32位数是一个寄存器地址,要把一个32位数表示成地址怎么表示呢?

(unsigned long *) 0xFFFFF010,就是这样(也可以表示成(unsigned char *) 0xFFFFF010,前者表示这个地方可以存放32位数据,后者表示这个地址只能存放8位数据)。

既然是地址,就像我们在超市里面的寄存包裹箱一样,是可以存放东西,而且可以取出东西的地方。我们叫可读写,当然有些地址是不能写的,只读的,比如这里面的VICIRQStatus..

寄存器地址为什么要加volatile修饰呢,是因为,这些寄存器里面的值是随时变化的。比如,我们这里的中断状态寄存器VICIRQStatus ,当某个中断发生的时候,我们无法知道,那么这个状态寄存器的内容也是无法预知的。我们读取的时候,CPU就直接到内存里面取值,而不是到cache里面取值。

*(volatile uint32 *)0x60001404 = 0x0000;

意思就是初始化0x60001404地址中的值为0x0000,一般在很多单片机

程序中完成寄存器的操作时经常这样用,它相当于下面两步

volatile uint32 * p = (volatile uint32 *)0x60001404;

* p = 0x0000; (peak:把p指向的变量赋值0x0000)

其中(volatile uint32 *)0x60001404,是利用c语言的强制类型转换把地址值转换为volatile uint32 * 类型的指针(相当于volatile uint32 * p = (volatile uint32 *)0x60001404),然后将0x0000赋给这个指针指向的地址(相当于* p = 0x0000),也就使

0x60001404地址中的值为0x0000。

关键字Const与Volatile的使用

关键字const有什么含意? 我只要一听到被面试者说:“const意味着常数”,我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着“只读”就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。) 如果应试者能正确回答这个问题,我将问他一个附加的问题: 下面的声明都是什么意思? const int a; int const a; const int *a; int * const a; int const * a const; /******/ 前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由: ?; 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) ?; 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 ?; 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。 Volatile 8. 关键字volatile有什么含意?并给出三个不同的例子。 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: ?; 并行设备的硬件寄存器(如:状态寄存器) ?; 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) ?; 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile 完全的重要性。 ?; 一个参数既可以是const还可以是volatile吗?解释为什么。 ?; 一个指针可以是volatile 吗?解释为什么。

c语言中#和##的用法

本文主要讲述c语言的一点基础语法和在内核的应用中其中的一点例子。 #,##分别在c语言中是怎么作用? 文章代码编译的环境: 桌面环境:Ubuntu10.04 内核:linux2.6.32 编译器:gcc4.4.3 一、基本的用法 1、#.参数名以#作为前缀则结果将被扩展为由实际参数的带引号的字符串。如: #define dprint(expr)printf(#expr"=%d\n",expr); intmain() { inta=20,b=10; dprint(a/b); return0; } 上面的例子会打印出: a/b=2 2、##.预处理器运算符##为宏提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白将被删除,并对替换后的结果重新扫描。 形成一个新的标号,如果这样产生的记号无效,或者结果依赖于##运算顺序,则结果没有定义。 如: #definepaste(front,back)front##back 因此,宏调用paste(name,_xiaobai)的结果为name_xiaobai. 如: #define createfun(name1,name2)\ void name1##name2()\ {\ printf("%scalled\n",__FUNCTION__);\ } createfun(the,function); intmain() { thefunction(); return0; } 输出的结果是:thefunctioncalled 二、##可以嵌套吗?

看下面的例子: #define cat(x,y)x##y 宏调用cat(var,123)讲生成var123. 但是,宏调用cat(cat(1,2),3)没有定义:##阻止了外层调用的参数的扩展。因此,它将生成下列的记号串: cat(1,2)3. 如果要再引入第二层的宏定义,如下定义: #define xcat(x,y)cat(x,y) 那么xcat(xcat(1,2),3)将生成123, 这是因为xcat自身的扩展不包含##运算符。 三、linux内核中例子 因为是做mips架构的,所以以mips为例子。 Linux2.6.25内核,include/asm-mips/io.h文件。拷贝一部分的代码出来。 #define__BUILD_MEMORY_SINGLE(pfx,bwlq,type,irq)\ \ staticinlinevoidpfx##write##bwlq(typeval,\ volatilevoid__iomem*mem)\ {\ volatiletype*__mem;\ type__val;\ \

讲讲volatile的作用

转载来自hbtian的笔记 讲讲volatile的作用 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile 变量。不懂得volatile内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 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; }

Java之volatile的使用及其原理

一、volatile的作用 我们已经知道可见性、有序性及原子性问题,通常情况下我们可以通过Synchronized关键字来解决这些个问题,不过如果对Synchronized原理有了解的话,应该知道Synchronized是一个比较重量级的操作,对系统的性能有比较大的影响,所以,如果有其他解决方案,我们通常都避免使用Synchronized来解决问题。 而volatile关键字就是Java中提供的另一种解决可见性和有序性问题的方案。对于原子性,需要强调一点,也是大家容易误解的一点:对volatile变量的单次读/写操作可以保证原子性的,如long和double类型变量,但是并不能保证i++这种操作的原子性,因为本质上i++是读、写两次操作。 二、volatile的使用 关于volatile的使用,我们可以通过几个例子来说明其使用方式和场景。 1、防止重排序 我们从一个最经典的例子来分析重排序问题。大家应该都很熟悉单例模式的实现,而在并发环境下的单例实现方式,我们通常可以采用双重检查加锁(DCL)的方式来实现。其源码如下: package com.paddx.test.concurrent; public class Singleton { public static volatile Singleton singleton; /** * 构造函数私有,禁止外部实例化 */ private Singleton() {}; public static Singleton getInstance() { if (singleton == null) { synchronized (singleton) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } } 现在我们分析一下为什么要在变量singleton之间加上volatile关键字。要理解这个问题,先要了解对象的构造过程,实例化一个对象其实可以分为三个步骤: ?分配内存空间。 ?初始化对象。

C语言中volatile用法小结

计算机二级C技巧:c语言中的volatile关键字 来源:考试大 2009年06月10日10:36 volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。 用volatile关键字声明的变量i每一次被访问时,执行部件都会从i相应的内存单元中取出i的值。 没有用volatile关键字声明的变量i在被访问的时候可能直接从cpu的寄存器中取值(因为之前i被访问过,也就是说之前就从内存中取出i的值保存到某个寄存器中),之所以直接从寄存器中取值,而不去内存中取值,是因为编译器优化代码的结果(访问cpu寄存器比访问ram快的多)。 以上两种情况的区别在于被编译成汇编代码之后,两者是不一样的。之所以这样做是因为变量i可能会经常变化,保证对特殊地址的稳定访问。 volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。 使用该关键字的例子如下: int volatile nVint; 当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。 例如: volatile int i=10; int a = i; ... //其他代码,并未明确告诉编译器,对i进行过操作 int b = i; volatile 指出i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在b中。而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在b中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。 注意,在vc6中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编代码,测试有无volatile关键字,对程序最终代码的影响: 首先,用classwizard建一个win32 console工程,插入一个voltest.cpp文件,输入下面的代码:#i nclude void main() { int i=10; int a = i; printf("i= %d\n",a); //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道

C语言各关键字与定义

C语言关键字 int char float double long short unsigned sizeof scanf printf 整型字符单精度双精度长整型短整型无符号计算字节格式输入格式输出 putchar getchar if else swtich case break (while do-while for) continue 字符输出字符输入判断多分支判断跳出循环循环提前结束循环goto 字符(puts gets strcmp strcpy strcat strlen )return 无条件转移串输出串输入串比较串拷贝串连接串长度测试函数返回 auto static register struct FLIE 自动型静态寄存器结构文件结构 (fopen fclose fgetc fread fscanf fputc fwrite fprintf rewind fseek ftell)打开关闭单字符组数据格式化入单字符组数据格式化出开头指定返回enum union void default extern const trpedef volatile 定义枚举联合无返回无,结束外部变量修饰作用定义新名修饰 非缓冲(open creat close read write lseek tell) 文件打开文件创建文件关闭文件读文件写定位指定文件返回 C语言各语法定义 int :int 变量名;int x,y; Char: char 变量名;char x,y; Float: float 变量名;float x,y; Double: double 变量名;double x,y; Long: long int 变量名;long int x,y; Short: short int 变量名;short int x,y; Unsigned: unsigned 变量名; unsigned int x,y; Sizeof: sizeof(类型名); sizeof(int); Scanf: scanf(格式控制,地址表);(" %d%o%x%c%f%e%s ",&十,&八,&十六,&单, &浮点,&浮点,&以'\0'为结束) ; Printf: printf(格式控制,输出表);("%d%o%x%u%c%f%e%s%", 十,八,十六,单,浮点, 浮点,以%0为结束); Putchar:putchat(c); Getchar:getchar(c); If: if(条件表达式){ 语句} Else: if(条件表达式){语句1;else 语句2;}

c语言中#和##的用法

本文主要讲述c语言的一点基础语法与在内核的应用中其中的一点例子。 #,##分别在c语言中就是怎么作用? 文章代码编译的环境: 桌面环境:Ubuntu10、04 内核:linux2、6、32 编译器:gcc4、4、3 一、基本的用法 1、#、参数名以#作为前缀则结果将被扩展为由实际参数的带引号的字符串。如: #define dprint(expr)printf(#expr"=%d\n",expr); intmain() { inta=20,b=10; dprint(a/b); return0; } 上面的例子会打印出: a/b=2 2、##、预处理器运算符##为宏提供了一种连接实际参数的手段。如果替换文本中的参数与##相邻,则该参数将被实际参数替换,##与前后的空白将被删除,并对替换后的结果重新扫描。 形成一个新的标号,如果这样产生的记号无效,或者结果依赖于##运算顺序,则结果没有定义。 如: #definepaste(front,back)front##back 因此,宏调用paste(name,_xiaobai)的结果为name_xiaobai、 如: #define createfun(name1,name2)\ void name1##name2()\ {\ printf("%scalled\n",__FUNCTION__);\ } createfun(the,function); intmain() { thefunction(); return0; } 输出的结果就是:thefunctioncalled 二、##可以嵌套不? 瞧下面的例子: #define cat(x,y)x##y

C语言中auto,register,static,const,volatile的区别

C语言中auto,register,static,const,volatile的区别 (1)auto 这个关键字用于声明变量的生存期为自动,即将不在任何类、结构、枚举、联合和函数中定义的变量视为全局变量,而在函数中定义的变量视为局部变量。这个关键字不怎么多写,因为所有的变量默认就是auto的。 (2)register 这个关键字命令编译器尽可能的将变量存在CPU内部寄存器中而不是通过内存寻址访问以提高效率。 (3)static 常见的两种用途: 1>统计函数被调用的次数; 2>减少局部数组建立和赋值的开销.变量的建立和赋值是需要一定的处理器开销的,特别是数组等含有较多元素的存储类型。在一些含有较多的变量并且被经常调用的函数中,可以将一些数组声明为static类型,以减少建立或者初始化这些变量的开销. 详细说明: 1>、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与栈变量和堆变量的区别。 2>、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。 3>当static用来修饰全局变量时,它就改变了全局变量的作用域,使其不能被别的程序extern,限制在了当前文件里,但是没有改变其存放位置,还是在全局静态储存区。 使用注意: 1>若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度; 2>若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度; 3>设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题(只要输入数据相同就应产生相同的输出)。 (4)const 被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。它可以修饰函数的参数、返回值,甚至函数的定义体。 作用: 1>修饰输入参数 a.对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。 b.对于内部数据类型的输入参数,不要将“值传递”的方式改为“const引用传递”。否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不应该改为void Func(const int &x)。 2>用const修饰函数的返回值 a.如果给以“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const修饰的同类型指针。 如对于:const char * GetString(void); 如下语句将出现编译错误: char *str = GetString();//cannot convert from 'const char *' to 'char *'; 正确的用法是:

八大浪费定义

八大浪费是定义工厂在JIT生产方式中的,其浪费的含义与社会上通常所说的浪费有所区别。对于JIT 来讲,凡是超出增加产品价值所必需的绝对最少的物料、设备、人力、场地和时间的部分都是浪费。因此,JIT生产方式所讲的工厂的浪费归纳为八大种,分别是:不良、修理的浪费,过分加工的浪费,动作的浪费,搬运的浪费,库存的浪费,制造过多过早的浪费,等待的浪费和管理的浪费,简称为八大浪费。 2具体表现 1.不良、修理的浪费 所谓不良、修理的浪费,指的是由于工厂内出现不良品,需要进行处置的时间、人力、物力上的浪费,以及由此造成的相关损失。这类浪费具体包括:材料的损失、不良品变成废品;设备、人员和工时的损失; 额外的修复、鉴别、追加检查的损失;有时需要降价处理产品,或者由于耽误出货而导致工厂信誉的下降。 2.加工的浪费 加工的浪费也叫过分加工的浪费,主要包含两层含义:第一是多余的加工和过分精确的加工,例如实际加工精度过高造成资源浪费;第二是需要多余的作业时间和辅助设备,还要增加生产用电、气压、油等能源的浪费,另外还增加了管理的工时。 3.动作的浪费 动作的浪费现象在很多企业的生产线中都存在,常见的动作浪费主要有以下12种:两手空闲、单手空闲、作业动作突然停止、作业动作过大、左右手交换、步行过多、转身的角度太大,移动中变换“状态”、不明技巧、伸背动作、弯腰动作以及重复动作和不必要的动作等,这些动作的浪费造成了时间和体力上的不必要消耗。 4.搬运的浪费 从JIT的角度来看,搬运是一种不产生附加价值的动作,而不产生价值的工作都属于浪费。搬运的浪费具体表现为放置、堆积、移动、整列等动作浪费,由此而带来物品移动所需空间的浪费、时间的浪费和人力工具的占用等不良后果。 国内目前有不少企业管理者认为搬运是必要的,不是浪费。因此,很多人对搬运浪费视而不见,更谈不上去消灭它。也有一些企业利用传送带或机器搬运的方式来减少人工搬运,这种做法是花大钱来减少工人体力的消耗,实际上并没有排除搬运本身的浪费。 5.库存的浪费 按照过去的管理理念,人们认为库存虽然是不好的东西,但却是必要的。JIT的观点认为,库存是没有必要的,甚至认为库存是万恶之源。如图1-1,由于库存很多,将故障、不良品、缺勤、点点停、计划有误、调整时间过长、品质不一致、能力不平衡等问题全部掩盖住了。 例如,有些企业生产线出现故障,造成停机、停线,但由于有库存而不至于断货,这样就将故障造成停机、停线的问题掩盖住了,耽误了故障的排除。如果降低库存,就能将上述问题彻底暴露于水平面,进而能够逐步地解决这些库存浪费.。 6.制造过多过早的浪费 制造过多或过早,提前用掉了生产费用,不但没有好处,还隐藏了由于等待所带来的浪费,失去了持续改善的机会。有些企业由于生产能力比较强大,为了不浪费生产能力而不中断生产,增加了在制品,使得制品周期变短、空间变大,还增加了搬运、堆积的浪费。此外,制造过多或过早,会带来庞大的库存量,利息负担增加,不可避免地增加了贬值的风险。 7.等待的浪费 由于生产原料供应中断、作业不平衡和生产计划安排不当等原因造成的无事可做的等待,被称为等待的浪费。生产线上不同品种之间的切换,如果准备工作不够充分,势必造成等待的浪费;每天的工作量变动幅度过大,有时很忙,有时造成人员、设备闲置不用;上游的工序出现问题,导致下游工序无事可做。此外,生产线劳逸不均等现象的存在,也是造成等待浪费重要原因。

volatile unsigned char详解

标签:*(volatile unsigned char*) (*(volatile unsigned char *)0x56000010) 以前看到#define SREG (*(volatile unsigned char *)0x5F)这样的定义,总是感觉很奇怪,不知道为什么,今天终于有了一点点心得,请大虾们多多批砖~~~ 嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,比如0x5F, 第一步是要把它强制转换为指针类型 (unsigned char *)0x5F,AVR的SREG是八位寄存器,所以0x5F强制转换为指向unsi gned char类型。 volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到。 第二步,对指针变量解引用,就能操作指针所指向的地址的内容了 *(volatile unsigned char *)0x5F 第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯,所以#defi ne SREG (*(volatile unsigned char *)0x5F) 类似的,如果使用一个32位处理器,要对一个32位的内存地址进行访问,可以这样定义: #define RAM_ADDR (*(volatile unsigned long *)0x0000555F) 然后就可以用C语言对这个内存地址进行读写操作了 读:tmp = RAM_ADDR; 写:RAM_ADDR = 0x55; zhiwei 发表于 2005-4-30 18:59 AVR 单片机 定义未volatile是因为它的值可能会改变,大家都知道为什么改变了; 如果在一个循环操作中需要不停地判断一个内存数据,例如要等待SREG的I标志位置位,因为SREG也是映射在SRAM空间,为了加快速度,编译器可能会编译出这样的代码:把SREG 读取到Register中,然后不停地判断Register相应位。而不会再读取SREG,这样当然是不行了,因为程序或其它事件(中断等)会改变SREG,结果很可能是一个死循环出不来了。如果定义成volatile型变量,编译的代码是这样的:每次要操作一个变量的时候都从内存中读取一次。 #define SREG (*(volatile unsigned char *)0x5F) 之后,可以进行如下基本操作,unsigned char temp,*ptr; temp=SREG;把SREG值保存到temp中 SREG=temp;把temp的值赋给SREG ptr = & SREG; 不知对否,大家试一下。

define用法以及

#define用法以及#define和typedef区别1.简单的define定义 #define MAXTIME 1000 2.define的“函数定义” define可以像函数那样接受一些参数,如下 #define max(x,y) (x)>(y)?(x):(y); 因为这个“函数”没有类型检查,就好像一个函数模板似的,没有模板那么安全就是了。 但是这样做的话存在隐患,例子如下: #define Add(a,b) a+b;如果遇到如:c * Add(a,b) * d的时候就会出现问题。 另外举一个例子: #define pin (int*); pin a,b; 本意是a和b都是int型指针,但是实际上变成int* a,b; a是int型指针,而b是int型变量。 这时应该使用typedef来代替define,这样a和b就都是int型指针了。 我们在定义的时候,养成一个良好的习惯,建议所有的层次都要加括号。 3.宏的单行定义(少见用法) #define A(x) T_##x #define B(x) #@x #define C(x) #x 我们假设:x=1,则有: A(1)------〉T_1 B(1)------〉'1' C(1)------〉"1" 4.define的多行定义 define可以替代多行的代码,例如MFC中的宏定义(非常的经典,虽然让人看了恶心)

#define MACRO(arg1, arg2) do { \ /* declarations */ \ stmt1; \ stmt2; \ /* ... */ \ } while(0) /* (no trailing ; ) */ 关键是要在每一个换行的时候加上一个"\" 5.在大规模的开发过程中,特别是跨平台和系统的软件里,define最重要的功能是条件编译。 就是: #ifdef WINDOWS ...... ...... #endif #ifdef LINUX ...... ...... #endif 可以在编译的时候通过#define设置编译环境。 6.如何定义宏、取消宏 #define [MacroName] [MacroValue] //定义宏 #undef [MacroName] //取消宏 #define PI (3.1415926) //普通宏 #define max(a,b) ((a)>(b)? (a),(b)) //带参数的宏 7.条件编译 #ifdef XXX…(#else) … #endif 例如 #ifdef DV22_AUX_INPUT #define AUX_MODE 3

嵌入式C语言运用(笔试面试前必看)

语言测试是招聘嵌入式系统程序员过程中必须而且有效的方法。这些年,我既参加也组织了许多这种测试,在这过程中我意识到这些测试能为面试者和被面试者提供许多有用信息,此外,撇开面试的压力不谈,这种测试也是相当有趣的。 从被面试者的角度来讲,你能了解许多关于出题者或监考者的情况。这个测试只是出题者为显示其对ANSI标准细节的知识而不是技术技巧而设计吗?这是个愚蠢的问题吗?如要你答出某个字符的ASCII值。这些问题着重考察你的系统调用和内存分配策略方面的能力吗?这标志着出题者也许花时间在微机上而不是在嵌入式系统上。如果上述任何问题的答案是"是"的话,那么我知道我得认真考虑我是否应该去做这份工作。 从面试者的角度来讲,一个测试也许能从多方面揭示应试者的素质:最基本的,你能了解应试者C 语言的水平。不管怎么样,看一下这人如何回答他不会的问题也是满有趣。应试者是以好的直觉做出明智的选择,还是只是瞎蒙呢?当应试者在某个问题上卡住时是找借口呢,还是表现出对问题的真正的好奇心,把这看成学习的机会呢?我发现这些信息与他们的测试成绩一样有用。 有了这些想法,我决定出一些真正针对嵌入式系统的考题,希望这些令人头痛的考题能给正在找工作的人一点帮助。这些问题都是我这些年实际碰到的。其中有些题很难,但它们应该都能给你一点启迪。 这个测试适于不同水平的应试者,大多数初级水平的应试者的成绩会很差,经验丰富的程序员应该有很好的成绩。为了让你能自己决定某些问题的偏好,每个问题没有分配分数,如果选择这些考题为你所用,请自行按你的意思分配分数。 预处理器(Preprocessor) 1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题) #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在这想看到几件事情: 1) #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等) 2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。 3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。 4) 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。 2 . 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。 #define MIN(A,B) ((A) <= (B) ? (A) : (B)) 这个测试是为下面的目的而设的: 1) 标识#define在宏中应用的基本知识。这是很重要的。因为在嵌入(inline)操作符变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。 2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else 更优化的代码,了解这个用法是很重要的。 3) 懂得在宏中小心地把参数用括号括起来 4) 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?

#define 用法大全

#define用法集锦[修正版] Definition[定义]: The #define Directive You can use the #define directive to give a meaningful name to a constant in your program. The two forms of the syntax are: Syntax #define identifier token-stringopt #define identifier[( identifieropt, ... , identifieropt )] token-stringopt Usage[用法]: 1.简单的define定义 #define MAXTIME 1000 一个简单的MAXTIME 就定义好了,它代表1000,如果在程序里面写 if(i(y)?(x):(y); 这个定义就将返回两个数中较大的那个,看到了吗?因为这个“函数”没有类型检查,就好像一个函数模板似的,当然,它绝对没有模板那么安全就是了。可以作为一个简单的模板来使用而已。 但是这样做的话存在隐患,例子如下: #define Add(a,b) a+b; 在一般使用的时候是没有问题的,但是如果遇到如:c * Add(a,b) * d 的时候就会出现问题,代数式的本意是a+b 然后去和c,d 相乘,但是因为使用了define(它只是一个简单的替换),所以式子实际上变成了c*a + b*d 另外举一个例子: #define pin (int*); pin a,b; 本意是a 和b 都是int 型指针,但是实际上变成int* a,b; a 是int 型指针,而 b 是int 型变量。 这是应该使用typedef 来代替define,这样a 和b 就都是int 型指针了。

java正确使用Volatile变量

【IT168 技术】简介: Java? 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 V olatile 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。在这期的 Java 理论与实践 中,Brian Goetz 将介绍几种正确使用 volatile 变量的模式,并针对其适用性限制提出一些建议。 Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。本文介绍了几种有效使用 volatile 变量的模式,并强调了几种不适合使用 volatile 变量的情形。 锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。 Volatile 变量 V olatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。V olatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。 出于简易性或可伸缩性的考虑,您可能倾向于使用 volatile 变量而不是锁。当使用 volatile 变量而非锁时,某些习惯用法(idiom)更加易于编码和阅读。此外,volatile 变量不会像锁那样造成线程阻塞,因此也很少造成可伸缩性问题。在某些情况下,如果读操作远远大于写操作,volatile 变量还可以提供优于锁的性能优势。 正确使用 volatile 变量的条件 您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件: 对变量的写操作不依赖于当前值。 该变量没有包含在具有其他变量的不变式中。 实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。 第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x 的值在操作期间保持不变, 而 volatile 变量无法实现这点。(然而,如果将值调整为只从单个线程写入,那么可以忽略第一个条件。) 大多数编程情形都会与这两个条件的其中之一冲突,使得 volatile 变量不能像 synchronized 那样普遍适用于实现线程安全。清单 1 显示了一个非线程安全的数值范围类。它包含了一个不变式 —— 下界总是小于或等于上界。

volatile作用

volatile的作用 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 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变量定义的意义和该用在哪里

volatile变量定义的意义和该用在哪里 一个定义为volaTIle的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volaTIle变量的几个例子: 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automaTICvariables) 3). 多线程应用中被几个任务共享的变量 回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volaTIle变量。不懂得volatile内容将会带来灾难。 假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。 1). 一个参数既可以是const还可以是volatile吗?解释为什么。 2). 一个指针可以是volatile 吗?解释为什么。 3). 下面的函数有什么错误: 下面是答案: 1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer 的指针时。 3). 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码: int square(volatile int *ptr) {

生产制造的八大浪费与消除

生产制造的八大浪费与消除 八大浪费是定义工厂在JIT生产方式中的,其浪费的含义与社会上通常所说的浪费有所区别。对于JIT来讲,凡是超出增加产品价值所必需的绝对最少的物料、设备、人力、场地和时间的部分都是浪费。因此,JIT生产方式所讲的工厂的浪费归纳为八大种,分别是:不良、修理的浪费,过分加工的浪费,动作的浪费,搬运的浪费,库存的浪费,制造过多过早的浪费,等待的浪费和管理的浪费,简称为八大浪费。 1.不良、修理的浪费 所谓不良、修理的浪费,指的是由于工厂内出现不良品,需要进行处置的时间、人力、物力上的浪费,以及由此造成的相关损失。这类浪费具体包括:材料的损失、不良品变成废品; 设备、人员和工时的损失;额外的修复、鉴别、追加检查的损失;有时需要降价处理产品,或者由于耽误出货而导致工厂信誉的下降。 2.加工的浪费 加工的浪费也叫过分加工的浪费,主要包含两层含义:第一是多余的加工和过分精确的加工,例如实际加工精度过高造成资源浪费;第二是需要多余的作业时间和辅助设备,还要增加生产用电、气压、油等能源的浪费,另外还增加了管理的工时。 3.动作的浪费 动作的浪费现象在很多企业的生产线中都存在,常见的动作浪费主要有以下12种:两手空闲、单手空闲、作业动作突然停止、作业动作过大、左右手交换、步行过多、转身的角度太大,移动中变换“状态”、不明技巧、伸背动作、弯腰动作以及重复动作和不必要的动作等,这些动作的浪费造成了时间和体力上的不必要消耗。 4.搬运的浪费 从JIT的角度来看,搬运是一种不产生附加价值的动作,而不产生价值的工作都属于浪费。搬运的浪费具体表现为放置、堆积、移动、整列等动作浪费,由此而带来物品移动所需空间的浪费、时间的浪费和人力工具的占用等不良后果。 国内目前有不少企业管理者认为搬运是必要的,不是浪费。因此,很多人对搬运浪费视而不见,更谈不上去消灭它。也有一些企业利用传送带或机器搬运的方式来减少人工搬运,这种做法

相关文档
最新文档