|
|
July 13 今天看到csdn的《09年2月编程语言排行榜:商业编程语言的王者之争》,明白自己掌握的开发语言简直就是凤毛麟角。不过很明白的看到,java依然是用的最多编程语言。C依然很强大。
  另外,有blog把开发语言比作信仰,很有意思:如下:
原文来自If programming languages were religions,很有意思,可以从宗教的角度来看看各种常见语言的特点。(这里丝毫没有要找出不同语言优劣的意思,每个人都有信仰自由)
C是犹太教——很古老而且戒律很多,但大多数人都熟悉并尊重其戒律。问题是很难皈依它,你要么开始就信仰它,要么会认为它简直太疯狂了。而且,一旦事情出了差错,人们就会怪罪于它。
Java是正统基督教——理论上来说它基于C,但它去掉了很多老的戒律,以至于跟原教旨已经大相径庭。另外,它新加了一套严格的教义,追随者们相信这些比原来的教义更为重要。他们认为这是世界上最好的语言,而且会将所有的异端烧死在刑柱上。
PHP是Cafeteria基督教——与Java在Web开发领域进行竞争。它引入了C和Java的一些概念,但只限于它所喜欢的。也许它不像其它语言那样条理清楚,但至少给你了更多自由,看起来还算一个整体。而且不会有下地狱(goto hell)这样的说法。
C++是伊斯兰教——来源于C,不仅保持了后者的戒律,还变本加厉加入了一套新的复杂戒律。它是如此地多才多艺,可用于构建任何事物,从弥天大罪到美丽的艺术品,追随者们相信它才是终极语言,并会被持不同意见的人激怒。更可怕的是,如果你冒犯了它或者是它的创始人,你的生命都会受到威胁。
C#是摩门教——乍看之下,它跟Java一样,不过仔细一看就会发现它被单一组织所控制(很多Java的追随者将其视为恶魔),而且很多概念都不相同。你在暗忖,它也许不错吧,呃。。。希望你不会被Java的追随者们歧视。
Lisp是佛教——没有语法,没有核心教义,也没有让人膜拜的神。就看你的造化了——需要你有足够的悟性。有人说它压根儿就不是一门语言,有的人却认为它是唯一有意义的语言。
Haskell是道教——与其它语言差别太大,以至于很多人怀疑它到底能做什么。其追随者相信它才是通往智慧的正道,只是这些智慧为大多数世俗之人力所不及。
Erlang是印度教——又一种奇怪的语言,看起来没什么用处,但不像大多数其它的现代语言,它建立在“同步之神”的基础上。
Perl是巫毒教——一套神秘的、难以理解的咒语,会让你的灵魂永久地堕落。老板在周五晚上九点吩咐你一项紧急任务,这时就得靠它了。(想起兽族部落的巫毒商店,而且它与萨满教有些类似)
Lua是巫术——一种泛神论的语言,容易与不同文化和地区相融合。它的代码非常自由,其中的一些技术在传统语言的用户看来是不可思议的。它与月亮有紧密的联系。
Ruby是新异教主义——不同语言和理念的混合体,可以被看作是一种语言。其支持者群体成长迅速,尽管很多人面带疑色地看着他们,但基本上他们还是善良的,无意于伤害他人。
Python是人文主义——简单、自由,它只需要你具备常识。很多追随者声称,Python帮助他们卸下了其它语言带来的担子,从而再次找到编程的乐趣。有些人还说它就是一种伪代码。
COBOL是古异教主义——很久以前,它曾经统治着大片地区,现在却差不多死掉了。尽管很多人都在其神的仪式中留下了伤疤,直到今天仍然有人追随着它。
APL是山达基教——尽管有很多人声称要追随它,但你总会怀疑它是一个大大的恶作剧。
LOLCODE是阿里乌斯教——它很神秘,产生于互联网,尽管有人在努力地开发和散布,但没人会对它当真。
Visual Basic是撒旦教——可惜你并不是真的需要将灵魂出卖。。。
最后,说说我的信仰吧:开始是撒旦教,发现它不太强势,于是找到摩门教,感觉很好,但发现它被单一组织控制,很不爽!后进了正统基督教,从此忠实地追随,不过偶尔会偷偷地跑到摩门教和Cafeteria基督教那边去看看。
April 04 运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础是动态
方法调度,它是一种在运行时而不是在编译期调用重载方法的机制,下面就继承和接口实现两方面谈谈java运行时多态性的实现。 一、通过继承中超类对象引用变量引用子类对象来实现 举例说明:
//定义超类superA class superA { int i = 100; void fun() { System.out.println(“This is superA”); } } //定义superA的子类subB class subB extends superA { int m = 1; void fun() { System.out.println(“This is subB”); } } //定义superA的子类subC class subC extends superA { int n = 1; void fun() { System.out.println(“This is subC”); } }
class Test { public static void main(String[] args) { superA a; subB b = new subB(); subC c = new subC(); a=b; a.fun(); (1) a=c; a.fun(); (2) } } | 运行结果为: This is subB This is subC
上述代码中subB和subC是超类superA的子类,我们在类Test中声明了3个引用变量a, b,
c,通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。也许有人会问:“为什么(1)和(2)不输出:This is
superA”。java
的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是
在超类中定义过的,也就是说被子类覆盖的方法。 所以,不要被上例中(1)和(2)所迷惑,虽然写成a.fun(),但是由于
(1)中的a被b赋值,指向了子类subB的一个实例,因而(1)所调用的fun()实际上是子类subB的成员方法fun(),它覆盖了超类
superA的成员方法fun();同样(2)调用的是子类subC的成员方法fun()。 另外,如果子类继承的超类是一个抽象类,虽然抽象类不能通过new操作符实例化,但是可以创建抽象类的对象引用指向子类对象,以实现运行时多态性。具体的实现方法同上例。 不过,抽象类的子类必须覆盖实现超类中的所有的抽象方法,否则子类必须被abstract修饰符修饰,当然也就不能被实例化了。 二、通过接口类型变量引用实现接口的类的对象来实现 接口的灵活性就在于“规定一个类必须做什么,而不管你如何做”。我们可以定义一个接口类型的引用变量来引用实现接口的类的实例,当这个引用调用方法时,它会根据实际引用的类的实例来判断具体调用哪个方法,这和上述的超类对象引用访问子类对象的机制相似。 举例说明:
//定义接口InterA interface InterA { void fun(); } //实现接口InterA的类B class B implements InterA { public void fun() { System.out.println(“This is B”); } }
//实现接口InterA的类C class C implements InterA { public void fun() { System.out.println(“This is C”); } }
class Test { public static void main(String[] args) { InterA a; a= new B(); a.fun(); a = new C(); a.fun(); } } | 输出结果为: This is B This is C 上例中类B和类C是实现接口InterA的两个类,分别实现了接口的方法fun(),通过将类B和类C的实例赋给接口引用a而实现了方法在运行时的动态绑定,充分利用了“一个接口,多个方法”展示了Java的动态多态性。 需要注意的一点是:Java在利用接口变量调用其实现类的对象的方法时,该方法必须已经在接口中被声明,而且在接口的实现类中该实现方法的类型和参数必须与接口中所定义的精确匹配。 结束语 以上就是java运行时多态性的实现方法,大家在编程过程中可以灵活运用,但是在性能要求较高的代码中不提倡运用运行时多态,毕竟Java的运行时动态方法调用较之普通的方法调用的系统开销是比较大的。 March 23 arm汇编语言调用C函数之参数传递
|
|
|
对于ARM体系来说,不同语言撰写的函数之间相互调用(mix calls)遵循的是
ATPCS(ARM-Thumb Procedure Call
Standard),ATPCS主要是定义了函数呼叫时参数的传递规则以及如何从函数返回,关于ATPCS的详细内容可以查看ADS1.2
Online Books ——Developer
Guide的2.1节。这篇文档要讲的是汇编代码中对C函数调用时如何进行参数的传递以及如何从C函数正确返回。 不同于x86的参数传递规则,ATPCS建议函数的形参不超过4个,如果形参个数少于或等于4,则形参由R0,R1,R2,R3四个寄存器进行传递;若形参个数大于4,大于4的部分必须通过堆栈进行传递。 我们先讨论一下形参个数为4的情况.
实例1: test_asm_args.asm //-------------------------------------------------------------------------------- IMPORT test_c_args ;声明test_c_args函数 AREA TEST_ASM, CODE, READONLY EXPORT test_asm_args test_asm_args STR lr, [sp, #-4]! ;保存当前lr ldr r0,=0x10 ;参数 1 ldr r1,=0x20 ;参数 2 ldr r2,=0x30 ;参数 3 ldr r3,=0x40 ;参数 4 bl test_c_args ;调用C函数 LDR pc, [sp], #4 ;将lr装进pc(返回main函数) END test_c_args.c //-------------------------------------------------------------------------------- void test_c_args(int a,int b,int c,int d) { printk("test_c_args:\n"); printk("%0x %0x %0x %0x\n",a,b,c,d); } main.c //-------------------------------------------------------------------------------- int main() { test_asm_args(); for(;;); }
程序从main函数开始执行,main调用了test_asm_args,test_asm_args调用了test_c_args,最后从
test_asm_args返回main。代码分别使用了汇编和C定义了两个函数,test_asm_args 和
test_c_args,test_asm_args调用了test_c_args,其参数的传递方式就是向R0~R3分别写入参数值,之后使用bl语句
对test_c_args进行调用。其中值得注意的地方是用红色标记的语句,test_asm_args在调用test_c_args之前必须把当前的
lr入栈,调用完test_c_args之后再把刚才保存在栈中的lr写回pc,这样才能返回到main函数中。 如果test_c_args的参数是8个呢?这种情况test_asm_args应该怎样传递参数呢? 实例2: test_asm_args.asm //-------------------------------------------------------------------------------- IMPORT test_c_args ;声明test_c_args函数 AREA TEST_ASM, CODE, READONLY EXPORT test_asm_args test_asm_args STR lr, [sp, #-4]! ;保存当前lr ldr r0,=0x1 ;参数 1 ldr r1,=0x2 ;参数 2 ldr r2,=0x3 ;参数 3 ldr r3,=0x4 ;参数 4 ldr r4,=0x8 str r4,[sp,#-4]! ;参数 8 入栈 ldr r4,=0x7 str r4,[sp,#-4]! ;参数 7 入栈 ldr r4,=0x6 str r4,[sp,#-4]! ;参数 6 入栈 ldr r4,=0x5 str r4,[sp,#-4]! ;参数 5 入栈 bl test_c_args_lots ADD sp, sp, #4 ;清除栈中参数 5,本语句执行完后sp指向 参数6 ADD sp, sp, #4 ;清除栈中参数 6,本语句执行完后sp指向 参数7 ADD sp, sp, #4 ;清除栈中参数 7,本语句执行完后sp指向 参数8 ADD sp, sp, #4 ;清除栈中参数 8,本语句执行完后sp指向 lr LDR pc, [sp],#4 ;将lr装进pc(返回main函数) END test_c_args.c //-------------------------------------------------------------------------------- void test_c_args(int a,int b,int c,int d,int e,int f,int g,int h) { printk("test_c_args_lots:\n"); printk("%0x %0x %0x %0x %0x %0x %0x %0x\n", a,b,c,d,e,f,g,h); } main.c //-------------------------------------------------------------------------------- int main() { test_asm_args(); for(;;); }
这部分的代码和实例1的代码大部分是相同的,不同的地方是test_c_args的参数个数和test_asm_args的参数传递方式。 在test_asm_args中,参数1~参数4还是通过R0~R3进行传递,而参数5~参数8则是通过把其压入堆栈的方式进行传递,不过要注意这四个入栈参数的入栈顺序,是以参数8->参数7->参数6->参数5的顺序入栈的。 直到调用test_c_args之前,堆栈内容如下: sp->+----------+ | 参数5 | +----------+ | 参数6 | +----------+ | 参数7 | +----------+ | 参数8 | +----------+ | lr | +----------+ test_c_args执行返回后,则设置sp,对之前入栈的参数进行清除,最后将lr装入pc返回main函数,在执行 LDR pc, [sp],#4 指令之前堆栈内容如下: +----------+ | 参数5 | +----------+ | 参数6 | +----------+ | 参数7 | +----------+ | 参数8 | sp->+----------+ | lr | +----------+
|
| |
原文地址
http://blog.chinaunix.net/u2/70832/showart_1019133.html |
February 12 1.概述 许多初学者对C/C 语言中的void及void指针类型不甚理解,因此在使用上出现了一些错误。本文将对void关键字的深刻含义进行解说,并 详述void及void指针类型的使用方法与技巧。 2.void的含义 void的字面意思是“无类型”,void *则为“无类型指针”,void *可以指向任何类型的数据。 void几乎只有“注释”和限制程序的作用,因为从来没有人会定义一个void变量,让我们试着来定义: void a; 这行语句编译时会出错,提示“illegal use of type 'void'”。不过,即使void a的编译不会出错,它也没有任何实际意义。 void真正发挥的作用在于: (1) 对函数返回的限定; (2) 对函数参数的限定。 我们将在第三节对以上二点进行具体说明。 众所周知,如果指针p1和p2的类型相同,那么我们可以直接在p1和p2间互相赋值;如果p1和p2指向不同的数据类型,则必须使用强制类型 转换运算符把赋值运算符右边的指针类型转换为左边指针的类型。 例如: float *p1; int *p2; p1 = p2; 其中p1 = p2语句会编译出错,提示“'=' : cannot convert from 'int *' to 'float *'”,必须改为: p1 = (float *)p2; 而void *则不同,任何类型的指针都可以直接赋值给它,无需进行强制类型转换: void *p1; int *p2; p1 = p2; 但这并不意味着,void *也可以无需强制类型转换地赋给其它类型的指针。因为“无类型”可以包容“有类型”,而“有类型”则不能包 容“无类型”。道理很简单,我们可以说“男人和女人都是人”,但不能说“人是男人”或者“人是女人”。下面的语句编译出错: void *p1; int *p2; p2 = p1; 提示“'=' : cannot convert from 'void *' to 'int *'”。 3.void的使用 下面给出void关键字的使用规则: 规则一 如果函数没有返回值,那么应声明为void类型 在C语言中,凡不加返回值类型限定的函数,就会被编译器作为返回整型值处理。但是许多程序员却误以为其为void类型。例如: add ( int a, int b ) { return a b; } int main(int argc, char* argv[]) { printf ( "2 3 = %d", add ( 2, 3) ); } 程序运行的结果为输出: 2 3 = 5 这说明不加返回值说明的函数的确为int函数。 林锐博士《高质量C/C 编程》中提到:“C 语言有很严格的类型安全检查,不允许上述情况(指函数不加类型声明)发生”。可是编译 器并不一定这么认定,譬如在Visual C 6.0中上述add函数的编译无错也无警告且运行正确,所以不能寄希望于编译器会做严格的类型检查。 因此,为了避免混乱,我们在编写C/C 程序时,对于任何函数都必须一个不漏地指定其类型。如果函数没有返回值,一定要声明为void类 型。这既是程序良好可读性的需要,也是编程规范性的要求。另外,加上void类型声明后,也可以发挥代码的“自注释”作用。代码的“自注 释”即代码能自己注释自己。 规则二如果函数无参数,那么应声明其参数为void 在C 语言中声明一个这样的函数: int function(void) { return 1; } 则进行下面的调用是不合法的: function(2); 因为在C 中,函数参数为void的意思是这个函数不接受任何参数。 我们在Turbo C 2.0中编译: #include "stdio.h" fun() { return 1; } main() { printf("%d",fun(2)); getchar(); } 编译正确且输出1,这说明,在C语言中,可以给无参数的函数传送任意类型的参数,但是在C 编译器中编译同样的代码则会出错。在C 中,不能向无参数的函数传送任何参数,出错提示“'fun' : function does not take 1 parameters”。 所以,无论在C还是C 中,若函数不接受任何参数,一定要指明参数为void。 规则三 小心使用void指针类型 按照ANSI(American National Standards Institute)标准,不能对void指针进行算法操作,即下列操作都是不合法的: void * pvoid; pvoid ; //ANSI:错误 pvoid = 1; //ANSI:错误 //ANSI标准之所以这样认定,是因为它坚持:进行算法操作的指针必须是确定知道其指向数据类型大小的。 //例如: int *pint; pint ; //ANSI:正确 pint 的结果是使其增大sizeof(int)。 但是大名鼎鼎的GNU(GNU's Not Unix的缩写)则不这么认定,它指定void *的算法操作与char *一致。 因此下列语句在GNU编译器中皆正确: pvoid ; //GNU:正确 pvoid = 1; //GNU:正确 pvoid 的执行结果是其增大了1。 在实际的程序设计中,为迎合ANSI标准,并提高程序的可移植性,我们可以这样编写实现同样功能的代码: void * pvoid; (char *)pvoid ; //ANSI:正确;GNU:正确 (char *)pvoid = 1; //ANSI:错误;GNU:正确 GNU和ANSI还有一些区别,总体而言,GNU较ANSI更“开放”,提供了对更多语法的支持。但是我们在真实设计时,还是应该尽可能地迎合 ANSI标准。 规则四如果函数的参数可以是任意类型指针,那么应声明其参数为void * 典型的如内存操作函数memcpy和memset的函数原型分别为: void * memcpy(void *dest, const void *src, size_t len); void * memset ( void * buffer, int c, size_t num ); 这样,任何类型的指针都可以传入memcpy和memset中,这也真实地体现了内存操作函数的意义,因为它操作的对象仅仅是一片内存,而不 论这片内存是什么类型。如果memcpy和memset的参数类型不是void *,而是char *,那才叫真的奇怪了!这样的memcpy和memset明显不是一个 “纯粹的,脱离低级趣味的”函数! 下面的代码执行正确: //示例:memset接受任意类型指针 int intarray[100]; memset ( intarray, 0, 100*sizeof(int) ); //将intarray清0 //示例:memcpy接受任意类型指针 int intarray1[100], intarray2[100]; memcpy ( intarray1, intarray2, 100*sizeof(int) ); //将intarray2拷贝给intarray1 有趣的是,memcpy和memset函数返回的也是void *类型,标准库函数的编写者是多么地富有学问啊! 规则五 void不能代表一个真实的变量 下面代码都企图让void代表一个真实的变量,因此都是错误的代码: void a; //错误 function(void a); //错误 void体现了一种抽象,这个世界上的变量都是“有类型”的,譬如一个人不是男人就是女人(还有人妖?)。 void的出现只是为了一种抽象的需要,如果你正确地理解了面向对象中“抽象基类”的概念,也很容易理解void数据类型。正如不能给抽 象基类定义一个实例,我们也不能定义一个void(让我们类比的称void为“抽象数据类型”)变量。
February 05 --------------------------------------------------------------- 3.3.8 将记号转换为字符串 [C语言参考手册,p38(r54)] --------------------------------------------------------------- 标准C语言中有一种机制可以将宏参数(扩展之后)转换为字符串型常量。在此之前,编程人员要利用许多C语言预处理器中的漏洞以不同方式达到相同结果。
在标准C语言中,宏定义中出现的#记号被当作一元“字符串化”运算符,后面为宏正式参数名。宏扩展期间,#和正式参数名换成相应的包含在字符串引号当中的
实际参数。生成字符串时,记号参数表中的每个空白序列换成一个空格符,任何嵌人引号和反斜杠前面加上一个反斜杠以保留其在字符串中的含义。参数开头和末尾
的空白符忽略,因此空参数扩展为空字符串""(即使逗号之间有空白符)。
例:考虑TEST宏的标准c语言定义
#define TEST(a,b) printf(#a "<" #b "=%d\n", (a)<(b)) 语句TEST(0,0xFFFF);TEST('\n', 10);展开如下: printf("0" "<" "0xFFFF" "=%d\n", (0)<(0xFFFF)); printf("'\\n'" "<" "10 "=%d\n", ('\n')<(10));
--------------------------------------------------------------- 3.3.9 宏扩展中的记号合并 [C语言参考手册,p38(r54)] --------------------------------------------------------------- 标准C语言中合并记号形成新记号时,由宏定义中的合并运算符##控制。重新扫描更多宏之前,宏替换表中任何运算符##中间的两个记号合并成一个记号。因此必须有这种记号:##不能放在替换表开头或末尾。如果合并之后得不到有效记号,则结果是未定义的。
例子: #define TEMP(i) temp ## i TEMP(1) = TEMP(2 + k) + x; 预处理之后变成: temp1 = temp2 + k + x;
上例中,扩展TEMP()+x时可能出现一个奇妙的情形。宏定义是有效的,但##右边没有要组合的记号(除非遇到+,但这不是我们所要的)。要解决这个问
题,应把正式参数i看成是专为##扩展的特殊的“空”记号。这样,TEMP()+x扩展的结果将如我们预料的那样为temp+x。 不能用记号拼接产生通用字符名。
和宏参数转换成字符串时一样(见3.3.8节),编程人员可以利用许多非标准C语言实现中的漏洞获得这种合并功能。尽管C语言的原始定义明确地把宏体描述
成记号序列,而不是字符序列,但许多语言编译器把宏体当作字符序列一样扩展和重新扫描。这在编译器处理注释语句时更加明显,编译器将注释语句完全删除而不
是换成空格,因此一些巧妙编写的程序就可以利用这个特性。 例:考虑下列例子: #define INC ++ #define TAB internal_table #define INCTAB table_of_increments #define CONC(x,y) x/**/y CONC(INC,TAB)
标准C语言将CONC体解释为两个记号x和y,用空格分开(注释语句变成空格)。调用CONC(INC,TAB)扩展为两个记号INC
TAB。但有些非标准C语言实现直接删除注释语句,然后重新扫描宏体中的记号,这样就把CONC(INC,TAB)扩展为一个记号INCTAB;
----------------------------------------------- 步骤 标准C语言扩展 可能的非标准扩展 ----------------------------------------------- 1 CONC(INC,TAB) CONC(INC,TAB) 2 INC/**/TAB INC/**/TAB 3 INC TAB INCTAB 4 ++ internal_table table_of_increments
测试代码:
#define XVARNAME(i) x##i #define PXVARNAME(n) printf("x"#n" = %d\n",x##n)
int k = 0; int b = 0; int XVARNAME(k)=12; PXVARNAME(k + 1); //xk + 1 = 13 PXVARNAME(k); //xk = 12 b = XVARNAME(k) + 0; printf("b = %d, k = %d\r\n", b, k); //b = 12, k = 0 b = XVARNAME(k + 10) + 0; printf("b = %d, k = %d\r\n", b, k); //b = 22, k = 0
--------------------------------------------------------------- 3.3.10 宏中的可变参数表 [C语言参考手册,p38(r54)] notice: Visual C++ 直至8.0版才开始支持C99的可变宏定义 --------------------------------------------------------------- #ifdef DEBUG #define myprintf(...) fprintf(stderr, __VA_ARGS__) #else #define myprintf(...) fprintf(__VA_ARGS__) #endif
特别地: #define make_em_str(...) #__VA_ARGS__ 下列调用 make_em_str(a,b,c,d) 展开成字符串 "a,b,c,d" February 01 区别:
从DESCRIPTION看来,两者的功能基本相同,唯一不同的是,当 dest 和 src 有重叠的时候选用不同的函数可能会造成不同的结果。不妨写个小程序来测一下:
0 #i nclude <string.h>
1 #i nclude <stdio.h>
2
3 int main()
4 {
5 int i = 0;
6 int a[10];
7
8 for(i; i < 10; i++)
9 {
10 a[i] = i;
11 }
12
13 memcpy(&a[4], a, sizeof(int)*6);
14
15 for(i = 0; i < 10; i++)
16 {
17 printf("%d ",a[i]);
18 }
20
21 printf("\n");
22 return 0;
23 }
很简单的小程序!不过已经足以达到我的目的了:)将上面代码gcc之后再运行,结果为:0 1 2 3 0 1 2 3 0 1 。
再把第13行改成:memmove(&a[4], a, sizeof(int)*6),重新gcc再运行,结果为:0 1 2 3 0 1 2 3 4 5 !
呵呵,两者的区别出现了。不过其实这样还不够,继续修改13行: memmove(a, &a[4], sizeof(int)*6) //也就是将源、目的置换一下而已
重新gcc编译再运行,结果为:4 5 6 7 8 9 6 7 8 9 。
还不够,继续修改13行为: memcpy(a, &a[4], sizeof(int)*6); gcc并运行,结果仍为: 4 5 6 7 8 9 6 7 8 9 !
至此真相已经大白了。对比上面四个结果,不难得出以下结论:
1. 当 src 和 dest 所指内存区有重叠时,memmove 相对 memcpy 能提供保证:保证能将 src 所指内存区的前 n 个字节正确的拷贝到 dest 所指内存中;
2. 当 src 地址比 dest 地址低时,两者结果一样。换句话说,memmove 与 memcpy 的区别仅仅体现在 dest 的头部和 src 的尾部有重叠的情况下;
memcpy
代码:
;***
;memcpy.asm - contains memcpy and memmove routines
;
; Copyright (c) 1986-1997, Microsoft Corporation. All right reserved.
;
;Purpose:
; memcpy() copies a source memory buffer to a destination buffer.
; Overlapping buffers are not treated specially, so propogation may occur.
; memmove() copies a source memory buffer to a destination buffer.
; Overlapping buffers are treated specially, to avoid propogation.
;
;*******************************************************************************
;***
;memcpy - Copy source buffer to destination buffer
;
;Purpose:
; memcpy() copies a source memory buffer to a destination memory buffer.
; This routine does NOT recognize overlapping buffers, and thus can lead
; to propogation.
; For cases where propogation must be avoided, memmove() must be used.
;
; Algorithm:
void* memcpy(void* dest, void* source, size_t count)
{
void* ret = dest;
//copy from lower address to higher address
while (count--)
*dest++ = *source;
return ret;
}
memmove
memmove - Copy source buffer to destination buffer
;
;Purpose:
; memmove() copies a source memory buffer to a destination memory buffer.
; This routine recognize overlapping buffers to avoid propogation.
; For cases where propogation is not a problem, memcpy() can be used.
;
; Algorithm:
void* memmove(void* dest, void* source, size_t count)
{
void* ret = dest;
if (dest <= source || dest >= (source + count))
{
//Non-Overlapping Buffers
//copy from lower addresses to higher addresses
while (count --)
*dest++ = *source++;
}
else
{
//Overlapping Buffers
//copy from higher addresses to lower addresses
dest += count - 1;
source += count - 1;
while (count--)
*dest-- = *source--;l
}
return ret;
}
另一种实现:
void* mymemcpy( void* dest, const void* src, size_t count )
{
char* d = (char*)dest;
const char* s = (const char*)src;
// int n = (count + 7) / 8; // count > 0 assumed
int n = count >> 3;
switch( count & 7 )
{
do { *d++ = *s++;
case 7: *d++ = *s++;
case 6: *d++ = *s++;
case 5: *d++ = *s++;
case 4: *d++ = *s++;
case 3: *d++ = *s++;
case 2: *d++ = *s++;
case 1: *d++ = *s++;
case 0 } //while (--n > 0);
while (n-- > 0)
}
return dest;
}
字符分类:
iswalnum() isalnum() 测试字符是否为数字或字母
iswalpha() isalpha() 测试字符是否是字母
iswcntrl() iscntrl() 测试字符是否是控制符
iswdigit() isdigit() 测试字符是否为数字
iswgraph() isgraph() 测试字符是否是可见字符
iswlower() islower() 测试字符是否是小写字符
iswprint() isprint() 测试字符是否是可打印字符
iswpunct() ispunct() 测试字符是否是标点符号
iswspace() isspace() 测试字符是否是空白符号
iswupper() isupper() 测试字符是否是大写字符
iswxdigit() isxdigit() 测试字符是否是十六进制的数字


大小写转换:
towlower() tolower() 把字符转换为小写
towupper() toupper() 把字符转换为大写


字符比较:
wcscoll() strcoll() 比较字符串


日期和时间转换:
strftime() 根据指定的字符串格式和locale设置格式化日期和时间
wcsftime() 根据指定的字符串格式和locale设置格式化日期和时间, 并返回宽字符串
strptime() 根据指定格式把字符串转换为时间值, 是strftime的反过程


打印和扫描字符串:
fprintf() /fwprintf() 使用vararg参量的格式化输出
fscanf() /fwscanf() 格式化读入
printf() 使用vararg参量的格式化输出到标准输出
scanf() 从标准输入的格式化读入
sprintf() /swprintf() 根据vararg参量表格式化成字符串
sscanf() 以字符串作格式化读入
vfprintf() /vfwprintf() 使用stdarg参量表格式化输出到文件
vprintf() 使用stdarg参量表格式化输出到标准输出
vsprintf() /vswprintf() 格式化stdarg参量表并写到字符串


数字转换:
wcstod() strtod() 把宽字符的初始部分转换为双精度浮点数
wcstol() strtol() 把宽字符的初始部分转换为长整数
wcstoul() strtoul() 把宽字符的初始部分转换为无符号长整数


多字节字符和宽字符转换及操作:
mblen() 根据locale的设置确定字符的字节数
mbstowcs() 把多字节字符串转换为宽字符串
mbtowc() /btowc() 把多字节字符转换为宽字符
wcstombs() 把宽字符串转换为多字节字符串
wctomb() /wctob() 把宽字符转换为多字节字符


输入和输出:
fgetwc() fgetc() 从流中读入一个字符并转换为宽字符
fgetws() fgets() 从流中读入一个字符串并转换为宽字符串
fputwc() fputc() 把宽字符转换为多字节字符并且输出到标准输出
fputws() fputs() 把宽字符串转换为多字节字符并且输出到标准输出串
getwc() getc() 从标准输入中读取字符, 并且转换为宽字符
getwchar() getchar() 从标准输入中读取字符, 并且转换为宽字符
None gets() 使用fgetws()
putwc() putc() 把宽字符转换成多字节字符并且写到标准输出
putwchar() putchar() 把宽字符转换成多字节字符并且写到标准输出
None puts() 使用fputws()
ungetwc() ungetc() 把一个宽字符放回到输入流中


字符串操作:
wcscat() strcat() 把一个字符串接到另一个字符串的尾部
wcsncat() strncat() 类似于wcscat() , 而且指定粘接字符串的粘接长度.
wcschr() strchr() 查找子字符串的第一个位置
wcsrchr() strrchr() 从尾部开始查找子字符串出现的第一个位置
wcspbrk() strpbrk() 从一字符字符串中查找另一字符串中任何一个字符第一次出现的位置
wcswcs() /wcsstr() strchr() 在一字符串中查找另一字符串第一次出现的位置
wcscspn() strcspn() 返回不包含第二个字符串的的初始数目
wcsspn() strspn() 返回包含第二个字符串的初始数目
wcscpy() strcpy() 拷贝字符串
wcsncpy() strncpy() 类似于wcscpy() , 同时指定拷贝的数目
wcscmp() strcmp() 比较两个宽字符串
wcsncmp() strncmp() 类似于wcscmp() , 还要指定比较字符字符串的数目
wcslen() strlen() 获得宽字符串的数目
wcstok() strtok() 根据标示符把宽字符串分解成一系列字符串
wcswidth() None 获得宽字符串的宽度
wcwidth() None 获得宽字符的宽度


另外还有对应于memory操作的 wmemcpy() , wmemchr() , wmemcmp() ,wmemmove() , wmemset()
January 31 声明: 以下内容为网络整理的结果!
它们用处不同,但大部分情况下可以完成相同的要求。
strcpy
原型:extern char *strcpy(char *dest,char *src); 用法:#include <string.h> 功能:把src所指由NULL结束的字符串复制到dest所指的数组中。 说明:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。 返回指向dest的指针。
例:char a[100],b[50];strcpy(a,b);如用strcpy(b,a),要注意a中的字符串长度(第一个‘\0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。
memcpy 原型:extern void *memcpy(void *dest, void *src, unsigned int count); 用法:#include <string.h> 功能:由src所指内存区域复制count个字节到dest所指内存区域。 说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。可以拿它拷贝任何数据类型的对象。
举例:char a[100],b[50]; memcpy(b, a, sizeof(b));注意如用sizeof(a),会造成b的内存地址溢出。
memset 原型:extern void *memset(void *buffer, int c, int count); 用法:#include <string.h> 功能:把buffer所指内存区域的前count个字节设置成字符c。 说明:返回指向buffer的指针。用来对一段内存空间全部设置为某个字符。
举例:char a[100];memset(a, '\0', sizeof(a));
memset可以方便的清空一个结构类型的变量或数组。
如: struct sample_struct { char csName[16]; int iSeq; int iType; };
对于变量 struct sample_strcut stTest;
一般情况下,清空stTest的方法: stTest.csName[0]='\0'; stTest.iSeq=0; stTest.iType=0;
用memset就非常方便: memset(&stTest,0,sizeof(struct sample_struct));
如果是数组: struct sample_struct TEST[10]; 则 memset(TEST,0,sizeof(struct sample_struct)*10);
对这个问题有疑问,不是对函数的疑问,而是因为没有弄懂mem和str的区别。 mem是一段内存,他的长度,必须你自己记住 str也是一段内存,不过它的长度,你不用记,随时都可以计算出来 所以memcpy需要第三个参数,而strcpy不需要 January 30 有些时候,我们常常会碰到一些从字符串中取数据的问题,如果取固定个数的数值还好,利用sscanf就可以实现,但倘若遇到从字符串中取数组,且数组个数不固定的情况就比较麻烦了. 比如构造一个矩阵,其值从文件set.txt中读取 1 2 3 4 5 6 7 8 9 此文件中含有3*3的矩阵,当然也可能是n*n,那如何才能灵活地从文件中读取数据,构造各种大小的数组呢? 经过查找,我发现了strsep函数, *************************************************************************************************************** 函数:char * strtok_r (char *newstring, const char *delimiters, char **save_ptr) 就象strtok函数一样,strtok_r函数能够连续调用,以将一个字符串分解成为几个token。不同的时下一个strtok_r函数不会破坏这个函数的状态。因为它提供了一个指向不同函数的**save_ptr指针。 这个函数是POSIX.1b提出的建议,它被许多系统支持,以提供多线程的字符分解。 函数:char * strsep (char **string_ptr, const char *delimiter) 另一个可重入的方法是取消了第一个参数。初始时的指针由用户指定,后面调用的指针则由delimiter决定。返回下一个被分解的token。 这个函数是在4.3BSD系统中引入的,因此被广泛使用着。 以下是一个使用strsep的实例: #include <string.h> #include <stddef.h> ... char string[] = "words separated by spaces -- and, punctuation!"; const char delimiters[] = " .,;:!-"; char *running; char *token; ... running = string; token = strsep (&running, delimiters); /* token => "words" */ token = strsep (&running, delimiters); /* token => "separated" */ token = strsep (&running, delimiters); /* token => "by" */ token = strsep (&running, delimiters); /* token => "spaces" */ token = strsep (&running, delimiters); /* token => "and" */ token = strsep (&running, delimiters); /* token => "punctuation" */ token = strsep (&running, delimiters); /* token => NULL */ **************************************************************************************************************** 这个说明不是很明确,我自己写了个程序来使用这个函数 #include <stdio.h> #include <string.h> int main(){ FILE *fp; char str[20],*tmp,*substr="a"; int t,data[5]; const char delimiters[]=" "; Matrix<int> m; List<Matrix<int> > l; fp = fopen("set.txt","r"); while(fgets(str,20,fp)!=NULL){ substr = str; int i=0; token = str; while(token!=NULL){ token = strsep(&substr," "); if(*token=='\n') break; datas[i++]=atoi(token); } m.setValue(datas); l.insert(m); } fclose(fp); return 0; } 此程序每从文件中取到一行数值,将其置入一个数组内,再将此数组置入矩阵中. 这里要注意这样几个问题: 1,每行数值后需要多加一个分隔符,否则会取到\n值,在linux下,文本是这样构成的 a b c\n 因此,若不在最后加空格,最后一个token取到的为c\n,因此取要在最后一个字符后加一个分隔符,此处为空格 2, 初始时,需要对token置任意值,,这样的话才可以开始循环. 3,数组的大小,我是这样设定的,我在文件头放置固定字符值,用以明确数组的大小.依照此值生成动态数组即可 5.字符串与数组工具 在许多程序中,字符串(或字符数组)是很重要的一部分。在GNU C库中提供了广泛字符串工具函数,包括字符串复制、串联、比较、搜索。这些函数中的大多数都能够在内存的任何地方操作;例如,函数memcpy能够用于任何一种数组中拷贝内容。 通常,一些C语言的初学者是直接将这些复制到自己的代码中,但当他们越来越熟悉的时候,在使用这些函数时,通常考虑更多的是可维护性、效率和可移植性。 举例说,你可以在两行C代码中比较两个字符串,但如果你使用内置strcmp函数,你出错的机会会更少。同时,由于这些库函数是经过了优化的,所以使用它们会使你的程序运行得更快。 5.1字符串表示法 这个小节是对一个C程序设计初学者做一个关于字符串概念的概要介绍。这里将说明在C语言中如何表示一个字符串,以及一些通常的缺陷。如果你已经很熟悉它们,可以跳过这一小节。 一个字符串是字符数组。但字符串变量通常被说明成一个字符型指针char *。这种字符串变量不包括空格符,它将被存话在内存中的某个地方,或者是一个常量,或者是一个动态分配的内存。在内存中的地址将存入这个指针变量,当然,你也可以将一个空指针存入指针变量。空指针没有指向任何东西,所以用空指针表示字符串将出错。 按习惯,在字符串后面加上一个空字符“。例如,你想测试字符型指针p是不是指向一个以空字符结束字符串,你可以使用!*p或*p=’ '。 一个空字符与一个空指针在概念上是很不同的,尽管它们都表示为整数0。 字符串在C语言源程序中,将其包含在双引号(" ")之内。在ISO C中,"a" "b"与"ab"相同。在GNU C中不允许修改字符串中的内容,因为它是被存放为只读的。 字符数组如果声明为const,则不能修改。而通常更好的方法是将其定义为const char *类型,因为这样,C编译器会检测到想修改这个字符串的企图。 通常字符数组在分配内存时,会加上一个用于存放空字符串(这个空字符串用来标识字符串结束)。在这个文档中,内存分配大小(allocation size)用来表示这个字符串所占用的内存总和,而长度(length)则用来表示不包括空字符在内的总长度。 尝试往字符串变量中存放超过其分配大小的字符是一个声名狼籍的程序错误。当你在编写程序时,请一定要提起注意。有许多库函数可以帮助你完成这个任务。另外,请牢记,你需要为表示字符串终了的空字符留下一个内存空间。 5.2字符串与数组函数的惯例 这一小节将描述所有基于任意数组或任意内存块的函数,讲述这些函数的一些通性的东西。 对任意内存块操作的函数的名字都是以mem开头的(如memcpy),并且总是带上一个参数,用来指定所操作的内存块大小。这些函数返回 void *类型的数组,数组的成员是bytes型的。你可以传送任何一种类型的指针给这些函数,而且可以使用sizeof操作符来计算它的大小。 相对应的,专门处理字符串的函数则是以str开头的(如strcpy),这些函数都是以用空字符结束的字符串代替显式的size参数。(虽然有些函数也接受指定的最大长度,但它们同样要检测结束字符串的空字符。这个函数返回char *类型的数组,这些数组的成员是char型的。 5.3字符串长度 我们可以使用strlen函数获得一个字符串的长度。这个函数在头文件string.h中声明。 函数:size_t strlen(const char *s) 这个函数将返回以空字符结束的字符串s的长度。 例如: strlen ("hello, world") (值为12,加上一个空字符) 当用于字符数组时,strlen函数将返回已存放的字符串内存分配大小,而非整个数组的内存分配大小。我们可以使用sizeof操作符来获取字符数组的内存分配大小: char string[32] = "hello, world"; sizeof (string) (其值为32) strlen (string) (其值为12,加上一个空字符) 5.4复制与联接字符串 我们可以使用这一小节中的函数复制字符串和字符数组中的内容,或者将一个字符串的内容附加到另一个字符串中去。这些函数在头文件string.h中声明: 有一个规律可以帮助你记住这一小节中所有函数的参数位置:目标数组都在源数组的左边!所有的函数都返回目标数组的地址。 如果源数组与目标数组或在物理地址上有重迭的话,大多数函数都无法正常地工作。例如,如果目标数组的头与源数组的尾在物理地址上重迭,那么源数组中的一部分内容就会在复制之前被覆盖。甚至可能出现使得标识字符串结束的空字符丢失,而使拷贝无休止地无意义地进行着。 所有会因为这个问题而出错的函数,都会在本手册中显式标识出来。除了这个小节里的函数外,还有sprintf函数(参见格式化输出函数章节)以及scanf函数(参见格式化输入函数章节)。 函数:void * memcpy (void *to, const void *from, size_t size) 这个函数会从数组from的开头处,复制size个字符,并从头写入数组to中。这个函数的参数from和to如果是有重迭的两个数组,那么就会出错。这种情况,请使用memmove函数来代替它。 Memcpy函数返回to的地址; 以下是一个使用memcpy复制一个数组中内容的例子: struct foo *oldarray, *newarray; int arraysize; ... memcpy (new, old, arraysize * sizeof (struct foo)); 函数:void * memmove (void *to, const void *from, size_t size) 从from中拷贝size字节到to中,即使这两个内存块有重迭也不影响执行效果。 函数:void * memccpy (void *to, const void *from, int c, size_t size) 这个函数将从form中拷贝不超过size字节到to中去,在拷贝中如果有一个字节与c匹配,就中止拷贝。该函数将返回一个指针。如果在拷贝时发现了c,这个指针指向to,如果没有,就是一个空指针。 函数:void * memset (void *block, int c, size_t size) 该函数将复制size个c(转换成为char型)到block中,返回block的值。 函数:char * strcpy (char *to, const char *from) 复制字符串from到字符串to中去。就象memcpy一样,这个函数也无法处理两个字符串有重迭部分的情况。该返回to的值。 函数:char * strncpy (char *to, const char *from, size_t size) 这个函数与strcpy类似,区别在于strncpy将精确地拷贝size字节到to中去。 如果from的长度大于size,那么strncpy仅拷贝from中的前size个字符。要注意的是,这种情况下,将不会往to中写入表示结束的空字符。 如果from的长度小于size,那么strncpy将拷贝from中所有内容,然后加上一些空字符,使其够size字节。这是十分有用的,但仅在ISO C中可用。 Strncpy也无法正确处理from和to有重迭的情况。 使用strncpy代替strcpy可以避免尝试对to所分配空间之后的操作。但是,如果你要把一个较小的字符串,写到一个大的缓冲中,会使你的程序运行得更慢,因为这时由于size很大,程序花了许多时间做无谓的工作:填写大量的空字符。 函数:char * strdup (const char *s) 拷贝一个以空字符结束的字符串到一个新分配的字符串中。这个新字符串将自动调用malloc函数来分配。如果malloc函数无法为这个新字符串分配空间,strdup将返回一个空指针。如果能够为其分配空间,将返回这个新字符串的地址指针。 函数: char * strndup (const char *s, size_t size) 它与strdup类似,区别在于strndup将复制不多于size字节的字符到新字符串中。 如果s的长度大于size,那么strndup将复制size个字符,再添上一个空字符串。否则,就会完整地被复制。 这个函数与strncpy不同的是,它将保证拷贝出来的字符串一定有表示结束的空字符。 函数:char * stpcpy (char *to, const char *from) 与strcpy类似,区别在于,stpcpy将返回一个指向字符串to结尾的指针(也就是,指向那个表示结束的空字符),而不指向字符串的开始。 例如,下面这个程序使用stpcpy将’foo’和’bar’连接成为’foobar’,然后再打印出来: #include <string.h> #include <stdio.h> int main (void) { char buffer[10]; char *to = buffer; to = stpcpy (to, "foo"); to = stpcpy (to, "bar"); puts (buffer); return 0; } 这个函数不属于ISO和POSIX标准,所以并不经常在UNIX系统上使用,但别自己去创建这个函数,因为可能在MS-DOS中就有。 这个函数也无法正确处理两个字符串有重迭部分的情况。 函数:char * stpncpy (char *to, const char *from, size_t size) 它与stpcpy类似,区别在于,stpncpy将精确地只拷贝size个字符。 如果字符串from的长度比size大,那么stpncpy将只复制前size个字符,然后返回一个指向最后拷贝的那个字符的地址。要注意的是,在这种情况下,最后并没有一个表示字符串结束的空字符。 如果from的长度小于size,那么,stpncpy将拷贝from中的所有字符串,然后在后面加上(size-form长度)个空字符。它将返回一个指向第一个空字符的指针。 这个函数并不属于ISO和POSIX标准,但却在GNU C库的开发经常用到。 它也无法正确地处理源、目标两个字符串有重迭部分的情况。 函数:char * strdupa (const char *s) 它与strdup函数十分类似,区别在于,它不使用malloc来分配一个新字符串,而是使用alloc来分配。 很明显,strdupa仅适合于宏,也就是你无法获得这个函数的地址。虽然有这个限制,但还是有它有用的一面。下面这个程序说明的有时使用malloc将付出更大的代价。 #include <paths.h> #include <string.h> #include <stdio.h> const char path[] = _PATH_STDPATH; int main (void) { char *wr_path = strdupa (path); char *cp = strtok (wr_path, ":"); while (cp != NULL) { puts (cp); cp = strtok (NULL, ":"); } return 0; } 请注意直接使用目录调用strtok是非法的。 这个函数仅在GNU C中能够使用。 函数:char * strndupa (const char *s, size_t size) 完成功能与strndup类似,不过它像strdupa一样使用alloca来为新字符串分配内存空间。它的优缺点与strdupa一样。 这个函数也只能用于宏,因为无法得到这个函数的地址。 它仅在GNU C中能够使用。 函数:char * strcat (char *to, const char *from) 与strcpy类似,区别在于,它是将字符串from中的内容增加到字符串to的后面。而不是覆盖字符串to。字符串from中的第一个字符将覆盖掉to中原来最后的空字符。 以下是一个与strcat等价的定义: char * strcat (char *to, const char *from) { strcpy (to + strlen (to), from); return to; } 它无法正确处理from与to两个字符串有重迭部分的情况。 函数:char * strncat (char *to, const char *from, size_t size) 它与strcat类似,区别在于附加到字符串to后面的字符将不超过size个。在to的最后将保证有一个空字符,也就是说添加后,总的分配空间将新增size+1。 这个函数可以使用下面这个程序实现: char * strncat (char *to, const char *from, size_t size) { strncpy (to + strlen (to), from, size); return to; } 同样的,它也无法正确处理from与to有重迭部分的情况。 以下是一个使用strncpy和strncat函数的实例。注意它们使用的方法,如何避免size过大。 #include <string.h> #include <stdio.h> #define SIZE 10 static char buffer[SIZE]; main () { strncpy (buffer, "hello", SIZE); puts (buffer); strncat (buffer, ", world", SIZE - strlen (buffer) - 1); puts (buffer); } 这个程序的输出为: hello hello, wo 函数:void * bcopy (void *from, const void *to, size_t size) 这个函数已经被memmove取代,它起源于BSD系统。注意,它并不等同于memmove函数,参数的顺序不同。 函数:void * bzero (void *block, size_t size) 这个函数已经被memset取代,它起源于BSD系统。注意,它并不如memset那么通用,因为bzero只能存入0值。 5.5字符串与数组的比较 你可以使用这一小节里的函数进行字符串或字符数组内容比较。它们不仅可以用来检查字符串是否相同,还可以在字符串排序时起作用。 与C语言的其它函数不大相同,字符串比较函数返回非零值,是表示它们不相等,而非相等。返回值是负数代表第一个字符串小于第二个字符串,如果返回的是正数则代表第一个字符串大于第二个字符串。 这些函数通常仅用于检查两个字符串是否相等。如:`! strcmp (s1, s2)'. 所有的这些函数都在头文件string.h中声明。 函数:int memcmp (const void *a1, const void *a2, size_t size) 函数memcmp用于比较字符串s1与s2的前size个字符。 如果两上字符块相同,memcmp将返回0。 对于任意的字符数组,memcmp函数通常用于测试它们是否相等。而不常进行byte-wise的字符串比较。如果是byte-wise比较,将返回一个表示两个字符串之间的关系的浮点数。 如果要比较的对象中包含一些由于边界对齐需求而填入结构对象中的空格、联合(union)结束的额外空格、字符串所分配的空间未使用完的部分引起的“holes”的话,最好使用memcmp来完成。这些“holes”的内容是不确定的,在执行byte-wise比较时结果也是不明确的。 例如,给出一个如下的结构定义: struct foo { unsigned char tag; union { double f; long i; char *p; } value; }; 如果你要比较两个struct foo对象的话,建议最好使用memcmp。 函数:int strcmp (const char *s1, const char *s2) 这个函数用来比较s1和s2字符串,这个函数将返回一个值,它的符号与第一对不同的字符的比较结果相关。 如果两个字符串相等的话,strcmp将返回0。 如果s1是s2的一个子串的话,s1小于s2。 函数:int strcasecmp (const char *s1, const char *s2) 与strcmp类似,不同之处在于,strcasecmp不区分大小写。 Strcasecmp源于BSD系统。 函数:int strncasecmp (const char *s1, const char *s2, size_t n) 与strncmp类似,不同之处在于,strncasecmp不区分大小写。 Strncasecmp是GNU扩展函数。 函数:int strncmp (const char *s1, const char *s2, size_t size) 此函数与strcmp极为类似。不同之处是,strncmp函数是指定比较size个字符。也就是说,如果字符串s1与s2的前size个字符相同,函数返回值为0。 这里是两个使用strcmp和strncmp的实例。在本例中,假定使用ASCII字符集(如果你是使用其它字符集--如EBCDIC--的话,返回的值可能不同,因为在不同的字符集中顺序不同。) strcmp ("hello", "hello") => 0 /* 这两个字符串相同。 */ strcmp ("hello", "Hello") => 32 /* 大小写敏感,h-H=32 */ strcmp ("hello", "world") => -15 /* h-w=-15 */ strcmp ("hello", "hello, world") => -44 /* 空字符与逗号不同 */ strncmp ("hello", "hello, world", 5) => 0 /* 前5个值是相同的,所以返回0 */ strncmp ("hello, world", "hello, stupid world!!!", 5) => 0 /* 前5个值是相同的,所以返回0 */ 函数:int bcmp (const void *a1, const void *a2, size_t size) 这是一个过时的memcmp函数的别名,起源于BSD。 5.6 Collation函数 在相同的地区,词典序与严格的字符序不尽相同。例如,在西班牙文字中常不考虑整理的需要,使用一些标识来表示重音。另一方面,字符序列“ll”将被认为是一个单独的字符“l”。 我们可以使用strcoll和strxfrm函数(在头文件string.h中声明)来实现按当地字符序来整理并比较字符串。使用的不同地区的字符集,可在LC_COLLATE类中设置,具体参见地区和国际化那一章节。 在标准C方言中,strcoll与strcmp相同。 使用这些函数进行比较时,它们需先将字符串映射成为本地字符序,然后再进行比较。 函数strcoll为了完成这样的比较,会自动地暗中完成这个映射工作。与此相反,strxfrm函数则是明确地完成映射。如果你要使用一个相同的字符串进行多次比较,那么更有效的方法可能是使用strxfrm函数将它们一次转换完成,尔后再使用strcmp进行比较。 函数:int strcoll (const char *s1, const char *s2) 此函数与strcmp类似,区别在于,它使用本地字符充(LC_COLLATE设置的)。 以下是一个字符串排序的例子,在此例中使用了strcoll进行字符串比较。实际的排序算法未在此处写出,它出自qsort(参见数组排序函数小节)。以下的代码仅说明比较的工作。(在本节的后面部分,还可以看到使用strxfrm实现)。 /* This is the comparison function used with qsort. */ int compare_elements (char **p1, char **p2) { return strcoll (*p1, *p2); } /* This is the entry point--the function to sort strings using the locale's collating sequence. */ void sort_strings (char **array, int nstrings) { /* Sort temp_array by comparing the strings. */ qsort (array, sizeof (char *), nstrings, compare_elements); } 函数:size_t strxfrm (char *to, const char *from, size_t size) 函数strxfrm根据本地字符集进行转换、整理,然后存入数组中。它只转换前size个(包括结束的空字符),并存盘。 要注意的是,当from和to有重迭部分将导致结果不明确。参见复制与连接小节。 这个函数的返回值是转换后的字符长度。这个值不受size的影响,但如果返回值大于或等于size的话,表示并未转换完数组中所有的内容。在这种情况下,你仅能得到部分转换的值,如果要整个字串,则带上一个更大的size,重新调用一遍strxfrm。 转换完后的字符串可能比原来的字符串长,也可能更短。 如果size是0的话,将没有字符串被转换。在这种情况下,strxfrm简单地返回字符串的字符个数。这对于决定需要多少的存储空间十分有用。 这儿是一个使用strxfrm进行多次字符串比较的例子。它完成与上例中一样的功能,但运行得更快,因为strxfrm将转换工作一次完成,无须每次比较时进行转换。 struct sorter { char *input; char *transformed; }; /* This is the comparison function used with qsort to sort an array of struct sorter. */ int compare_elements (struct sorter *p1, struct sorter *p2) { return strcmp (p1->transformed, p2->transformed); } /* This is the entry point--the function to sort strings using the locale's collating sequence. */ void sort_strings_fast (char **array, int nstrings) { struct sorter temp_array[nstrings]; int i; /* Set up temp_array. Each element contains one input string and its transformed string. */ for (i = 0; i < nstrings; i++) { size_t length = strlen (array[i]) * 2; char *transformed; size_t transformed_lenght; temp_array[i].input = array[i]; /* First try a buffer perhaps big enough. */ transformed = (char *) xmalloc (length); /* Transform array[i]. */ transformed_length = strxfrm (transformed, array[i], length); /* If the buffer was not large enough, resize it and try again. */ if (transformed_length >= length) { /* Allocate the needed space. +1 for terminating NUL character. */ transformed = (char *) xrealloc (transformed, transformed_length + 1); /* The return value is not interesting because we know how long the transformed string is. */ (void) strxfrm (transformed, array[i], transformed_length + 1); } temp_array[i].transformed = transformed; } /* Sort temp_array by comparing transformed strings. */ qsort (temp_array, sizeof (struct sorter),nstrings, compare_elements); /* Put the elements back in the permanent array in their sorted order. */ for (i = 0; i < nstrings; i++) array[i] = temp_array[i].input; /* Free the strings we allocated. */ for (i = 0; i < nstrings; i++) free (temp_array[i].transformed); } 兼容性提示:字符串collation函数是ISO C的新功能。老的C版本无此功能。 5.7查找函数 这一小节讲述一些字符串、字符数组内进行各种各样的搜索操作的函数,这些函数在头文件string.h中声明。 函数:void * memchr (const void *block, int c, size_t size) 此函数在字符串block的前size的字符中查找字符c(参数的数据类型是int,运行是将转换成为cahr类型)。如果找到,该函数将返回指向这个值的指针;如果无匹配的结果,将返回空指针。 函数:char * strchr (const char *string, int c) 此函数在字符串string(以空字符结束的字符串)中查找字符c参数的数据类型是int,运行是将转换成为cahr类型)。如果找到,该函数将返回指向这个值的指针;如果无匹配的结果,将返回空指针。 例如: strchr ("hello, world", 'l') => "llo, world" strchr ("hello, world", '?') => NULL 表示结束的空字符被认为是字符串的一部分,所以你可以使用这个函数获得指向字符串结尾处的指定(也就是用NULL作参数)。 函数:char * index (const char *string, int c) index是strchr的另一个名字,它们完全一样。 函数:char * strrchr (const char *string, int c) 本文的链接为:http://oldsite.linuxaid.com.cn/training/showtri.jsp?i=90 作者为: fjxufeng(风过留枫) 此函数与strchr类似,区别在于,strchr是从头往后找,而strrchr则是从最后面往前找。例如: strrchr ("hello, world", 'l') => "ld" 函数:char * rindex (const char *string, int c) rindex是strrchr的另一个名字,它们完全一样。 函数:char * strstr (const char *haystack, const char *needle) 此函数与strchr类似,不同之处是strstr是在字符串haystack中查找子串needle,而不是一个字符。它将返回这个子串所在位置,如果没有找到,将返回空指针。如果needle是一个空字符串,函数将返回haystack。 例如: strstr ("hello, world", "l") => "llo, world" strstr ("hello, world", "wo") => "world" 函数:void * memmem (const void *haystack, size_t haystack-len,const void *needle, size_t needle-len) 此函数与strstr类似,不同之处是字符串needle和haystack都是字符数组,而不是以空字符结束的字符串。Needle-len是字符串needle的长度,haystack-len则是haystack的长度。这个函数是GNU C扩展的。 函数:size_t strspn (const char *string, const char *skipset) 函数strcspn(字符串例外范围)用于统计字符串string中包括skipset的最大子串长度,在skipset中的字符顺序无关紧要。例如: strspn ("hello, world", "abcdefghijklmnopqrstuvwxyz") => 5 函数:size_t strcspn (const char *string, const char *stopset) 函数strcspn(字符串例外范围)用于统计字符串string中不包括stopset的最大子串长度,例如: strcspn ("hello, world", " ,.;!?") => 5 函数:char * strpbrk (const char *string, const char *stopset) 函数strpbrk与strcspn关,它将返回一个指针,指向不包括stopset的子串后第1个字符,如果在字符串string中没找到stopset中的字符,将返回空指针。例如: strpbrk ("hello, world", " ,.;!?") => ", world" 5.8在字符串中查找 有时在你的程序中会需要进行一些词法分析、句法分析,例如分解一个命令行。你可以使用strtok函数,这个函数在头文件string.h中声明。 函数:char * strtok (char *newstring, const char *delimiters) 可以通过调用strtok函数将一个字符串分成几个部分。 第一次调用strstok函数时,应该是字符串newstring做为参数。Strtok函数会通过这个调用设置一些状态信息。后面的 strtok函数调用,将以null为参数(newstring),继续进行分解。当遇到一个新的不是null为参数的strstok函数调用,将重新初始化状态信息。库函数保证不会中途影响。 参数delimiters是用来指定分割不同部分的字符。所有这些字符都将被丢弃。第一个不是delimiters的成员的字符是下一个token的开始,当另一个成员字符被发现,则表示这个token结束。函数返回这个token。 在下一次调用strtok时,将继续上一次的工作,找下一个token。要注意的是,每个分隔符不一定要是完全相同的,只要属于delimiters就行。 当字符串newstring被分隔完,或找不到属于delimiter的字符中,strtok将返回一个空指针。 警告:由于strtok会在分解时修改字符串的值,所以你应该在使用strtok分割时,先将其复制到临时的缓冲区去。如果你让strtok修改了一个程序其它部分的字符串函数,你将会遇到麻烦;因为它们可能还有其他作用。 如果你操作的字符串是一个常量,而strtok又会试图去修改它,这时程序会导致一个致命错误。 Strtok函数是不可重入的。 以下是一个使用strtok的实例: #include <string.h> #include <stddef.h> ... char string[] = "words separated by spaces -- and, punctuation!"; const char delimiters[] = " .,;:!-"; char *token; ... token = strtok (string, delimiters); /* token => "words" */ token = strtok (NULL, delimiters); /* token => "separated" */ token = strtok (NULL, delimiters); /* token => "by" */ token = strtok (NULL, delimiters); /* token => "spaces" */ token = strtok (NULL, delimiters); /* token => "and" */ token = strtok (NULL, delimiters); /* token => "punctuation" */ token = strtok (NULL, delimiters); /* token => NULL */ GNU C库包括两个可以重入的strtok函数。 函数:char * strtok_r (char *newstring, const char *delimiters, char **save_ptr) 就象strtok函数一样,strtok_r函数能够连续调用,以将一个字符串分解成为几个token。不同的时下一个strtok_r函数不会破坏这个函数的状态。因为它提供了一个指向不同函数的**save_ptr指针。 这个函数是POSIX.1b提出的建议,它被许多系统支持,以提供多线程的字符分解。 函数:char * strsep (char **string_ptr, const char *delimiter) 另一个可重入的方法是取消了第一个参数。初始时的指针由用户指定,后面调用的指针则由delimiter决定。返回下一个被分解的token。 这个函数是在4.3BSD系统中引入的,因此被广泛使用着。 以下是一个使用strsep的实例: #include <string.h> #include <stddef.h> ... char string[] = "words separated by spaces -- and, punctuation!"; const char delimiters[] = " .,;:!-"; char *running; char *token; ... running = string; token = strsep (&running, delimiters); /* token => "words" */ token = strsep (&running, delimiters); /* token => "separated" */ token = strsep (&running, delimiters); /* token => "by" */ token = strsep (&running, delimiters); /* token => "spaces" */ token = strsep (&running, delimiters); /* token => "and" */ token = strsep (&running, delimiters); /* token => "punctuation" */ token = strsep (&running, delimiters); /* token => NULL */ 在很多数据输入的时候,我们需要从文件中输入多个数值,如在给矩阵赋值时,我们需要从文件中读取 1 3 2 4 32 2 10 5 这样一些输入。 那如何能够从文件中快速读取这样一些数值呢? 即我们如何可以从一行字符串中分析出这若干个数值,并进而取值。 我们利用的函数为了strsep函数,这是一个在GNU C中在string.h定义的函数,具体作用为是将一个字符串分成几个部分,其具体实例可见我的Blog中转载的"字符串与数组工具"的文章。 在这里我们只是讲这个函数使用时,值得注意的一些地方。 在使用strsep时,函数会不断地对输入函数按分隔符进行分割,直至返回值为Null为止。 因此,若我们的文件如下所示: 0 1 2 1 2 3 1 2 1 则程序在读取文件中的某一行时,如0 1 2,它首先将0 1 2取出,然后会取到一个NULL值,若不加控制,此NULL值也会输出。此外,由于在Unix的文本文件中,换行时会在每行后加入一个回车符\n,因此我们看到的文件实际在文件中应该是这样表示的: 0 1 2\n 1 2 3\n 1 2 1\n 而由于我们的分隔符为空格,因此可见在取第一行时,我们最后取到的2实际为2\n,因此为了解决这一问题,我们在写输入文件时,要人为地在每行后加入一个空格,即 0 1 2 \n 1 2 3 \n 1 2 1 \n 这样的话就可以避免这一问题的出现。 因此我的程序段如下: #include <stdio.h> #include <string.h> int main(){ FILE *fp; char str[20],*tmp,*substr="a"; int t,data[5]; const char delimiters[]=" "; fp = fopen("set.txt","r"); //输入文件为set.txt while(fgets(str,20,fp)!=NULL){ tmp = str; while(substr!=NULL){ substr = strsep(&tmp,delimiters); if(*substr=='\n') break; printf("%s ",substr); } printf("\n"); } fclose(fp); return 0; } 其输出结果为: 0 1 2 1 2 3 1 2 1 C 语言从 C94 引入多语言支持以后处理中文方便多了。即使不用 wchar_t 也可以获得很多好处,比如增加了 strcoll 这个根据 locale 比较字符串的函数。 简单地说,一个 locale 就是一组处理跟语言相关问题的规则,这里有一篇简介。这些规则就包括如何对字符串进行比较和排序。按照 C94 及 C99 标准的规定,程序在启动时设置 locale 为 "C"。在 "C" locale 下,字符串的比较就是按照内码一个字节一个字节地进行,这时 strcoll 与 strcmp 函数没有区别。在其他 locale 下,字符串的比较方式则不同了,例如在简体中文 locale 下,strcmp 仍然按内码比较,而 strcoll 对于汉字则是按拼音进行的(这也跟操作系统有关,Windows 还支持按笔划排序,可以在“区域和语言设置”里面修改)。 在 C 语言里面,用 setlocale 函数设置当前 locale,在程序开头使用一句 setlocale (LC_ALL, ""); 就可以了。第二个参数为空串表示依照操作系统的当前设置。下面是一个示例程序(须使用 C 编译器;如果要用 C++ 编译器,还要自己封装一下 strcoll 或者加一个强制类型转换): #include <stdio.h> #include <string.h> #include <stdlib.h> #include <locale.h> int main (void) { int i; char str[10][4]= {"镕","堃","趙","錢","孫","李","周","吳","鄭","王"}; qsort (str, 10, 4, strcoll); printf ("按内码排序:"); for (i=0; i<10; i++) printf ("%s", str[i]); setlocale (LC_ALL, ""); qsort (str, 10, 4, strcoll); printf ("\n按音序排序:"); for (i=0; i<10; i++) printf ("%s", str[i]); return 0; } 程序输出(操作系统 GNU/Linux,locale 为 "zh_CN.UTF-8"): 按内码排序:吳周堃孫李王趙鄭錢镕 按音序排序:堃李錢镕孫王吳趙鄭周 程序输出(操作系统 Windows, locale 为 "Chinese_People's Republic of China.936"): 按内码排序:吳堃孫李王周趙鄭錢镕 按音序排序:堃李錢镕孫王吳趙鄭周 按内码排序的差别是因为两个测试里使用了不同的内码,但可以看出第二行的结果相同。之所以用了生僻字和繁体字为例,是因为一级汉字在 GB2312 里面恰好是按拼音排的,体现不出差别了。 另外,使用宽字符(wchar_t)的程序在开头一定要使用 setlocale,不然会遇到很多麻烦,比如 wprintf 不能输出中文…… 文章出处:http://www.diybl.com/course/3_program/c++/cppsl/20081117/151299.html 一、预备知识—程序的内存分配 由C/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack): 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。 3、全局区(static): 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后有系统释放 。 4、文字常量区: 常量字符串就是放在这里的, 程序结束后由系统释放。 5、程序代码区: 存放函数体的二进制代码。 Example: int a = 0; // 全局初始化区 char *p1; // 全局未初始化区 main() { int b; // 栈 char s[] = "abc"; // 栈 char *p2; // 栈 char *p3 = "123456"; // 123456\0在常量区,p3在栈上。 static int c =0; // 全局(静态)初始化区 p1 = (char *)malloc(10); p2 = (char *)malloc(20); // 分配得来得10和20字节的区域就在堆区。 strcpy(p1, "123456"); // 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 } 二、堆和栈的理论知识 2.1 申请方式 栈: 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 堆: 需要程序员自己申请,并指明大小,在c中malloc函数:如p1 = (char *)malloc(10); 在C++中用new运算符 如p2 = (char *)malloc(10); 但是注意p1、p2本身是在栈中的。 2.2 申请后系统的响应 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块 内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的 大小,系统会自动的将多余的那部分重新放入空闲链表中。 2.3 申请大小的限制 栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因 此,能从栈获得的空间较小。 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 2.4 申请效率的比较: 栈:由系统自动分配,速度较快。但程序员是无法控制的。 堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。 另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。 2.5 堆和栈中的存储内容 栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由 右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址, 也就是主函数中的下一条指令,程序由该点继续运行。 堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。 2.6 存取效率的比较 char s1[] = "aaaaaaaaaaaaaaa"; char *s2 = "bbbbbbbbbbbbbbbbb"; aaaaaaaaaaa是在运行时刻赋值的; 而bbbbbbbbbbb是在编译时就确定的; 但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 比如: #include void main() { char a = 1; char c[] = "1234567890"; char *p ="1234567890"; a = c[1]; a = p[1]; return; } 对应的汇编代码 10: a = c[1]; 00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 0040106A 88 4D FC mov byte ptr [ebp-4],cl 11: a = p[1]; 0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 00401070 8A 42 01 mov al,byte ptr [edx+1] 00401073 88 45 FC mov byte ptr [ebp-4],al 第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。 2.7 小结: 堆和栈的区别可以用如下的比喻来看出: 使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是 快捷,但是自由度小。 使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。 还有就是函数调用时会在栈上有一系列的保留现场及传递参数的操作。栈的空间大小有限定,VC的缺省是2M。栈不够用的情况一般是程序中分配了大量 数组和递归函数层次太深。有一点必须知道,当一个函数调用完返回后它会释放该函数中所有的栈空间。栈是由编译器自动管理的,不用你操心。堆是动态分配内存 的,并且你可以分配使用很大的内存。但是用不好会产生内存泄漏。并且频繁地malloc和free会产生内存碎片(有点类似磁盘碎片),因为C分配动态内 存时是寻找匹配的内存的。而用栈则不会产生碎片。在栈上存取数据比通过指针在堆上存取数据快些。一般大家说的堆栈和栈是一样的,就是栈(stack),而 说堆时才是堆heap。栈是先入后出的,一般是由高地址向低地址生长。 文章出处:http://www.diybl.com/course/3_program/c/c_js/2007109/77275.html C语言不像Java,Php之类的高级语言,对象中直接封装了字符串的处理函数。C语言中进行普通的字符串处理也经常会让我们焦头烂额……不过好在C语言
中还是提供了像strtok这样功能强大的字符串处理函数,可以帮我们实现部分需要的功能。下面我们介绍一下strtok函数的用法以及具体的使用例。 原型:char *strtok(char *s, char *delim);
功能:分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。实质上的处理是,strtok在s中查找包含在delim中的字符并用NULL(’\0′)来替换,直到找遍整个字符串。
说明:首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。strtok在s中查找包含在delim中的字符并用NULL(’\0′)来替换,直到找遍整个字符串。
返回值:从s开头开始的一个个被分割的串。当没有被分割的串时则返回NULL。所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。
具体使用例子:
- #include <stdio.h>
- #include <string.h>
- int main(int argc,char **argv)
- {
- char * buf1="aaa, ,a, ,,,bbb-c,,,ee|abc";
- /* Establish string and get the first token: */
- char* token = strtok( buf1, ",-|");
- while( token != NULL )
- {
- /* While there are tokens in "string" */
- printf( "%s ", token );
- /* Get next token: */
- token = strtok( NULL, ",-|");
- }
- return 0;
- }
在JAVA字符串处理函数列表一览中介绍了比较多的字符串处理函数,不过有一种我们经常会用到的处理没有被加入–字符串的分割。下面是分别用split函数和StringTokenizer类来实现字符串分割的简单代码: ●利用split函数:
- String s = new String("2_8_7_4_3_9_1");
- String[] arr = s.split("_");
●利用StringTokenizer类:
- String s = new String("2_8_7_4_3_9_1");
- StringTokenizer commaToker = new StringTokenizer(s, "_");
- String[] arr = new String[commaToker.countTokens()];
Java中的字符串也是一连串的字符。但是与许多其他的计算机语言将字符串作为字符数组处理不同,Java将字符串作为String类型对象来处理。将字
符串作为内置的对象处理允许Java提供十分丰富的功能特性以方便处理字符串。下面是一些使用频率比较高的函数及其相关说明。 substring() 它有两种形式,第一种是:String substring(int startIndex) 第二种是:String substring(int startIndex,int endIndex)
concat() 连接两个字符串
replace() 替换 它有两种形式,第一种形式用一个字符在调用字符串中所有出现某个字符的地方进行替换,形式如下: String replace(char original,char replacement) 例如:String s=”Hello”.replace(’l',’w'); 第二种形式是用一个字符序列替换另一个字符序列,形式如下: String replace(CharSequence original,CharSequence replacement)
trim() 去掉起始和结尾的空格
valueOf() 转换为字符串
toLowerCase() 转换为小写
toUpperCase() 转换为大写
length() 取得字符串的长度 例: char chars[]={’a',’b’.’c'}; String s=new String(chars); int len=s.length();
charAt() 截取一个字符 例: char ch; ch=”abc”.charAt(1); 返回值为’b’
getChars() 截取多个字符 void getChars(int sourceStart,int sourceEnd,char target[],int targetStart) sourceStart 指定了子串开始字符的下标 sourceEnd 指定了子串结束后的下一个字符的下标。因此,子串包含从sourceStart到sourceEnd-1的字符。 target 指定接收字符的数组 targetStart target中开始复制子串的下标值 例: String s=”this is a demo of the getChars method.”; char buf[]=new char[20]; s.getChars(10,14,buf,0);
getBytes() 替代getChars()的一种方法是将字符存储在字节数组中,该方法即getBytes() 例: String s = “Hello!你好!”; byte[] bytes = s.getBytes();
toCharArray() 例: String s = “Hello!你好!”; char[] ss = s.toCharArray();
equals()和equalsIgnoreCase() 比较两个字符串
regionMatches() 用于比较一个字符串中特定区域与另一特定区域,它有一个重载的形式允许在比较中忽略大小写。 boolean regionMatches(int startIndex,String str2,int str2StartIndex,int numChars) boolean regionMatches(boolean ignoreCase,int startIndex,String str2,int str2StartIndex,int numChars)
startsWith()和endsWith() startsWith()方法决定是否以特定字符串开始,endWith()方法决定是否以特定字符串结束
equals()和== equals()方法比较字符串对象中的字符,==运算符比较两个对象是否引用同一实例。 例:String s1=”Hello”; String s2=new String(s1); s1.eauals(s2); //true s1==s2;//false
compareTo()和compareToIgnoreCase() 比较字符串
indexOf()和lastIndexOf() indexOf() 查找字符或者子串第一次出现的地方。 lastIndexOf() 查找字符或者子串是后一次出现的地方。
StringBuffer构造函数 StringBuffer定义了三个构造函数: StringBuffer() StringBuffer(int size) StringBuffer(String str) StringBuffer(CharSequence chars)
下面是StringBuffer相关的函数:
length()和capacity() 一个StringBuffer当前长度可通过length()方法得到,而整个可分配空间通过capacity()方法得到。
ensureCapacity() 设置缓冲区的大小 void ensureCapacity(int capacity)
setLength() 设置缓冲区的长度 void setLength(int len)
charAt()和setCharAt() char charAt(int where) void setCharAt(int where,char ch)
getChars() void getChars(int sourceStart,int sourceEnd,char target[],int targetStart)
append() 可把任何类型数据的字符串表示连接到调用的StringBuffer对象的末尾。 例:int a=42; StringBuffer sb=new StringBuffer(40); String s=sb.append(”a=”).append(a).append(”!”).toString();
insert() 插入字符串 StringBuffer insert(int index,String str) StringBuffer insert(int index,char ch) StringBuffer insert(int index,Object obj) index指定将字符串插入到StringBuffer对象中的位置的下标。
reverse() 颠倒StringBuffer对象中的字符 StringBuffer reverse()
delete()和deleteCharAt() 删除字符 StringBuffer delete(int startIndex,int endIndex) StringBuffer deleteCharAt(int loc)
replace() 替换 StringBuffer replace(int startIndex,int endIndex,String str)
substring() 截取子串 String substring(int startIndex) String substring(int startIndex,int endIndex)
January 09 28年前,一封名为《人生的路为什么越走越窄》的读者来信见报后,曾在全国青年人中引发了一场关于理想和现实的大讨论。 28年后,一个24岁的来自底层的打工青年再次被逼至墙角——当理想被沉重的现实不断围剿,直到成为他生命中不能承受之重。他最后选择了在人间蒸发,成为他亲人的长久牵挂。 这个喜欢庄子的80后叫罗炼,他的故事受到了湖南卫视《零点锋云》栏目的关注,节目播出后迅速成为社会热议的一个话题。当栏目制作方邀我为这个年轻人写点什么的时候,我毫不犹豫地答应了——只为一个今天太多人认为很装很虚无的东西——理想。 在这个充斥着娱乐致死加功利主义盛行的时代,罗炼的故事显得遗世独立,对太多人进行了一次真切的逼问:现实充满诱惑更充满严酷的挑战,你还会仰望理想的星空吗?您将凭借什么坚持到底? 有志青年罗炼怀揣着纸一般脆弱的文学梦,怀揣着家人殷殷的期待离开乡村,来到南下广东打工,先后辗转于珠江三角洲,做过保安、油漆工,跑过太阳能和房地产生意,开始了理想与现实的残酷PK。罗炼面对的是一个日益繁荣的商业世界,每天都在激发他的进取心和创业成功的渴望。整整六年,他是那样努力,但成功依旧那么遥远。终于,由于生存压力如影随形的逼迫,理想还未展翅翱翔,便已支离破碎,宛如断线的纸鸢。 逐步走向时代舞台中心的80后,是一群幸运儿。因为他们有着更完美的物质环境、更开阔的眼界、更充盈的知识、更独立的个性。但他们又是不幸的。这些家庭的宠儿(绝大多数是独生子女)往往都从小缺乏苦难和挫折的锤炼,一上路便极易受现实急功近利思潮熏陶,往往追求速成,忽略对现实的适应能力的培养、自身能力的提升和社会经验的累积,一旦遭遇社会充分竞争带来的巨大压力以及阶层分化带来的各种机会不公,便要么迅速麻木,甚至蜕变成及时行乐的猪;要么悲观厌世,选择逃避,如同罗炼。 从这个意义上,整个80后乃至更多理想主义者在和罗炼一起流浪。他们往往胸怀大志,却总是慨叹报国无门。我首先还是为他们的坚持致敬,因为按照人正常发展的规律,你如果放弃了理想,那你下一步一定会尝试放弃良心。 年轻的罗炼们其实无处逃遁,因为世间已无“不知有汉,无论魏晋”的桃花源。 我想起一个这样的故事。伊斯兰教创始人默罕莫德带领大军进行圣战的途中,遇到一座险峻高山,士兵望而生畏,默罕莫德说:我念一个咒语,真主就会让山自动移开。谁料念罢山岿然不动,于是,默罕莫德说:既然山不走向我们,就让我们走向山吧。 要想坚持并实现理想,首先要学会承认现实。因为这是你无法改变和逃避的。要学会“头顶理想的天空,脚踩现实的大地。”这个世界从来都不完美,也充满不公,但改变这一切不正是我们存在的理由吗? 其次,要明白任何一个理想的实现都不是由于一个伟大的想法,而是因为一个伟大的方法。这需要经验、阅历甚至学识的积累和百折不回的探索——“有何胜利而言,挺住意味着一切!” 最后,也是最重要的是内心的修炼,比行动上不抛弃、不放弃更关键的是不抱怨。塞林格在《麦天里的守望者》写下这么一句充满智慧的话:一个男人不成熟的标志在于他甘愿为某个事业英勇地献身,一个男人成熟的标志在于他甘愿为某个事业卑贱地活着。 出身寒微但心比天高的罗炼像个受伤的孩子。他的理想在天上,可惜他的通天塔需要从现实的最底层一砖一瓦地建立。太年轻的他无法忍受这样漫长的等待和付出,于是,他当了逃兵。 我想告诉他这样一句话:我们到遥远的地方去寻找,青鸟却在我们的出发之地。 背景 24岁的罗炼来自湖南省浏阳市沙市镇,高中文化,是一个喜欢读庄子的年轻人,系文学青年。5年前,带着梦想南下广东打工,先后辗转于珠江三角洲,做过保安、油漆工,跑过太阳能和房地产生意。2008年9月14日中秋节,在家具厂做学徒的他在月饼盒里,留下一纸手写字条后,悄然出走,至今不知所终。他在字条里写道:“终生役役而不见成功,苶(nie)然疲役而不知所向,讳穷不免,求通不得,无以树业,无以养亲,不亦悲乎!人谓之不死,奚益!” January 07 题目:LPC2000 启动代码Start.s文件简要分析
作者:Singyea@七星居
日期:2008.7.21
最近要做一个温度采集。大师兄非要上ARM7,可俺还差不多是个白板呢,只能恶补一下了,先找个启动代码看看,ARM的汇编跟x86下的就是不一样
啊(再说x86下的都菜的不行,呵呵),编译器与编译器之间的差异也挺大的。本例采用的是 Keil
环境下,由Keil自动生成的启动文件。平时可能用不着去自己写,可自己总是好奇心太重了,就拿来分析一下。对以后写启动代码或Bootloader也许
会有些帮助吧。
先说一下启动代码的位置,启动代码是在板子加电后首先执行的。所以非要用汇编来写才行。要完成处理器模式的初始化、设置中断向量表、设置各个模式下的堆栈、初始某些变量从而把系统带到一个合适的运行环境中开始用户程序的运行。
;-------------------------------------------------------------------------------
;
; 本段设置处理器的模式相关常量
;
;-------------------------------------------------------------------------------
; Standard definitions of Mode bits and Interrupt (I & F) flags in PSRs
; 定义常量,处理器的几种工作模式。
; ARM 有7种工作模式:
;
; User: 非特权模式,大部分任务执行在这种模式 正常程序执行的模式
; FIQ: 当一个高优先级(fast)中断产生时将会进入这种模式 高速数据传输和通道处理
; IRQ: 当一个低优先级(normal)中断产生时将会进入这种模式 通常的中断处理
; SVC: 用于操作系统的保护模式
; Abort: 当存取异常时将会进入这种模式 虚拟存储及存储保护
; Undef: 当执行未定义指令时会进入这种模式 软件仿真硬件协处理器
; System: 使用和User模式相同寄存器集的特权模式
;
; 这个值将被写到CPSR寄存器的第0,1,2,3,4,5位,以表示当前的状态。
Mode_USR EQU 0x10
Mode_FIQ EQU 0x11
Mode_IRQ EQU 0x12
Mode_SVC EQU 0x13
Mode_ABT EQU 0x17
Mode_UND EQU 0x1B
Mode_SYS EQU 0x1F
; 设置IRQ和FIQ中断禁止位,这两个值将被写到CPSR寄存器的第6位(FIQ)和第7位(IRQ)。1是禁止,0是允许。
I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled
;---------------------------------------------------------------------------------------------------
; 本段将完成堆栈相关的常量,
;
;---------------------------------------------------------------------------------------------------
;
; 设置各个模式下的栈大小
;
UND_Stack_Size EQU 0x00000000
SVC_Stack_Size EQU 0x00000008
ABT_Stack_Size EQU 0x00000000
FIQ_Stack_Size EQU 0x00000000
IRQ_Stack_Size EQU 0x00000080
USR_Stack_Size EQU 0x00000400
ISR_Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size)
; 初始化栈空间
; 定义一个数据段,名为STACK,NOINIT - 仅仅保留内存单元,还没有写入值,可读写,ALIGN=3 - 按字节对齐。
AREA STACK, NOINIT, READWRITE, ALIGN=3
; 分配内存,用户模式栈名为Stack_Mem,大小为1024字节;异常模式堆栈__initial_sp 大小为128+8字节。
Stack_Mem SPACE USR_Stack_Size
__initial_sp SPACE ISR_Stack_Size
Stack_Top
;
Heap_Size EQU 0x00000000
;堆空间,其中__heap_base 与__heap_limit 这两个符号是给采用了MICROLIB的程序准备的。
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
;-------------------------------------------------------------------------------------------
;
; 初始化 VPB总线的频率,Start的时候将分频因子设置为1,表示与CPU CLOCK相同
;-------------------------------------------------------------------------------------------
; VPBDIV 是VPB总线的分频因子
;
; VPBDIV definitions
VPBDIV EQU 0xE01FC100 ; VPBDIV Address
VPBDIV_SETUP EQU 1
VPBDIV_Val EQU 0x00000000
;-------------------------------------------------------------------------------------------
; 定义与锁相环相关的寄存器
; Phase Locked Loop (PLL) definitions
;-------------------------------------------------------------------------------------------
; 定义 PLL相关寄存器的基地址
PLL_BASE EQU 0xE01FC080 ; PLL Base Address
; 用偏移量的方法定义其他寄存器
PLLCON_OFS EQU 0x00 ; PLL Control Offset ;控制寄存器,写入该寄存器的值在馈送序列执行前不起作用
PLLCFG_OFS EQU 0x04 ; PLL Configuration Offset ;配置寄存器,属性同上
PLLSTAT_OFS EQU 0x08 ; PLL Status Offset ;状态寄存器,读取状态
PLLFEED_OFS EQU 0x0C ; PLL Feed Offset ;馈送寄存器,使能装载PLL控制和配置信息
PLLCON_PLLE EQU (1<<0) ; PLL Enable ;PLL使能,写入到配置寄存器的第0位,
; 初始为0,表示没有激活
PLLCON_PLLC EQU (1<<1) ; PLL Connect ;PLL连接使能位 ,写入到配置寄存器的第1位,当PLLC和
; PLLE都为1,且在有效的PLLE馈送后,将PLLE作为时钟源接
; 入Core,否则Core直接使用振荡器时钟。
PLLCFG_MSEL EQU (0x1F<<0) ; PLL Multiplier ;PLL 倍频因子
PLLCFG_PSEL EQU (0x03<<5) ; PLL Divider ;PLLE 分频因子
PLLSTAT_PLOCK EQU (1<<10) ; PLL Lock Status ;PLL锁定状态位,应该是从PLLSTAT寄存器的第10位读取数值
; 然后与 PLLSTAT_PLOCK 相比较,相同则为PLL锁定状态。
PLL_SETUP EQU 1
PLLCFG_Val EQU 0x00000024 ;表示向CFG配置寄存器写入的值为0 01 00100,跟系统所需的频率有关。
;LPC2119的主频最高是60MHz,振荡器的主频是10MHz
;分频值为1,倍频值为4,所以主频为40Mhz。
;-------------------------------------------------------------------------------------
; 存储器加速模块
;
;-------------------------------------------------------------------------------------
; Memory Accelerator Module (MAM) definitions
MAM_BASE EQU 0xE01FC000 ; MAM Base Address
MAMCR_OFS EQU 0x00 ; MAM Control Offset
MAMTIM_OFS EQU 0x04 ; MAM Timing Offset
MAM_SETUP EQU 1
MAMCR_Val EQU 0x00000002
MAMTIM_Val EQU 0x00000004
;---------------------------------------------------------------------------
; 外部存储器扩展
;
;---------------------------------------------------------------------------
; External Memory Controller (EMC) definitions
EMC_BASE EQU 0xFFE00000 ; EMC Base Address
BCFG0_OFS EQU 0x00 ; BCFG0 Offset
BCFG1_OFS EQU 0x04 ; BCFG1 Offset
BCFG2_OFS EQU 0x08 ; BCFG2 Offset
BCFG3_OFS EQU 0x0C ; BCFG3 Offset
;// <e> External Memory Controller (EMC)
EMC_SETUP EQU 0
BCFG0_SETUP EQU 0
BCFG0_Val EQU 0x0000FBEF
BCFG1_SETUP EQU 0
BCFG1_Val EQU 0x0000FBEF
BCFG2_SETUP EQU 0
BCFG2_Val EQU 0x0000FBEF
BCFG3_SETUP EQU 0
BCFG3_Val EQU 0x0000FBEF
;// </e> End of EMC
; External Memory Pins definitions
PINSEL2 EQU 0xE002C014 ; PINSEL2 Address
PINSEL2_Val EQU 0x0E6149E4 ; CS0..3, OE, WE, BLS0..3,
; D0..31, A2..23, JTAG Pins
PRESERVE8 ;汇编伪指令,堆栈8字节对准
;-------------------------------------------------------------------------------
; 前面都是对寄存器位置的声明,以下是具体代码。定义了一个代码段
;
;-------------------------------------------------------------------------------
;
; Area Definition and Entry Point
; Startup Code must be linked first at Address at which it expects to run.
;定义个名为RESET的代码段
AREA RESET, CODE, READONLY
ARM
; 中断向量表的异常入口共8条语句32字节,在链接的时候保证在0地址处
Vectors LDR PC, Reset_Addr ;将Reset_Addr加载到PC中,程序就跳到reset_addr所指位置。
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP ; Reserved Vector
; LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0] ; Vector from VicVectAddr
LDR PC, FIQ_Addr
Reset_Addr DCD Reset_Handler ;分配一段字内存单元给程序段Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
;
Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler
;-----------------------------------------------------------------------------------
;
;
;-----------------------------------------------------------------------------------
; Reset Handler
EXPORT Reset_Handler
Reset_Handler
;设置扩展存储的引脚
; Setup External Memory Pins
IF :DEF:EXTERNAL_MODE ;判断 EXTERNAL_MODE
是否定义,如果定义了就设置响应的管脚。见某论坛上的一个人问,外部存储器设置了但是不能工作,就是这里的 EXTERNAL_MODE
没有被定义导致扩展 存储的引脚的代码没有被编译所以没有被初始化,EXTERNAL_MODE EQU 1,应该就可以了 ,还没测试。
LDR R0, =PINSEL2
LDR R1, =PINSEL2_Val ;前面 PINSEL2_Val 被初始化为0x0E6149E4,完成一系列管脚的初始化
STR R1, [R0]
ENDIF
;设置扩展存储器的控制寄存器,分别检测几个bank是否存在,分别进行设置
; Setup External Memory Controller
IF EMC_SETUP <> 0
LDR R0, =EMC_BASE
IF BCFG0_SETUP <> 0
LDR R1, =BCFG0_Val
STR R1, [R0, #BCFG0_OFS]
ENDIF
IF BCFG1_SETUP <> 0
LDR R1, =BCFG1_Val
STR R1, [R0, #BCFG1_OFS]
ENDIF
IF BCFG2_SETUP <> 0
LDR R1, =BCFG2_Val
STR R1, [R0, #BCFG2_OFS]
ENDIF
IF BCFG3_SETUP <> 0
LDR R1, =BCFG3_Val
STR R1, [R0, #BCFG3_OFS]
ENDIF
ENDIF ; EMC_SETUP
;设置VPB总线的分频因子,这里分频因子被设置为0
; Setup VPBDIV
IF VPBDIV_SETUP <> 0
LDR R0, =VPBDIV
LDR R1, =VPBDIV_Val
STR R1, [R0]
ENDIF
;设置锁相环
; Setup PLL
IF PLL_SETUP <> 0
LDR R0, =PLL_BASE
MOV R1, #0xAA
MOV R2, #0x55
; Configure and Enable PLL
MOV R3, #PLLCFG_Val
STR R3, [R0, #PLLCFG_OFS] ;PLLCFG_Val = 0x00000024
MOV R3, #PLLCON_PLLE ;R3 = 1
STR R3, [R0, #PLLCON_OFS] ; PLLCON = 1,PLL 使能
STR R1, [R0, #PLLFEED_OFS] ; 写 馈送寄存器命令序列一次是10101010一次01010101,0xAA,0x55。规定的。
STR R2, [R0, #PLLFEED_OFS]
;等待PLL稳定
; Wait until PLL Locked
PLL_Loop LDR R3, [R0, #PLLSTAT_OFS] ;将状态寄存器装载到R3中
ANDS R3, R3, #PLLSTAT_PLOCK ;PLLSTAT_OFS 与1<<10 进行与且影响 CPSR 的Z位,
BEQ PLL_Loop ;Z位为1 跳到PLL_Loop
; Switch to PLL Clock
MOV R3, #(PLLCON_PLLE:OR:PLLCON_PLLC)
STR R3, [R0, #PLLCON_OFS]
STR R1, [R0, #PLLFEED_OFS]
STR R2, [R0, #PLLFEED_OFS]
ENDIF ; PLL_SETUP
;-----------------------------------------------------------------------------
; 存储器加速模块
; Setup MAM
IF MAM_SETUP <> 0
LDR R0, =MAM_BASE
MOV R1, #MAMTIM_Val
STR R1, [R0, #MAMTIM_OFS]
MOV R1, #MAMCR_Val
STR R1, [R0, #MAMCR_OFS]
ENDIF ; MAM_SETUP
;--------------------------------------------------------------------------
;内存映射,结构有点复杂额,总之就是根据不同的情况去设置存储器映射控制寄存器,
; 00 Boot装载,中断向量从BootBlock重新映射
; 01 用户FLASH模式,中断向量不重新映射,就在FLASH里面
; 10 用户RAM模式,中断向量从静态RAM重新映射
; 11 用户外部存储器模式,中断向量从外部存储器重新映射。
; Memory Mapping (when Interrupt Vectors are in RAM)
;----------------------------------------------------------------------------
MEMMAP EQU 0xE01FC040 ; Memory Mapping Control ;
IF :DEF:REMAP
LDR R0, =MEMMAP
IF :DEF:EXTMEM_MODE ;判断是不是扩展模式 是就往R1里写3
MOV R1, #3
ELIF :DEF:RAM_MODE ;如果是RAM中,往R1里写2
MOV R1, #2
ELSE
MOV R1, #1 ;否则R1里写1,唉汇编的编译判断真够烦的。
ENDIF
STR R1, [R0] ;写到MEMMAP寄存器。
ENDIF
; Initialise Interrupt System
; ...
;为处理器的每种处理模式初始化栈空间
; Setup Stack for each mode
LDR R0, =Stack_Top
; Enter Undefined Instruction Mode and set its Stack Pointer
MSR CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit ;CPSR_c = 0x1B|0x80|0x40 = 意思为禁止IRQ和FIQ,ARM状态,处理
器进入了未定义模式
MOV SP, R0 ;栈指针指向了Stack_Top
SUB R0, R0, #UND_Stack_Size ;Stack_Top = Stack_Top - ABT_Stack_Size
; Enter Abort Mode and set its Stack Pointer
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size
; Enter FIQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size
; Enter IRQ Mode and set its Stack Pointer
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size
; Enter Supervisor Mode and set its Stack Pointer
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size
; 进入用户模式,并且设置栈空间指针
; Enter User Mode and set its Stack Pointer
MSR CPSR_c, #Mode_USR
IF :DEF:__MICROLIB ;支持 MICROLIB库,如果C程序的运行库是MICROLIB 则将__initial_sp 导出
EXPORT __initial_sp
ELSE
MOV SP, R0
SUB SL, SP, #USR_Stack_Size
ENDIF
;进入C代码的执行,为C代码的执行创造一个运行环境
; Enter the C code
IMPORT __main ;导入外部符号
LDR R0, =__main ;将main的地址加载到r0中
BX R0 ;带状态的跳到main 去执行
IF :DEF:__MICROLIB ;还是有关使用MICROLIB库的,如果使用了该库就要导出下面连个符号。
EXPORT __heap_base
EXPORT __heap_limit
ELSE
; User Initial Stack & Heap
AREA |.text|, CODE, READONLY
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + USR_Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ENDIF
END
一直对底层的东西不太懂,总是朦朦胧胧的感觉,索性拿启动代码看一下吧,指望着能学一下汇编。周末的学习效率是在太低了,一个小小的启动文件竟然费了两天功夫。费了牛劲了看是看完了,还是有好多地方一头雾水。因为认识的深度有限,错误的地方肯定少不了,所以还请您校正。
| LPC2294-LDR PC,[PC,#-0x0FF0]阐释
|
|
[ 2007-4-13 22:16:00 | By: CANopen ] |
;异常向量表如下: CODE32 AREA vectors,CODE,READONLY ENTRY Reset LDR PC,ResetAddr LDR PC,UndefinedAddr LDR PC,SWI_Addr LDR PC,P_Abort_Addr LDR PC,D_Abort_Addr DCD 0xB9205F80 LDR PC,[PC,#-0x0FF0] LDR PC,FIQ_Addr ResetAddr DCD ResetInit UndefinedAddr DCD Undefined SWI_Addr DCD SoftwareInterrupt P_Abort_Addr DCD P_Abort D_Abort_Addr DCD D_Abort Reserved DCD 0 IRQ_Addr DCD 0 FIQ_Addr DCD FIQ_Handler
LPC2294基于ARM7TDMI-S内核,由于该体系结构采用三级流水线技术,对于ARM指令集而言,PC总是指向当前指令的下两条指令的地址,即PC的值为当前指令的地址值加8个字节。
当发生IRQ异常CPU跳转至异常向量表0x00000018处执行指令LDR
PC,[PC,#-0x0FF0],此时程序计数器PC中内容应该为0x00000020,CPU执行完成该指令将地址
【0x00000020-0x0ff0=】0xFFFFF030(VICVectAddr)装入PC,程序转入IRQ异常处理例程。
|