# 2009年8月12日

2 3 5 4 1--> 1 3 5 4 2 -->1 3 2 4 5 --> 1 2 3 4 5,最少经过三次交换，得到一个升序列。

```{3 ,7 ,1 ,6 ,2 ,4 ,8 ,5 }再和标准序列
{1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 }比较

posted @ 2009-08-12 11:37 newstar 阅读(224) | 评论 (0)编辑 收藏

# 2009年8月7日

LIS（最长递增序列问题 nlogn）
P:0<=i<n;k是序列a[0:i]的最长递增子序列的长度；b[k]是序列a[0:i]中所有长度为k的递增子序列中最小结尾元素的值。在有i-1到i的循环中，当a[i] >= b[k]时,k = k+1,b[k] = a[i];当a[i] < b[k]时，应当在b[1:k]中找到一个位置j，使得b[j-1]<=a[i]<b[j]，将b[j] = a[i],使得b序列满足性质.也就是说在b[1：k]序列中找到第一个比a[i]大的元素，将这个元素置为a[i]。为什么呢？ 因为有b序列的性质可以得到，b[k]是序列a[0:i]中所有长度为k的递增子序列中最小结尾元素的值，那么如果存在b[j-1]<=a[i]<b[j]，说明在a[0:i-1]中，长度为j-1的子序列的最小结束元素是b[j-1],长度为j的子序列的最小结束元素是b[j],i>j>j-1且b[j]>a[i]>b[j-1]，所以在a[0：i]中长度为j的子序列的最小结束元素是a[i]，因为它比b[j]小。从这也可以看出 b序列是有序的，可以用二分求解出a[i]放入的位置此算法关键：维护b序列的性质，二分求解
posted @ 2009-08-07 21:01 newstar 阅读(302) | 评论 (1)编辑 收藏

# 2009年7月9日

 "4Blocks" is a two player cooperative game played on a special board. The board is a grid composed of 1x1 square cells. There are two different kinds of blocks: '1' blocks and '4' blocks. '1' blocks are 1x1, and '4' blocks are 2x2: You must place blocks on the board so that their sides are aligned to the grid lines and no two blocks overlap. The final score is the sum of the values in each cell. '1' blocks are worth 1 point, and '4' blocks are worth 16 points because they cover 4 cells and each cell is worth 4 points.Your friend has taken his turn and placed a number of '1' blocks on the board. The current configuration is given in the vector grid. The j-th character of the i-th element of grid is '.' if the cell at row i, column j is empty, and '1' if your friend placed a '1' block in that cell. It is now your turn, and you can place any number of '1' or '4' blocks on the board, but you cannot remove any of the blocks that have already been placed. Return the maximum score that can be achieved. For example, the following images show one possible starting state, and the optimal placement of blocks from that state:The final score would be 4*16 + 6*1 = 70想法：求解最大的得分，也就是求出在这些空格中，可以摆放的最大的blocks的个数c，然后 16*c + n*m - 4*c既是其最大的得分。采用动态规划求解，设dp[x][y][s]表示在从方格(x,y)开始，y列的前一列的摆放状态为s,按列遍历到最后，能够摆放blocks最多有多少个。转移方程：(x,y)的摆放状态只与前一列相关。如果（x,y）被blocks覆盖，即判断前一列状态s&(1< g;    int dp(int x,int y,int mask)    {        int &ret = a[x][y][mask];        if(ret >= 0) return ret;        ret = 0;        if(y == m-1) return ret = 0;        if(x == n) return ret = dp(0,y+1,mask);        int nm = mask;        if(mask & (1 << x))        {            nm ^= (1< ret)            ret = temp;        return ret;    }    int maxScore(vector grid)    {        g = grid;        n = (int)g.size();        m = (int)g[0].size();        memset(a,0xff,sizeof(a));                int c = dp(0,0,0);        printf("%d\n",c);        return 12 * c + n*m;    }};
posted @ 2009-07-09 16:00 newstar 阅读(65) | 评论 (0)编辑 收藏

# 2009年7月5日

摘要: 注：    写得实在太好了！    原文出处：http://blog.csdn.net/rstevens/archive/2007/10/14/1824785.aspx1.   概述 根据以前学习内核源码的经验，在学习文件系统实现之前，我大概定了个目标：1、  建立一个清晰的全局...  阅读全文
posted @ 2009-07-05 01:12 newstar 阅读(38) | 评论 (0)编辑 收藏

——

makefile带来的好处就是——“自动化编译”，一旦写好，只需要一个make命令，整个工程完全自动编译，极大的提高了软件开发的效率。make是一个命令工具，是一个解释makefile中指令的命令工具，一般来说，大多数的IDE都有这个命令，比如：Delphi的make，Visual C++的nmake，Linux下GNU的make。可见，makefile都成为了一种在工程方面的编译方法。

——————————

Makefile 介绍
———————

make命令执行时，需要一个 Makefile 文件，以告诉make命令需要怎么样的去编译和链接程序。

1）如果这个工程没有编译过，那么我们的所有C文件都要编译并被链接。
2）如果这个工程的某几个C文件被修改，那么我们只编译被修改的C文件，并链接目标程序。
3）如果这个工程的头文件被改变了，那么我们需要编译引用了这几个头文件的C文件，并链接目标程序。

target ... : prerequisites ...
command
...
...

target也就是一个目标文件，可以是Object File，也可以是执行文件。还可以是一个标签（Label），对于标签这种特性，在后续的“伪目标”章节中会有叙述。

prerequisites就是，要生成那个target所需要的文件或是目标。

command也就是make需要执行的命令。（任意的Shell命令）

edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果找到，它会找文件中的第一个目标文件（target），在上面的例子中，他会找到“edit”这个文件，并把这个文件作为最终的目标文件。
3、如果edit文件不存在，或是edit所依赖的后面的 .o 文件的文件修改时间要比edit这个文件新，那么，他就会执行后面所定义的命令来生成edit这个文件。
4、如果edit所依赖的.o文件也存在，那么make会在当前文件中找目标为.o文件的依赖性，如果找到则再根据那一个规则生成.o文件。（这有点像一个堆栈的过程）
5、当然，你的C文件和H文件是存在的啦，于是make会生成 .o 文件，然后再用 .o 文件生命make的终极任务，也就是执行文件edit了。

edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

edit : \$(objects)
cc -o edit \$(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit \$(objects)

GNU的make很强大，它可以自动推导文件以及文件依赖关系后面的命令，于是我们就没必要去在每一个[.o]文件后都写上类似的命令，因为，我们的make会自动识别，并自己推导命令。

objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

edit : \$(objects)
cc -o edit \$(objects)

main.o : defs.h
kbd.o : defs.h command.h
command.o : defs.h command.h
display.o : defs.h buffer.h
insert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h

.PHONY : clean
clean :
rm edit \$(objects)

objects = main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o

edit : \$(objects)
cc -o edit \$(objects)

\$(objects) : defs.h
kbd.o command.o files.o : command.h
display.o insert.o search.o files.o : buffer.h

.PHONY : clean
clean :
rm edit \$(objects)

clean:
rm edit \$(objects)

.PHONY : clean
clean :
-rm edit \$(objects)

Makefile 总述
———————

Makefile里主要包含了五个东西：显式规则、隐晦规则、变量定义、文件指示和注释。

1、显式规则。显式规则说明了，如何生成一个或多的的目标文件。这是由Makefile的书写者明显指出，要生成的文件，文件的依赖文件，生成的命令。

2、隐晦规则。由于我们的make有自动推导的功能，所以隐晦的规则可以让我们比较粗糙地简略地书写Makefile，这是由make所支持的。

3、变量的定义。在Makefile中我们要定义一系列的变量，变量一般都是字符串，这个有点你C语言中的宏，当Makefile被执行时，其中的变量都会被扩展到相应的引用位置上。

4、文件指示。其包括了三个部分，一个是在一个Makefile中引用另一个Makefile，就像C语言中的include一样；另一个是指根据某些情况指定Makefile中的有效部分，就像C语言中的预编译#if一样；还有就是定义一个多行的命令。有关这一部分的内容，我会在后续的部分中讲述。

5、注释。Makefile中只有行注释，和UNIX的Shell脚本一样，其注释是用“#”字符，这个就像C/C++中的“//”一样。如果你要在你的Makefile中使用“#”字符，可以用反斜框进行转义，如：“\#”。

include <filename>

filename可以是当前操作系统Shell的文件模式（可以保含路径和通配符）

include foo.make *.mk \$(bar)

include foo.make a.mk b.mk c.mk e.mk f.mk

make 命令开始时，会把找寻include所指出的其它Makefile，并把其内容安置在当前的位置。就好像C/C++的＃i nclude指令一样。如果文件都没有指定绝对路径或是相对路径的话，make会在当前目录下首先寻找，如果当前目录下没有找到，那么，make还会在下面的几个目录下找：

1、如果make执行时，有“-I”或“--include-dir”参数，那么make就会在这个参数所指定的目录下去寻找。
2、如果目录<prefix>/include（一般是：/usr/local/bin或/usr/include）存在的话，make也会去找。

-include <filename>

GNU的make工作时的执行步骤入下：（想来其它的make也是类似）

1、读入所有的Makefile。
2、读入被include的其它Makefile。
3、初始化文件中的变量。
4、推导隐晦规则，并分析所有规则。
5、为所有的目标文件创建依赖关系链。
6、根据依赖关系，决定哪些目标要重新生成。
7、执行生成命令。

1- 5步为第一个阶段，6-7为第二个阶段。第一个阶段中，如果定义的变量被使用了，那么，make会把其展开在使用的位置。但make并不会完全马上展开， make使用的是拖延战术，如果变量出现在依赖关系的规则中，那么仅当这条依赖被决定要使用了，变量才会在其内部展开。

————

foo.o : foo.c defs.h # foo模块
cc -c -g foo.c

1、文件的依赖关系，foo.o依赖于foo.c和defs.h的文件，如果foo.c和defs.h的文件日期要比foo.o文件日期要新，或是foo.o不存在，那么依赖关系发生。
2、如果生成（或更新）foo.o文件。也就是那个cc命令，其说明了，如何生成foo.o这个文件。（当然foo.c文件include了defs.h文件）

targets : prerequisites
command
...

targets : prerequisites ; command
command
...

targets是文件名，以空格分开，可以使用通配符。一般来说，我们的目标基本上是一个文件，但也有可能是多个文件。

command是命令行，如果其不与“target: prerequisites”在一行，那么，必须以[Tab键]开头，如果和prerequisites在一行，那么可以用分号做为分隔。（见上）

prerequisites也就是目标所依赖的文件（或依赖目标）。如果其中的某个文件要比目标文件要新，那么，目标就被认为是“过时的”，被认为是需要重生成的。这个在前面已经讲过了。

clean:
rm -f *.o

print: *.c
lpr -p \$?
touch print

objects = *.o

objects := \$(wildcard *.o)

Makefile文件中的特殊变量“VPATH”就是完成这个功能的，如果没有指明这个变量，make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量，那么，make就会在当当前目录找不到的情况下，到所指定的目录中去找寻文件了。

1、vpath <pattern> <directories>

2、vpath <pattern>

3、vpath

vapth 使用方法中的<pattern>需要包含“%”字符。“%”的意思是匹配零或若干字符，例如，“%.h”表示所有以“.h”结尾的文件。 <pattern>指定了要搜索的文件集，而<directories>则指定了<pattern>的文件集的搜索的目录。例如：

vpath %.c foo
vpath % blish
vpath %.c bar

vpath %.c foo:bar
vpath % blish

clean:
rm *.o temp

.PHONY : clean

.PHONY: clean
clean:
rm *.o temp

all : prog1 prog2 prog3
.PHONY : all

prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o

prog2 : prog2.o
cc -o prog2 prog2.o

prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o

.PHONY: cleanall cleanobj cleandiff

cleanall : cleanobj cleandiff
rm program

cleanobj :
rm *.o

cleandiff :
rm *.diff

“make clean”将清除所有要被清除的文件。“cleanobj”和“cleandiff”这两个伪目标有点像“子程序”的意思。我们可以输入“make cleanall”和“make cleanobj”和“make cleandiff”命令来达到清除不同种类文件的目的。

Makefile 的规则中的目标可以不止一个，其支持多目标，有可能我们的多个目标同时依赖于一个文件，并且其生成的命令大体类似。于是我们就能把其合并起来。当然，多个目标的生成规则的执行命令是同一个，这可能会可我们带来麻烦，不过好在我们的可以使用一个自动化变量“\$@”（关于自动化变量，将在后面讲述），这个变量表示着目前规则中所有的目标的集合，这样说可能很抽象，还是看一个例子吧。

bigoutput littleoutput : text.g
generate text.g -\$(subst output,,\$@) > \$@

bigoutput : text.g
generate text.g -big > bigoutput
littleoutput : text.g
generate text.g -little > littleoutput

<targets ...>: <target-pattern>: <prereq-patterns ...>
<commands>
...

targets定义了一系列的目标文件，可以有通配符。是目标的一个集合。

target-parrtern是指明了targets的模式，也就是的目标集模式。

prereq-parrterns是目标的依赖模式，它对target-parrtern形成的模式再进行一次依赖目标的定义。

objects = foo.o bar.o

all: \$(objects)

\$(objects): %.o: %.c
\$(CC) -c \$(CFLAGS) \$< -o \$@

foo.o : foo.c
\$(CC) -c \$(CFLAGS) foo.c -o foo.o
bar.o : bar.c
\$(CC) -c \$(CFLAGS) bar.c -o bar.o

files = foo.elc bar.o lose.o

\$(filter %.o,\$(files)): %.o: %.c
\$(CC) -c \$(CFLAGS) \$< -o \$@
\$(filter %.elc,\$(files)): %.elc: %.el
emacs -f batch-byte-compile \$<

\$(filter %.o,\$(files))表示调用Makefile的filter函数，过滤“\$filter”集，只要其中模式为“%.o”的内容。其的它内容，我就不用多说了吧。这个例字展示了Makefile中更大的弹性。

main.o : main.c defs.h

cc -M main.c

main.o : main.c defs.h

gcc -M main.c的输出是：

main.o: main.c defs.h /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h \
/usr/include/bits/sched.h /usr/include/libio.h \
/usr/include/_G_config.h /usr/include/wchar.h \
/usr/include/bits/wchar.h /usr/include/gconv.h \
/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stdarg.h \
/usr/include/bits/stdio_lim.h

gcc -MM main.c的输出则是：

main.o: main.c defs.h

%.d: %.c
@set -e; rm -f \$@; \
\$(CC) -M \$(CPPFLAGS) \$< > \$@.\$\$\$\$; \
sed 's,\(\$*\)\.o[ :]*,\1.o \$@ : ,g' < \$@.\$\$\$\$ > \$@; \
rm -f \$@.\$\$\$\$

main.o : main.c defs.h

main.o main.d : main.c defs.h

sources = foo.c bar.c

include \$(sources:.c=.d)

————

@echo 正在编译XXX模块......

echo 正在编译XXX模块......

exec:
cd /home/hchen
pwd

exec:
cd /home/hchen; pwd

make 一般是使用环境变量SHELL中所定义的系统Shell来执行命令，默认情况下使用UNIX的标准Shell——/bin/sh来执行命令。但在MS- DOS下有点特殊，因为MS-DOS下没有SHELL环境变量，当然你也可以指定。如果你指定了UNIX风格的目录形式，首先，make会在SHELL所指定的路径中找寻命令解释器，如果找不到，其会在当前盘符中的当前目录中寻找，如果再找不到，其会在PATH环境变量中所定义的所有路径中寻找。MS- DOS中，如果你定义的命令解释器没有找到，其会给你的命令解释器加上诸如“.exe”、“.com”、“.bat”、“.sh”等后缀。

clean:
-rm -f *.o

subsystem:
cd subdir && \$(MAKE)

subsystem:
\$(MAKE) -C subdir

export <variable ...>

unexport <variable ...>

export variable = value

variable = value
export variable

export variable := value

variable := value
export variable

export variable += value

variable += value
export variable

subsystem:
cd subdir && \$(MAKE) MAKEFLAGS=

make: Entering directory `/home/hchen/gnu/make'.

make: Leaving directory `/home/hchen/gnu/make'

define run-yacc
yacc \$(firstword \$^)
mv y.tab.c \$@
endef

foo.c : foo.y
\$(run-yacc)

posted @ 2009-07-05 00:22 newstar 阅读(40) | 评论 (0)编辑 收藏

# 2009年7月1日

 TCP是面向连接的，所谓面向连接，就是当计算机双方通信时必需先建立连接，然后数据传送，最后拆除连接三个过程 并且TCP在建立连接时又分三步走： 第一步是请求端（客户端）发送一个包含SYN即同步（Synchronize）标志的TCP报文，SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号；  第二步，服务器在收到客户端的SYN报文后，将返回一个SYN+ACK的报文，表示客户端的请求被接受，同时TCP序号被加一，ACK即确认（Acknowledgement）。  第三步，客户端也返回一个确认报文ACK给服务器端，同样TCP序列号被加一，到此一个TCP连接完成。 然后才开始通信的第二步：数据处理。 这就是所说的TCP三次握手（Three-way Handshake）。  简单的说就是：（C：客户端，S：服务端） C：SYN到S S：如成功--返回给C（SYN+ACK） C：如成功---返回给S（ACK） 以上是正常的建立连接方式，但如下： 假设一个C向S发送了SYN后无故消失了，那么S在发出SYN+ACK应答报文后是无法收到C的ACK报文的（第三次握手无法完成），这种情况下S一般会重试（再次发送SYN+ACK给客户端）并等待一段时间后丢弃这个未完成的连接，这段时间的长度我们称为SYN Timeout，一般来说这个时间是分钟的数量级（大约为30秒-2分钟）；一个C出现异常导致S的一个线程等待1分钟并不是什么很大的问题，但如果有一个恶意的攻击者大量模拟这种情况，S将为了维护一个非常大的半连接列表而消耗非常多的资源----数以万计的半连接，即使是简单的保存并遍历也会消耗非常多的CPU时间和内存，何况还要不断对这个列表中的IP进行SYN+ACK的重试。实际上如果S的TCP/IP栈不够强大，最后的结果往往是堆栈溢出崩溃---即使S的系统足够强大，S也将忙于处理攻击者伪造的TCP连接请求而无暇理睬客户的正常请求（毕竟C的正常请求比率非常之小），此时从正常客户的角度看来，S失去响应，这种情况我们称作：服务器端受到了SYN Flood攻击（SYN洪水攻击）。  以上的例子常被称作DoS（拒绝服务攻击）与DDoS（分布式拒绝服务攻击） 注意：其中这儿的C和S都是相对的，对于现在的计算机来讲，只要自己的计算机建立任一服务，在一定情况下都可被称为S
posted @ 2009-07-01 11:10 newstar 阅读(196) | 评论 (0)编辑 收藏

1 #include <sys/cdefs.h>;
2 #include <string.h>;
3
4 /*
5 * sizeof(word) MUST BE A POWER OF TWO
6 * SO THAT wmask BELOW IS ALL ONES
7 */
8 typedef    int word;        /* "word" used for optimal copy speed */
9
10 #define    wsize    sizeof(word)
11 #define    wmask    (wsize - 1)
12
13 /*
14 * Copy a block of memory, handling overlap.
15 * This is the routine that actually implements
16 * (the portable versions of) bcopy, memcpy, and memmove.
17 * length 以Byte作为度量单位
18 */
19 #ifdef MEMCOPY
20 void *
21 memcpy(dst0, src0, length)
22 #else
23 #ifdef MEMMOVE
24 void *
25 memmove(dst0, src0, length)
26 #else
27 void
28 bcopy(src0, dst0, length)
29 #endif
30 #endif
31     void *dst0;
32     const void *src0;
33     register size_t length;
34 {
35     register char *dst = dst0;
36     register const char *src = src0;
37     register size_t t;
38
39     if (length == 0 || dst == src)        /* nothing to do */
40         goto done;
41
42     /*
43      * Macros: loop-t-times; and loop-t-times, t>;0
44      */
45 #define    TLOOP(s) if (t) TLOOP1(s)
46 #define    TLOOP1(s) do { s; } while (--t)
47
48     if ((unsigned long)dst < (unsigned long)src) {
49         /*
50          * Copy forward.
51          */
52         t = (int)src;    /* only need low bits */
53                 /*这样做的目的是由于下面的TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize)拷贝用的是32位为一个单位
54                  *所以 这是的指针所指向的地址是最后两位是00（这个想法我还没验证）所以在匹配之前一定要将DES 和 SRC指针对齐到最后两位为00才行
55                  */
56         if ((t | (int)dst) & wmask) {  // 如果源和目标地址的最后两位不是00的话
57             /* 对齐操作数？？？
58              * Try to align operands.  This cannot be done
59              * unless the low bits match.
60              */
61             if ((t ^ (int)dst) & wmask || length < wsize)
62                 t = length;               //如果目标和源的地址最后两位参差不齐如 11 10 只能以Byte为单位拷贝
63             else
64                 t = wsize - (t & wmask);  //如果目标和源的地址最后两位如 10 10相等的话 把他们同时移到00来做
65             length -= t;
66             TLOOP1(*dst++ = *src++);
67         }
68         /*
69          * Copy whole words, then mop up any trailing bytes.
70          */
71         t = length / wsize;
72         TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize);
73         t = length & wmask;
74         TLOOP(*dst++ = *src++);
75     } else {
76         /*
77          * Copy backwards.  Otherwise essentially the same.
78          * Alignment works as before, except that it takes
80          */
81         src += length;
82         dst += length;
83         t = (int)src;
84         if ((t | (int)dst) & wmask) {
85             if ((t ^ (int)dst) & wmask || length <= wsize)
86                 t = length;
87             else
89             length -= t;
90             TLOOP1(*--dst = *--src);
91         }
92         t = length / wsize;
93         TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src);
94         t = length & wmask;
95         TLOOP(*--dst = *--src);
96     }
97 done:
98 #if defined(MEMCOPY) || defined(MEMMOVE)
99     return (dst0);
100 #else
101     return;
102 #endif
103 }

BSD的memcpy是经过优化的代码，主要是从SRC到DES的赋值操作时，使用了WORD（也就是INT)型指针，每次拷贝32位。他正好符合CACHE中32位的原则，效率比较高。大概是8位赋值的效率的好几倍。
但是计算机架构中，数据项只能存在于于地址是数据项的大小的整数倍上，如DOUBLE型只能存在于8的整数倍如8008而不能存在于1006的地址上，如果跨界存取就有可能使原子数据项跨越一个页或CACHE的边界，不但会影响拷贝性能，严重时还会产生BUS ERROR（core dumped）的错误，所以在使用WORD指针前一定要将DES和SRC指针对齐到被4（sizeof(int) = 4 对于32位系统而言) 整除的位置。但如果DES和SRC的最后两位不相等的话，不管怎么移都不可能保证DES和SRC指针移动相等的长度后都可以被4整除，所以这时只能采用每8位拷贝一次，只能采用CHAR 型指针。
posted @ 2009-07-01 10:41 newstar 阅读(459) | 评论 (0)编辑 收藏

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;
}

1. 编译器的优化  (请高手帮我看看下面的理解)

2. 在什么情况下会出现(如1楼所说)

1). 并行设备的硬件寄存器（如：状态寄存器）
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量

“易变”是因为外在因素引起的，象多线程，中断等，并不是因为用volatile修饰了的变量就是“易变”了，假如没有外因，即使用volatile定义，它也不会变化；

－－－－－－－－－－－－简明示例如下：－－－－－－－－－－－－－－－－－－

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 <stdio.h>
void main()

int i=10;
int a = i;
printf("i= %d",a);
//下面汇编语句的作用就是改变内存中i的值，但是又不让编译器知道
__asm {
mov dword ptr [ebp-4], 20h

int b = i;
printf("i= %d",b);
}

i = 10
i = 32

i = 10
i = 10

＃i nclude <stdio.h>
void main()

volatile int i=10;
int a = i;
printf("i= %d",a);
__asm {
mov dword ptr [ebp-4], 20h

int b = i;
printf("i= %d",b);
}

i = 10
i = 32

－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－－

volatile对应的变量可能在你的程序本身不知道的情况下发生改变

--------------------------------------------------------------------------------

for ( int i=0; i<100000; i++);

for ( volatile int i=0; i<100000; i++);

volatile的本意是“易变的”

static int i=0;

int main(void)

...
while (1)

if (i) dosomething();

/* Interrupt service routine. */
void ISR_2(void)

i=1;

1、中断服务程序中修改的供其它程序检测的变量需要加volatile；

2、多任务环境下各任务间共享的标志应该加volatile；

3、存储器映射的硬件寄存器通常也要加volatile说明，因为每次对它的读写都可能由不同意义；

posted @ 2009-07-01 10:20 newstar 阅读(92) | 评论 (0)编辑 收藏