风声竹影 的个人资料听风竹轩的书架日志列表网络 工具 帮助

日志


1月31日

memset ,memcpy 和strcpy 的根本区别?

声明:
以下内容为网络整理的结果!


它们用处不同,但大部分情况下可以完成相同的要求。

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不需要
1月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

用strcoll实现中文按拼音排序

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语言中利用strstr函数进行字符串分割

在前面C语言中利用strtok函数进行字符串分割介绍的strtok函数,比较适合多个字符(也就是字符串)作分隔符的场合,而很多时候我们仅仅需要某一个特定的字符来分割字符串,当然利用strtok也可以实现,不过这里介绍的strstr效率上来说更加适合。

原型:extern char *strstr(char *haystack, char *needle);
所在头文件:#include <string.h>
功能:从字符串haystack中寻找needle第一次出现的位置(不比较结束符NULL)。
说明:返回指向第一次出现needle位置的指针,如果没找到则返回NULL。

具体使用例子:

  1. #include <stdio.h>
  2. #include <string.h> 
  3.  
  4. int main(int argc,char **argv)
  5. {
  6. char *haystack="aaa||a||bbb||c||ee||";
  7. char *needle="||";
  8. char* buf = strstr( haystack, needle);
  9. while( buf != NULL )
  10. {
  11.     buf[0]='\0';
  12.     printf( "%s\n ", haystack);
  13.     haystack = buf + strlen(needle);
  14.     /* Get next token: */
  15.     buf = strstr( haystack, needle);
  16. }
  17.    return 0;
  18. }

C语言中利用strtok函数进行字符串分割

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中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。

具体使用例子:

  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(int argc,char **argv)
  4. {
  5. char * buf1="aaa, ,a, ,,,bbb-c,,,ee|abc";
  6. /* Establish string and get the first token: */
  7. char* token = strtok( buf1, ",-|");
  8. while( token != NULL )
  9.     {
  10. /* While there are tokens in "string" */
  11.         printf( "%s ", token );
  12. /* Get next token: */
  13.         token = strtok( NULL, ",-|");
  14.     }
  15. return 0;
  16. }

JAVA字符串分割的两种方法–split和StringTokenizer

在JAVA字符串处理函数列表一览中介绍了比较多的字符串处理函数,不过有一种我们经常会用到的处理没有被加入–字符串的分割。下面是分别用split函数和StringTokenizer类来实现字符串分割的简单代码:

●利用split函数:
  1. String s = new String("2_8_7_4_3_9_1");
  2. String[] arr = s.split("_");

●利用StringTokenizer类:

  1. String s = new String("2_8_7_4_3_9_1");
  2. StringTokenizer commaToker = new StringTokenizer(s, "_");
  3. String[] arr = new String[commaToker.countTokens()];

JAVA字符串处理函数列表一览

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)

1月9日

80后的理想脆弱如纸?

28年前,一封名为《人生的路为什么越走越窄》的读者来信见报后,曾在全国青年人中引发了一场关于理想和现实的大讨论。
28年后,一个24岁的来自底层的打工青年再次被逼至墙角——当理想被沉重的现实不断围剿,直到成为他生命中不能承受之重。他最后选择了在人间蒸发,成为他亲人的长久牵挂。
这个喜欢庄子的80后叫罗炼,他的故事受到了湖南卫视《零点锋云》栏目的关注,节目播出后迅速成为社会热议的一个话题。当栏目制作方邀我为这个年轻人写点什么的时候,我毫不犹豫地答应了——只为一个今天太多人认为很装很虚无的东西——理想。
在这个充斥着娱乐致死加功利主义盛行的时代,罗炼的故事显得遗世独立,对太多人进行了一次真切的逼问:现实充满诱惑更充满严酷的挑战,你还会仰望理想的星空吗?您将凭借什么坚持到底?
有志青年罗炼怀揣着纸一般脆弱的文学梦,怀揣着家人殷殷的期待离开乡村,来到南下广东打工,先后辗转于珠江三角洲,做过保安、油漆工,跑过太阳能和房地产生意,开始了理想与现实的残酷PK。罗炼面对的是一个日益繁荣的商业世界,每天都在激发他的进取心和创业成功的渴望。整整六年,他是那样努力,但成功依旧那么遥远。终于,由于生存压力如影随形的逼迫,理想还未展翅翱翔,便已支离破碎,宛如断线的纸鸢。
逐步走向时代舞台中心的80后,是一群幸运儿。因为他们有着更完美的物质环境、更开阔的眼界、更充盈的知识、更独立的个性。但他们又是不幸的。这些家庭的宠儿(绝大多数是独生子女)往往都从小缺乏苦难和挫折的锤炼,一上路便极易受现实急功近利思潮熏陶,往往追求速成,忽略对现实的适应能力的培养、自身能力的提升和社会经验的累积,一旦遭遇社会充分竞争带来的巨大压力以及阶层分化带来的各种机会不公,便要么迅速麻木,甚至蜕变成及时行乐的猪;要么悲观厌世,选择逃避,如同罗炼。
从这个意义上,整个80后乃至更多理想主义者在和罗炼一起流浪。他们往往胸怀大志,却总是慨叹报国无门。我首先还是为他们的坚持致敬,因为按照人正常发展的规律,你如果放弃了理想,那你下一步一定会尝试放弃良心。
年轻的罗炼们其实无处逃遁,因为世间已无“不知有汉,无论魏晋”的桃花源。
我想起一个这样的故事。伊斯兰教创始人默罕莫德带领大军进行圣战的途中,遇到一座险峻高山,士兵望而生畏,默罕莫德说:我念一个咒语,真主就会让山自动移开。谁料念罢山岿然不动,于是,默罕莫德说:既然山不走向我们,就让我们走向山吧。
要想坚持并实现理想,首先要学会承认现实。因为这是你无法改变和逃避的。要学会“头顶理想的天空,脚踩现实的大地。”这个世界从来都不完美,也充满不公,但改变这一切不正是我们存在的理由吗?
其次,要明白任何一个理想的实现都不是由于一个伟大的想法,而是因为一个伟大的方法。这需要经验、阅历甚至学识的积累和百折不回的探索——“有何胜利而言,挺住意味着一切!”
最后,也是最重要的是内心的修炼,比行动上不抛弃、不放弃更关键的是不抱怨。塞林格在《麦天里的守望者》写下这么一句充满智慧的话:一个男人不成熟的标志在于他甘愿为某个事业英勇地献身,一个男人成熟的标志在于他甘愿为某个事业卑贱地活着。
出身寒微但心比天高的罗炼像个受伤的孩子。他的理想在天上,可惜他的通天塔需要从现实的最底层一砖一瓦地建立。太年轻的他无法忍受这样漫长的等待和付出,于是,他当了逃兵。
我想告诉他这样一句话:我们到遥远的地方去寻找,青鸟却在我们的出发之地。

背景
24岁的罗炼来自湖南省浏阳市沙市镇,高中文化,是一个喜欢读庄子的年轻人,系文学青年。5年前,带着梦想南下广东打工,先后辗转于珠江三角洲,做过保安、油漆工,跑过太阳能和房地产生意。2008年9月14日中秋节,在家具厂做学徒的他在月饼盒里,留下一纸手写字条后,悄然出走,至今不知所终。他在字条里写道:“终生役役而不见成功,苶(nie)然疲役而不知所向,讳穷不免,求通不得,无以树业,无以养亲,不亦悲乎!人谓之不死,奚益!”

1月7日

[精]LPC2000 启动代码Start.s文件简要分析

题目: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]阐

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异常处理例程。

[精]ARM LPC2000 启动代码分析

1、将寄存器定义到ARM_LPC2000,并用 INCLUDE ARM_LPC2000.INC 包含到主程序文件开头,注意INCLUDE要顶格。

2、设置Targets Settings中Post-linker要选择 ARM fromELF,并在 ARM fromELF设置中使用二进制 plain binary.

3、设置Targets Settings中ARM Linker的Output页的Linktype为**,并设 RO Base = 0x0, RW Base = 0x40000000

4、建立工程时要不要选择周立功的向导,直接用最上面第一项

以下是代码:

INCLUDE ARM_LPC2000.INC
GBLL TargetDowload
GBLL DebugTesting
; GBLL SecurePretect
GBLL PhaseLockLoop

;;===========================================================================================
u0_baud   EQU  115200   ; 115200 = 0x1C200; 11059200 = 0xA8C000;
Fosc   EQU  11059200        ; Crystal frequence:11059200/27648000/55296000。
Fcclk   EQU  (Fosc  * 4 )    ; 系统频率,必须为Fosc的整数倍(1~32),且<=60MHZ。
Fcco   EQU  (Fcclk * 4)     ; CCO频率,必须为Fcclk的2、4、8、16倍,范围为156MHz~320MHz。
Fpclk   EQU  (Fcclk / 4) * 1 ; VPB时钟频率,只能为(Fcclk / 4)的1、2、4倍。
; ===========================================================================================
SVC_STACK_LEGTH         EQU         0
FIQ_STACK_LEGTH         EQU         0
IRQ_STACK_LEGTH         EQU         256
ABT_STACK_LEGTH         EQU         0
UND_STACK_LEGTH         EQU         0

; ===========================================================================================
;  程序部分:初始化及中断向量
;  
; ===========================================================================================
AREA Initialize,CODE,READONLY    ; AREA 段名(独立) CODE|DATA| 属性
ENTRY
CODE32    ; CODE32: 以下为 ARM 指令;CODE16: 以下为 THUMB 指令
; ===========================================================================================
Reset
LDR  PC, Reset_Address           ;; 0x00-复位异常向量
LDR  PC, Undefine_Address         ;; 0x04-未定义指令异常
LDR  PC, SWI_Address           ;; 0x08-SWI 异常
LDR  PC, PrefetchAbort_Address      ;; 0x0C-指令预取中止异常
LDR  PC, DataAbort_Address       ;; 0x10-数据预取中止异常
DCD  0xb9205f88          ;; 为使向量表所有32位数据累加和为零而补数,LPC2000系列仅校验0x00~0x1F
    LDR  PC, [PC,#-0x0FF0]        ;; 0x18-IRQ 异常    PC执行完本指令后指向0x20,此时PC-#0xFF0=0xFFFFF030,正是VICVectAddr地址
LDR  PC, FIQ_Address         ;; 0x1C-FIQ 异常

Reset_Address   DCD  Reset_Initialize   ;; 返回:BL => MOV PC,LR
Undefine_Address  DCD  Undefine_Handler   ;; 返回:MOVS  PC,R14_und
SWI_Address    DCD  SWI_Handler     ;; 返回:MOVS  PC,R14_svc
PrefetchAbort_Address DCD  PrefetchAbort_Handler  ;; 返回:SUBS  PC,R14_abt,#04
DataAbort_Address  DCD  DataAbort_Handler   ;; 返回:SUBS  PC,R14_abt,#08
FIQ_Address    DCD  FIQ_Handler     ;; 返回:SUBS  PC,R14_fiq,#04

; ===========================================================================================
Undefine_Handler  B  .
SWI_Handler    B  SWI_Exception
PrefetchAbort_Handler B  .
DataAbort_Handler  B  .
FIQ_Handler
STMFA SP!, {R0-R3, LR}
BL  FIQ_Exception
LDMFA SP!, {R0-R3, LR}
SUBS PC, LR, #4

;;*******************************************************************************************
;;===============  SWI 调用 ===============

; MOV R0,#0x12
; MOV R1,#0x31     ;;  调用0x12(R0)号软中断的0x31(R1)号子功能。
; SWI 0       ;; 相当于 mov r0,#0x31; swi #0x12

SWI_Exception            ;; SWI: CPU Mode exchanging!
MOV PC,LR

FIQ_Exception            ;; FIQ: Winthin 27 clock!
MOV PC,LR
; ===========================================================================================
;        InitStack & InitTarget
; ===========================================================================================
InitStack
MOV R0,LR
MSR CPSR_c,#0x000000D3
LDR R1,=SvcStackSpace
MOV SP,R1

MSR CPSR_c,#0x000000D2
LDR R1,=IrqStackSpace
MOV SP,R1

MSR CPSR_c,#0x000000D1
LDR R1,=FiqStackSpace
MOV SP,R1

MSR CPSR_c,#0x000000D7
LDR R1,=AbtStackSpace
MOV SP,R1

MSR CPSR_c,#0x000000Db
LDR R1,=UndtStackSpace
MOV SP,R1

MSR CPSR_c,#0x0000005F  ;#0x000000DF修改,打开中断
LDR SP,=StackUSR
MOV PC,R0

InitTarget
  ldr r0,=0xe01fc000
;; /* Set Up ReMap */
  mov      r1,#1    ;;1->#0xe01fc040: image to Flash,Boot loader; ;;0x02: image to RAM,User program
  str      r1,[r0,#0x40]

    if  :def:  PhaseLockLoop  ;;/////////////
;; /* Set system timers for each component */
mov      r1,#1     ;;   1-enable
strb     r1,[r0,#0x80]   ;; Step1:Enable PLL by PLLCON(0xe01fc080)

if (Fpclk/(Fcclk/4)) = 1  ;; Step2:Setup reg_VPBDIV
  mov      r2,#0
  strb     r2,[r0,#0x100]
endif
if (Fpclk/(Fcclk/4)) = 2
  mov      r2,#2
  strb     r2,[r0,#0x100]
endif
if (Fpclk/(Fcclk/4)) = 4
  mov      r2,#1    ;;  MEMVALB VPBDIV,0x00010001
  strb     r2,[r0,#0x100]  ;;  Fcclk/Fpclk 比值: =1:00,=2:10,=4:01; b7~4=b3~0
endif

if (Fcco/Fcclk) = 2    ;; Step3:Setup reg_PLLCFG
  MEMVALB PLLCFG,((Fcclk/Fosc) - 1)|(0<<5)
endif
if (Fcco/Fcclk) = 4
  mov      r1,#0x23   ;;  MEMVALB PLLCFG,((Fcclk/Fosc) - 1)|(1<<5)
  strb     r1,[r0,#0x84]
endif
if (Fcco/Fcclk) = 8
  MEMVALB PLLCFG,((Fcclk/Fosc) - 1)|(2<<5)
endif
if (Fcco/Fcclk) = 16
  MEMVALB PLLCFG,((Fcclk/Fosc) - 1)|(3<<5)
endif

mov      r3,#0xaa
strb     r3,[r0,#0x8c]   ;; PLLFEED = 0xAA;
mov      r12,#0x55    ;; PLLFEED = 0x55;
strb     r12,[r0,#0x8c]

ldrh     r1,[r0,#0x88]  ;;PLLSTAT
tst      r1,#0x400   ;;  Test PLLSTAT_bit10 = 1?
beq      {PC} - 12   ;; Step5:waiting until LOCK signal.

mov      r1,#3    ;;PLLCON 3-connect
strb     r1,[r0,#0x80]  ;; Step6:Connect PLL

strb     r3,[r0,#0x8c]
strb     r12,[r0,#0x8c]  ;; Step7:Feedback 0xaa,0x55 once more to avalid setting!
endif      ;;/////////////

if :def: TargetDowload  ;;////####
;; /* Set memory accelerater module*/
MEMVALB MAMCR,0x0000  ;; Step1:disable MAM.
if Fcclk < 20000000
  MEMVALB MAMTIM,0x0001
else
if Fcclk < 40000000
  MEMVALB MAMTIM,0x0002
else
  MEMVALB MAMTIM,0x0003
endif
endif
MEMVALB MAMCR,0x0000  ;; Step2: enable MAM.

;----------- Set Up VIC:
mvn      r0,#0    ;; r0 = 0xffffffff
mov      r2,#0
str      r0,[r2,#-0xfec] ;;VICIntEnClr = 0xffffffff
str      r2,[r2,#-0xfd0] ;;VICVectAddr = 0x0
str      r2,[r2,#-0xff4] ;;VICIntSelect= 0x0
endif      ;;////####

mov pc,lr
; ===========================================================================================
Reset_Initialize
  BL InitStack
  BL InitTarget
  B InitSystem
; ===========================================================================================
__user_initial_stackheap
    LDR   r0,=bottom_of_heap
;    LDR   r1,=StackUSR
    MOV   pc,lr
; ===========================================================================================
if :def: SecurePretect
  if :def: EN_CRP     ;; 芯片加密:向地址0x1FC处写0x87654321 便实现了ARM7加密,只能ISP擦除。
         if  . >= 0x1FC
         INFO    1,"\nThe data at 0x000001fc must be 0x87654321."
         endif
CrpData
     while . < 0x1FC
     NOP
     wend
CrpData1                    ;; 对于LPC2100系列ARM7核:
     DCD     0x87654331  ;; When the Data is 0x87654321 on address_0x1FC,user code be protected.
     endif
   endif
; ===========================================================================================    
; 设置 ARM Linker 类型:Simple image 的 RO Base: 0x00000000; RW Base: 0x40000300。
; 或  Command Line:-info totals -entry 0x00000000 -ro-base 0x00000000 -rw-base 0x40000300

AREA   MyStacks, DATA, NOINIT, ALIGN=2  ;ALIGN=2 表示代码是04字节对齐的。

SvcStackSpace       %   SVC_STACK_LEGTH * 4  ;Stack spaces for Administration Mode 管理模式堆栈空间
IrqStackSpace       %   IRQ_STACK_LEGTH * 4  ;Stack spaces for Interrupt ReQuest Mode 中断模式堆栈空间
FiqStackSpace       %   FIQ_STACK_LEGTH * 4  ;Stack spaces for Fast Interrupt reQuest Mode 快速中断模式堆栈空间
AbtStackSpace       %   ABT_STACK_LEGTH * 4  ;Stack spaces for Suspend Mode 中止义模式堆栈空间
UndtStackSpace      %   UND_STACK_LEGTH * 4  ;Stack spaces for Undefined Mode 未定义模式堆栈

AREA   Variable, DATA

BufferTxd   SPACE  100  ;
BufferRxd   SPACE  100  ;
BufferCurTime  SPACE  16  ;
BufferI2CSlaveAddress   %       1  ;包含 bit=0/1 读写信息
BufferI2CNumber      %     1       ;
BufferI2CData      %     0x100   ;

AREA   Heap, DATA, NOINIT
bottom_of_heap  SPACE   1

    AREA   Stacks, DATA, NOINIT
StackUSR   SPACE 100

;;===========================================================================================
AREA MainProgram,CODE,READONLY
CODE32    ; CODE32: 以下为 ARM 指令;CODE16: 以下为 THUMB 指令
;;===========================================================================================

;; 从这里开始写你自己的程序

芯晟要走了他的第一次

芯晟要走了我的第一次

关键词中兴    数码视讯    和协航电    东方口岸    芯晟                                          

引言

 

随着111号的三方协议落章、15号的体检报告等有关材料的递交,长达数月的万里长征之找工作终于告一段落了。

读了这么多年书,最终还是为谋一份好点儿的工作,做为第一份工作,有必要将此过程小小的总结下,也给路人一些启发。

 

背景

 

本科的时候没有好好地找过工作,也没关注过其他人寻找的过程,对此没有概念没有经验没有危机感,此后大把机会的浪费换取着经验的积累。

首 先是时间的仓促。本想在十一的时候好好把简历给整理整理,以待节后正式开始长征,但事与愿违,实验室突然发下任务,必须在假期结束前完成,什么任务啊,尽 是些毫无技术含量的纯体力活,其他的抱怨就不再说了。开始写简历了,这才发现那个什么才尽,凭着个人的想象力,天花乱缀的吹了通,不过这个时候我也认为, 适度的夸张还是有必要的——简历关都过不了你连表现的机会都没有,接下来的无数次笔试机会就证明了这一点,当然你也要对相应的东西事先了解了解。在接下的 日子里,如果我一有什么灵感都会修改简历,最重要的是,我会根据和技术面试官的面试讨论,把简历中的内容做有针对性的修改。

还有就是准备不足了。这包括知识准备不足、自己定位不足。都笔试、面试这么多次后才发现自己的种种方面一窍不通,接下来就广搜资料疯狂补习,还有一个我认为比较好的习惯就是,每次笔/面试时我都会把我不肯定的或不知道的题目用各种方法快速的记下来,回头立刻解决他们(及其相关内容),这让我在每次笔试后都有收获。由于笔试的东西大都是些比较基础的点,笔到一定程度后我再回过头去想以前那些NB公司的笔试实在是太easy了, 面烂后再想想有什么场面的面试我不能摆平的,赫赫。由此可见,此准备能让你抓住很多机会,很多好机会。。。接着是定位不足!原先搞的东西硬件偏多些,就理 所当然的奔着硬件工程师的职位去投、去笔、去面,之后才发现自己离它的要求差的太多,然后去饿补,又发现自己对此毫无兴趣,虽然在此过程中得到了中兴等公 司的offer,但我决定立刻转型,转型为嵌入式软件,此时已经过了两个星期了,也错过了好多好公司了。可见,先期的定位非常重要,兴趣、职位特点、个人能力你要清楚,要不然到时浪费的不单是时间、精力、金钱,还有一去不复返的机会。

最 后就是压力了。这点上我还是做的不错的,其实这样说对自己还是有点讽刺意味的。理工科的研究生在实验室里还是有不少活要做的,有时在那个时候某个紧急项目 刚好出了点什么问题,那些都是需要你投入不少时间和精力才能摆平的。我清楚地知道,对于刚转型的我,还有太多的东西要去了解去学习去实践,我急于在每次笔 试面试前准备的更充分点,我不想让我自以为强大的能力( :) ) 无用武之地,(在这个阶段)工作与实验室不可兼得。我选择了前者。我每天频繁的外出,就算在实验室也是忙自己的事。对此,也肯定是要付出代价的。老师们关 心的是项目有没进度,你为实验室贡献了多少,他们不会在乎你的特殊情况,你找工作的艰辛。想想以前老师经常跟我们说的那句话心里就觉得好笑——“你们以后 出去肯定是月薪八千”。。。。 由 于我的反常,我被找去了谈话,被威胁到,如果还是这种状态,毕业有难度啊,找到工作了如果毕不了业也没用啊。我的反应很显然了,估计大家都是一样。没办 法,我只好再挤出点精力关心关心这可爱的实验室,没办法,没有丝毫积累的新实验室总是让人很累,让人做很多无用功。幸好的是,最艰难的转型期已经过去了。

 

网投

 

在 中英文简历马马虎虎写好后,我就开始迫不及待地撒网了。由于我认为自己不是很强,简历的命中有时还要看个人的运气,加上实验室宽带的速度还不错,我选择广 撒网,我不能保证我觉得适合的公司适合的职位就会给我机会,我决定只要稍微适合自己的我都花上几分钟用鼠标点点(有简历模板、或者把写好的东西copy上去),投!多花这么几分钟不会怎么样,等它考虑好我了我再考虑它!

为了不重复投,我把每个网投或现场投的公司名字都记录下来了,回头一看,乖乖,已经有100多家了,呵呵,北京(主要是在北京的)的好公司还真不少啊,之前我就不认得几个公司,通过找工作还真是开眼界了。

 

笔霸

 

10.22.上午830 1250 清华建筑报告厅 海信 素质测评and 专业笔试(硬件)

这是我第一次笔试,十分兴奋,早早的就起来了,打扮的帅帅的,激动的迎接我的处女笔。却没想到过程如此复杂,先是花了近一个小时填了张详细资料表,接着做了一百多道脑筋急转弯,最好就是专业题了。虽然结束时已经是将近1点了,但新鲜感弥盖了疲劳。

接下来的笔试是单身的男子,一大把一大把的,相互间的时间冲突是很正常的,一天赶三场也不是没有的,对此华为就被不少人放弃过。

 

面霸

 

由于自己的英语太烂,大外企一般都与我无缘。

总的面试次数还是很多的,给我深刻印象也具里程性的就数炬力(还有大唐)了,前面也提到过它,这是我离一个好offer最近的一次了。它让我知道,每一个面试都不应该掉以轻心,即使是在谈offer的阶段,对于一些开放式问题一定要有所准备,临时的发挥是很勉强的,事后我专门花了不少时间研究了比较热门的智力题和开放式问题,对于后者我仔细的看了别人推荐的范例,我再结合自己的想法,基本上能给出令面试官满意的回答了,事实证明效果也相当不错,而且对于后来的拒offer和毁约也帮助很大。

 

        OFFER

 

中兴通讯,我的第一个offer,理当纪念下,硬件工程师,虽然号称810W/y,没办法,住房公积金(或房补)很不确定,加上不解决北京户口,时间还尚早,理所当然,拒之。后来看理工第一批录取的也没谁跟它签,考虑的不多。

之后还有一些小offer了,但真的不值一提。但到最后先后出现了数码视讯、和协航电、东方口岸、芯晟,他们确实让我幸福地辛苦了一大把。。。。。。

 

数码视讯

 

地点很远,虽然交通快捷,一公交一城铁半小时多点的路程,在没办IC卡前一去一回可是要8块人民币啊,前后算了下大概共去过那5次。

12.1笔 试的,两星期后面试,其中也上网了解过它,有一个帖子说了它很多负面的东西,就此我还跟技术面试官小谈过呢,毕竟那是很早以前的事了,要带着发展的眼光辨 正的看。在技术面时他还给我出了道智力题,向两个人问一句话得出正确的路线,虽然和我以前研究过的原题不一样,但思路都差不多,思考几分钟后给出了答案。 感觉到offer不远了,给了说了很多加入这公司的利和弊,还给了我他的手机号,说有什么问题随时联系他,感动。之后很正常的被通知录取了,但时间上逼迫的很紧,考虑到已经12月底了,待遇及各方面也达到了自己的心理底线,在和面试官电话询问了公司的更多细节后,便和它签了个“接收协议”,幸好的是,它没接着催我签三方等,这点也是我一直纳闷的,至今还没明白。

……

……

之后我确定自己要毁了和数码视讯的“接收协议”时,一直在想着怎么个处理办法,毕竟上面写着要交3000块违约金啊,虽不多但也不少,起码让人很不爽啊。开始是打算等公司通知去签三方时先拖着,然后再说想毁约,还是学生没钱,能懒就懒了吧。

……

……

直到后来我去院就业办给三方盖章时,听某人建议就把那份我和公司间的“接收协议”带去问问老师,看违约后有什么后果有什么建议没。结果那些人一看到这协议就把我三方给扣了,说先解除了这个协议再还我三方,KAO!我就懵了,还好“越是在压力下越能激发出我的潜能”,我大致敲定出一套方案,马上给数码打了个电话,说当天下午去趟公司关于解除协议的事,在谈话中我本着诚恳的态度,在大部分实话实说的内容中,略微修改了些细节,最终零后果地搞定了这个事情。

也许数码视讯真的在一点一点的改变,它最终还是给我留下了不错的印象,祝福它今年能成功上市。

 

和协航电

 

公司地点太远了,虽然从学校坐一趟车就能到,但要在车上待一个多小时啊,还好只去过那两次,一次是笔试加一面,第二次是从上午到免费午饭再到下班共四面,上午出发的结果天黑了才到校。我经历笔试程序最多是海信,笔了三次——素质、专业、英语,和协航电是我面试程序最多的了,5面,能把你面到天黑到面烂!而且结合和众面试官的谈话可以感觉到,和协航电做事很认真,对人很用心,后来的事也能说明这点。

……

……

其实,和协航电的待遇不错了,但我最后还是放弃了,事先绞尽脑汁想了想怎么个说法,然后给HR打了个电话,表明了意思。但过了一两天,我导师突然给我电话了,实在是纳闷,我导师就没电话找过我啥事的,原来是和协航电通过我专业的一个主任找到我导师,要他向我转达下,如果我再考虑他们公司,公司可以对给我的待遇再加点。……激动和不安…… 最终过两天后我还是礼貌地打了个电话过去,再次回拒了他。

和协航电是个很不错的公司,也许是我与它有缘无分吧。

 

东方口岸

 

这公司似乎是主研发类公司中的另类,一句话,就是公司从不缺钱——有海关做靠山。工作地点处于繁华段——也是东方什么的(和某人的德勤邻座啊,呵呵,),工作环境很好,美女众多,工作压力不大,稳定,福利好,就是给的money很一般,和数码视讯差不多,比和协航电略少点。

正是因为这样,在我跟二面官面谈时,也许是我笔试成绩很好,二面官没有问及我任何技术问题,只是介绍了智能卡的前景,不断的给我分析这个公司的利和弊,问了问我自己的想法,最后也给我留了电话,说有何问题可以找他。

整个过程和数码有着类似之处,但是,几天后,我收到了东方口岸的拒信,我很疑惑,决定不放弃,再试一试,就斟酌了下回了封“拒 拒信”(其实名字不是这个了),想知道下被拒的原因。第二天,那个二面我的人就给我电话了,向我说明了下原因——“在和我谈话中感觉我的要求很高,公司给的很一般,而在未来两三年里不会有多少上浮”,其实它的待遇也不差了,而且自己也蛮喜欢这单位的,便又和他blabla说了通——“人的志向是要高点的,其实我对公司也是很感兴趣,在选择任何一个或几个公司时,选择前的犹豫不决也是很正常的,一旦下了决定就不会再考虑任何东西了”。也许他是真的对我印象不错,说再让我考虑几天,再给他电话,同时人事处发录用通知。

……

……

以上三个offer我都了解的很细致、很全面,从总体上说都差不多(以及下面那个户口问题都不大),真的难以取舍。经过自己长时间的权衡、同学和家人的意见后,决定准备和东方口岸签了,接着给二面官回了电话说准备加入公司,给HR问了详细的签约流程,定了体检时间,约了个签三方的时间。但是在去体检的前一天,一切又改变了。

……

……

最终我只是给HR电话,说我不去体检了,没脸再跟那个超级无敌nice的二面官解释了,唉 。。。。。。

 

芯晟

 

如果芯晟的招聘进度能再快那么一点的话,也许一切都简化了不少。

1211号去的一面,感觉还不错,而且也感觉这公司的待遇也不错(一切都是钱在作怪啊),于是期待着它的二面、三面。。。。于是把前面那些offer们一个劲的拖,能拖多久拖多久,同时自己也是不停的往芯晟那电话催着,唉,没办法,找工作嘛,自己要多主动点了,结果芯晟的前台和HR都和我很熟了,嘿嘿(没什么动机,我也是无奈啊)。而且我也了解到下轮面试只有5个人左右(也许其他人都忍不住签了别的吧),稍感安慰。

迫于我的压力,公司终于在15号下午给我安排了面试,由于技术总监要立马飞美国,他也和我谈了十多分钟而已,除此我还被面了两次,其中的一个人和那总监我感觉发挥的还不错,但是面我的最后一个人让我很没底,也许是他太厉害了,一下就把我看穿了,让我觉得一无是处了(我觉得也就是),让我对这个offer加上了不确定性。

正式因为这个不确定性,以及HR说的最早给录取结果的时间,让我不能同时统筹考虑OFFERs的选择,所以才(提前)选择了东方口岸。

……

……

但是,就在我准备去东方口岸体检的前一天,芯晟给我发offer了,第二天去谈谈待遇问题。

……

……

一个成立只有两年多的小外企,待遇(相对)很诱人,各方面虽然不能全部令自己满意,但到这个时候也只能知足了。

考虑不多,该拒的拒吧,该签的也麻利些了。。。。。。

 

插曲

 

海格,顺利通过笔试(笔试过这么多,唯一做得很爽的)、一面,但被告知只有广州职位,虽然方向很对口,待遇福利也还可以,但还是没去二面。

后来由于“广东电信”的缘故,我又托人找海格招聘负责人电话,说我又可以考虑广州了,再后来由于“广东电信郊区”的消息,在被海格通知去二面的电话中再次“出尔反尔”。

结尾

 

我承认,我有罪,我霸着这么多个offer,不道德,没人品。但我也要说,在没有一个能一锤定音的offer时,一切都是很正常的事,人之常情。对此,在我每拒一次时,我都会极力推荐周围的人给公司,不管这有没有效果。。。

 

芯晟要走了我的第一次工作经历,这条路不知道走得对不对,我拭目以待。。。

【作者: bumingwu】【访问统计:518】【2007年01月17日 星期三 15:56】【注册】【打印


- 评论人:Michael   2008-11-14 00:11:20   

现在如何?好想知道近况。


- 评论人:atv   2008-10-25 15:43:09   

您好,能告诉我你的联系方式不?我想向你具体了解一下北京和协航电科技有限公司。


- 评论人:wo   2008-09-21 21:16:32   

我也是被芯晟逼走的,那鸟公司,就知道剥削国人,那些鸟海龟还打着什么“中国自主”进行诈骗,奉劝各位敬而远之吧。。。。。。


- 评论人:sunlit   2008-08-07 19:39:37   

深喉,芯晟真的这样了么?
有所耳闻,看来是真的?


- 评论人:深喉   2008-05-31 09:52:42   

孙滨,没想到是你的博客,好久不见了哈。我不说我是谁了,反正我已经离开公司了。
1.现在全公司降薪20%-40%,而且是没有提前通知的情况下,就把5月份的工资降了。
2.签订三方协议的毕业生,公司现在说人不要了,但是因为给解决了户口,要学生给公司5万元“赔偿”。
3.内部用人原则是“来了就望死了用,没用马上开掉,一点个人发展的机会都没有”。
4.公司不给交各项保险。
5.离职的工程师,要求赔偿,公司最后以“打白条”的方式,进行工资结算。
希望大家开清楚这个公司,不要再进来被骗了。


- 评论人:456   2007-03-16 19:54:54   

怎么烂啊,说来听听


- 评论人:123   2007-03-07 12:46:14   

芯晟是家很烂的公司,我就是这里出来的


- 评论人:123   2007-03-07 12:44:51   

这是一家很烂的公司,真的,我就是那里出来的


- 评论人:123   2007-03-07 12:44:36   

这是一家很烂的公司,真的,我就是那里出来的


- 评论人:peng   2007-01-27 01:48:50   

改做ASIC design还是比较有前途,好好加油吧,一切顺利!


- 评论人:le   2007-01-22 20:57:54   

好赞的面经呀!成长的也够快的,以后发展潜力无可度量……


- 评论人:小林   2007-01-19 19:04:01   

恭喜恭喜啊,这篇文章对我们这些明年找第一次的很有作用啊,到时还得你们来再指导:)


- 评论人:fairyzhou   2007-01-19 18:14:40   

不管怎么说,祝贺你。怎么没说薪金如何?第一笔工资要敲诈的。又一个有钱青年诞生了


- 评论人:fairyzhou   2007-01-19 18:12:49   

汗! 第一次找工作,面试两次,第二次搞定
第二次找工作,笔试三次,面试两次,全搞定。
估计是我不挑,关键是我米有本钱挑阿


- 评论人:joey   2007-01-19 13:37:56   

offer男啊,做IC不错


- 评论人:bumingwu   2007-01-18 23:22:26   

IC,就在你旁边,量子芯座


- 评论人:yueliangbuku   2007-01-18 09:45:34   

芯晟做什么的?


- 评论人:wuv   2007-01-17 19:31:47   

1月5日

剖析几种主流嵌入式软件代码压缩技术

关键字:嵌入式 软件代码 技术

  对于嵌入式软件而言,代码尺寸是越小越好。压缩代码以适应受到成本或空间限制的存储子系统已经成为嵌入式系统开发的一项重要事务。ARMMIPS、IBM以及ARC都提供了降低存储器占用的技术,本文将对这几种架构中代码压缩技术的实现进行比较分析。

  如今,存储子系统的成本高于微处理器已不再稀奇。因此,选择一款能节约存储成本的处理器就变得很有意义。编写紧凑的代码只是事情的一个方面,而处理器的指令集对存储器的消耗同样影响很大。对于代码密度很差的处理器而言,无论怎样绞尽脑汁地去压缩你的C源代码都于事无补。如果你关注存储器的消耗,选择恰当的处理器并精心调整代码是明智的。

  并不是所有的处理器都拥有或需要代码压缩,只有32位的RISC(精简指令集计算机)处理器需要代码压缩,因为RISC处理器代码密度较差。RISC处理器在过去是设计用于通用计算机和工作站,在其设计时认为存储器价格便宜。尽管存储器价格可能便宜,但如果能占用更少的存储器不是更便宜吗?对于蜂窝电话以及其他成本控制严格的嵌入式系统应用而言,在RAM或ROM上5美元的成本差异,就能导致量产时利润的巨大差别。通常,存储器的大小是固定的,而产品的功能特性却各异。紧凑的目标代码意味着可以实现更多的自动拨号、更好的语音识别能力,或者可能是更清晰的屏幕显示。

  在32位嵌入式处理器中,ARM、MIPS以及PowerPC曾是首先寻找出降低其存储器消耗、提高代码密度方法的几种处理器。更早一些的处理器,如摩托罗拉的68k系列以及英特尔的x86系列,并不需要代码压缩。事实上,其标准代码密度都比RISC处理器的代码压缩模式还要高。

  易于使用的Thumb技术

  我们先从ARM的代码压缩方案(Thumb)讲起,因为其使用广泛、有很好的支持,是一个典型处理器代码压缩方案,并相当简洁、有效。

  Thumb实际上是添加到ARM的标准RISC指令集之上的独立指令集。在你的代码中,你可以通过一条模式切换指令在这两种指令集之间进行切换。Thumb指令集架构(Instruction Set Architecture, ISA)是由大约36条16位指令组成,仅靠这些指令是完成不了太多任务的,但Thumb指令集包括了基本的加法、减法、循环移位以及跳转指令。通过使用这些较短的指令替换ARM标准的32位指令,可以将某些代码的规模减小大约20%到30%。但有一些问题需要引起注意:

  首先,Thumb代码和标准ARM代码不能混杂使用,必须显式地在两种模式间进行切换,就好像Thumb是一套完全不同的指令集(实际上也是)。这迫使程序员将所有的16位代码与32位代码分开并隔离到独立的模块中。

  其次,由于Thumb是经过简化和精简的指令集架构,在Thumb模式中无法完成所有你希望的工作。Thumb模式无法进行诸如处理中断、长跳 转、原子存储器(atomic memory)操作,或协处理器操作等。Thumb有限的指令意味着仅对基本的算术和逻辑操作有用,其他的任何工作必须使用ARM的标准32位指令集来完 成。

  Thumb的限制不仅表现在指令集上,当处于Thumb模式中,ARM处理器将仅有8个寄存器(而不是16个),这些寄存器无法像标准模式下ARM代码那样进行条件执行和移位或循环移位操作。在标准ARM代码和Thumb代码间进行参数传递并不困难,只要将参数放到堆栈中或通过处理器的前8个寄存器就可以了。

  从标准模式到Thumb模式之间的来回切换也要消耗时间,而且还要增加代码。此外,还需要几十个前导(preamble)以及后同步指令 (postamble)来组织指针并清空CPU的流水线。如果在Thumb模式中运行的代码小于几十条指令,就不值得为之付出这样的开销。

  最后,Thumb还对于性能有着少许的影响。通常,使用Thumb指令对代码进行压缩会导致代码运行速度降低大约15%,这主要是由于在16位 模式和32位模式间切换所引起的。Thumb指令还不如32位的标准指令灵活,因此,和32位代码相比,常常需要更多的指令来完成同样的工作。从积极的一 方面来说,由于其指令长度只有32位指令集的一半,Thumb使得缓存的使用效率更高。

  如果任务能够在这些限制下完成,Thumb可以节约不少成本。Thumb技术已经得到每一款ARM处理器的支持,无论用户使用与否,多数ARM编译器以及汇编程序都支持Thumb指令集。因此,采用Thumb的体验应该相当轻松。

  MIPS处理器

  理解了Thumb技术后,MIPS16e就没什么新奇的了。一些MIPS处理器中增加了另外的16位指令集,与ARM系统非常似。 MIPS16e指令集包括了一组16位的标准MIPS算法、逻辑以及跳转指令的简化版本。其使用和Thumb一样,也需要在标准模式和MIPS16e模式 之间来回切换,这也将导致付出时间和增加代码的开销。除非能在“压缩”模式上运行相当长时间,否则没有必要进行模式切换。其代码压缩效率和ARM差不多, 对于多数程序而言,也是20%到30%。

  MIPS16e和Thumb都不能对代码进行真正的压缩,它们只是对部分指令提供了可替换的操作码,而且得到的压缩比是依赖于短操作码和长操作码的总长度的比值。也就是说,依赖于代码所完成的任务,诸如操作系统和中断处理例程等系统级代码根本就不能使用16位指令,因此不能获得代码压缩效果。一般的算法,只要不使用任何大操作数,就能得到很好的压缩效率。最后,别忘了数据是无法进行压缩的,只有代码能够被压缩。如果你应用代码中包括了大量的静态数据结构,所能得到的总存储器节约是非常小的。还有,15%的性能损失也许很不值得。另一方面,MIPS16e和Thumb都是免费的(假定你的处理器已经包含了它们),选用它们的成本非常低。

  PowerPC的CodePack技术

  值得提前说明的是,IBM的CodePack方法是各种代码压缩技术中最复杂的。与Thumb和MIPS16e不同,CodePack系统是真正对运行代码进行压缩,就好像在PowerPC软件中运行WinZip。CodePack会分析并压缩整个程序,生成的用户代码必须在运行中解压缩并执行压缩版本。尽管很复杂,CodePack和其它技术一样提供20%到30%的空间节省。

  CodePack是一项很有吸引力的技术。在使用该技术时,只须和平常一样使用标准工具编译嵌入式PowerPC代码就行,CodePack甚至对已有的代码也能使用(无论有没有源代码)。在将代码写入ROM或装入磁盘之 前,运行CodePack压缩工具对代码进行压缩。压缩工具会分析代码指令的分布并生成一对专门针对这个程序代码的键值。当运行压缩后的代码时,拥有 CodePack功能的处理器使用这一对键值来在运行中解开压缩的代码,就好像直接运行压缩后的代码。解压缩会对处理器的流水线产生很小的延迟,但是其影 响被取指延迟以及其它延迟所掩盖。对于绝大多数应用,CodePack带来的性能影响是可以忽略的。

  但是,CodePack还有一些其它的影响。由于每一个压缩的程序都有其单独的压缩键值,CodePack本质上既是压缩系统也是加密系统。没有键值,无论你自己还是其它任何人都无法运行相应的程序。如果丢失了或者未获得相应的键值,压缩后的程序只是一堆无用的乱码,这也意味着压缩后的PowerPC程序不是二进制代码兼容的。除非同时包括其解压缩键值,否则无法轻易地和其它系统交换压缩后的程序。这会使嵌入式系统软件的现场分配稍微有些复杂。

  另外,CodePack为每个程序生成两个键值是因为指令的高16位和低16位是分别进行压缩的。IBM的工程师发现每一条PowerPC指令 的高半字(操作码就在其中)和低半字(其内容通常为操作数、偏移量或掩码)的分布频度是不一样的。对它们分别使用不同的压缩算法会使压缩效果比仅使用任何 单一算法要好,这就是CodePack对程序所做的事。

  ARCompact

  ARC International公司又采用了另外的代码压缩方法。因为ARCtangent处理器有用户可定义的指令集,ARC(及其用户)可以对指令集进行随心所欲的修改。作为ARCompact,ARC公司决定加入一组16位指令来改进其处理器的代码密度。

1月3日

openocd--开源芯片调试工具


openocd---开源芯片调试工具
2008-07-17 13:46:19  作者:asmcos  来源:  浏览次数:549  网友评论0  文字大小:【】【】【】 评分等级:0

Openocd, the Open On-Chip Debugger has been created by Dominic Rath as part of a diploma thesis at the University of Applied Sciences, FH-Augsburg. For other material presented on this site, see the respective notes of authorship.

All information and software available on this website is provided AS IS, without any warranty, either express or implied, including, but not limited to, the implied warranties of merchantability, fitness for a particular purpose or non-infringement, other than those warranties which are imposed by and incapable of exclusion, restriction or modification under the laws applicable to this agreement.

Contact information:

Dominic Rath
Dominic.Rath gmx.de

OpenOCD 是完全免费的片上调式软件(On-chip-debugger),类似Debug Board。

它为目标程序的挂起和恢复,寄存器,内存的读写等提供一个易读的telnet接口,

同时,还在tcp端口提供一个远程gdb调试接口。

最初在Debug Board设计出来之前,GTA01开发组就是使用OpenOCD结合wiggler作为他们的调试工具的。

获得 OpenOCD 的方法

MokoMakefile方式

通过MokoMakefile 构建openmoko开发工具"make openmoko-devel-tools" 将会生成openocd和dfu-util的二进制文件。构建时需要使用 "bitbake openocd-native" 命令。

Debian包方式

我们现在可以从http://people.openmoko.org/laforge/dpkg 获得debian包。安装openocd,执行

dpkg --install openocd_82-1_i386.deb

您还可以在Debian/Unstable找到OpenOCD的deb包。


Gentoo Ebuild

gentoo bugzilla有给Openocd使用的实验性ebuild。

原始码

OpenOCD Revision 82 及之后的版本已经证实可以在QT2410Neo1973 装置及wiggler及Amontect JTAGkey , JTAGkey tiny上良好的运作。你可以透过下面的方法在OpenOCD子版本外查看rev. 130 。

svn co -r 130 http://svn.berlios.de/svnroot/repos/openocd/trunk

Windows Binaries

YAGARTO 项目针对OpenOCD提供Win32安装。你可以在这个网址http://www.yagarto.de /howto/openocd/index.html查看到详情及下载。YAGAPTO接着会与从这个地址http: //people.openmoko.org/laforge/misc/debug_board_v2/windows_drivers/ 下载的FDTI一起运作。 drivers available from

设定

User:HaraldWelte 提供了 openocd.cfg 设定档 做为利用OpenOCD及QT2410做为wiggler之用。

使用OpenOCD

我们无法提供OpenOCD的完整使用手册,但你可以参考Bootloader#Using_JTAG_to_boot_from_RAMNAND bad blocks#JTAG_.2F_OpenOCD_.2F_u-boot_RAM_basedNeo1973 OpenOCD#Using OpenOCD telnet interface这些内容。

已知的臭虫及问题排除

CP15 register 读取/写入 ARM920T core 无法作用

这个为已知的臭虫,但除错工作目前为停滞状态。

你启动它了吗?

最容易犯及最具破坏力的错误是忘记确实的启动CPU。只把电源接上是不够的! 按住电源按钮,直到 boot loader开始进行倒数,或者,在这个情况下,你可以让cpu保持忙碌。

已知的在JTAG chainn下无法与装置设定切合的装置

You get something like:

Error:   jtag.c:1224 jtag_examine_chain(): number of discovered devices in JTAG chain (51) doesn't match configuration (1)

这看起来是libftdi启动的臭虫,有时,在FT2232下也无法取得在启动OpenOCD时完全的重新定定。请重新自Amontec JTAGkey / Debug Board 上拔下USB装置,再重新插回,就可以很快的重新启动及使用。

JTAG 通讯错误

其它常见的错误如下:

JTAG communication failure, check connection, JTAG interface, target power etc.

同时,它也可以藉由插、拔USB来修复。

OpenOCD及除错版

libftdi-0.8

如果你想要使用OpenOCD搭配Debug Board v2一起使用,并且想在OpenOCD下同时使用串行埠,你 需使用libftdi-0.8 或之后的版本。你的套件仍是使用之前的libftdi版本的机率很高,因此,你可以自下面的地址 http://www.intra2net.com/de/produkte/opensource/ftdi/ 下载,并且自行建置。

openocd.cfg

openocd.cfg设定档是可以与 Debug Board (v2/v3)、Neo1973 Hardware#GTA01Bv3及OpenOCD 130合用的版本:

telnet_port 4444
gdb_port 3333
interface ft2232
jtag_speed 0
ft2232_vid_pid 0x1457 0x5118
ft2232_layout "jtagkey"
reset_config trst_and_srst
jtag_device 4 0x1 0xf 0xe
daemon_startup attach
target arm920t little reset_run 0 arm920t
working_area 0 0x200000 0x4000 backup
run_and_halt_time 0 5000

对于OpenOCD之后的版本,你可能必须指定装置描述。针对v2 Debug board ,你可以在你的openocd.cfg中加入这一行:

ft2232_device_desc "Debug Board for Neo1973 A"

如果你使用Linux的libftdi 版本,并且你在lsusb output "Debug Board for Neo1973 看到iProduct string ,请加入以下的内容,而非上述的设定行:

ft2232_device_desc "Debug Board for Neo1973"

使用OpenOCD telnet 界面

telnet界面可以使用以下的指令存取

telnet localhost 4444

它提供很多除错的功能。你可能会对下列的内容感到兴趣。

reset
reset halt
resume
poll
reg
load_binary
mdw
mww

使用OpenOCD与gdb进行远程侦错

首先,你需要一个适合的跨gdb(在你的主架构上执行的gdb架构,如i386,但在arm上是与程序一起运作)。在OpenEmbedded中,你可以使用下面的指令建立cross-gdb:

bitbake  gdb-cross

如果你想要对kernel进行除错工作, you can then start this gdb with

$ arm-linux-gdb vmlinux

它会出现以下的内容:

GNU gdb 6.6
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "--host=i686-linux --target=arm-linux"...
(gdb)

我们现在需要连接到OpenOCD daemon,你可以使用 target remote指令。在这个特定的案例中,我们已经使用在telnet指令列中使用halt

(gdb) target remote localhost:3333
Remote debugging using localhost:3333
warning: shared library handler failed to enable breakpoint
0xc0129b8c in memmove () at include/asm/current.h:9
9 {
(gdb)

对于这项工作的断点,你可以使用以下的指令,启用OpenOCD中的软件断点。

> arm7_9 sw_bkpts enable

telnet 指令列,并使用 hbreak 在gdb中设定断点。


本文引用地址:http://www.akae.cn/phpcms/article/2008/0717/article_11.html

arm-elf-gcc工具链建立

http://openhardware.net/
这里给出了建立arm-elf-gcc工具链的方法
1月2日

图解autoscan、aclocal、autoheader、automake、autoconf、configure、make

1.autoscan (autoconf): 扫描源代码以搜寻普通的可移植性问题,比如检查编译器,库,头文件等,生成文件configure.scan,它是configure.ac的一个雏形。

    your source files --> [autoscan*] --> [configure.scan] --> configure.ac

2.aclocal (automake):根据已经安装的宏,用户定义宏和acinclude.m4文件中的宏将configure.ac文件所需要的宏集中定义到文件 aclocal.m4中。aclocal是一个perl 脚本程序,它的定义是:“aclocal - create aclocal.m4 by scanning configure.ac”

user input files   optional input     process          output files
================ ============== ======= ============

acinclude.m4 - - - - -.
V
.-------,
configure.ac ------------------------>|aclocal|
{user macro files} ->| |------> aclocal.m4
`-------'
3.autoheader(autoconf): 根据configure.ac中的某些宏,比如cpp宏定义,运行m4,声称config.h.in

user input files optional input process output files
================ ============== ======= ============

aclocal.m4 - - - - - - - .
|
V
.----------,
configure.ac ----------------------->|autoheader|----> autoconfig.h.in
`----------'

4.automake: automake将Makefile.am中定义的结构建立Makefile.in,然后configure脚本将生成的Makefile.in文件转换 为Makefile。如果在configure.ac中定义了一些特殊的宏,比如AC_PROG_LIBTOOL,它会调用libtoolize,否则它 会自己产生config.guess和config.sub

user input files   optional input   processes          output files
================ ============== ========= ============

.--------,
| | - - -> COPYING
| | - - -> INSTALL
| |------> install-sh
| |------> missing
|automake|------> mkinstalldirs
configure.ac ----------------------->| |
Makefile.am ----------------------->| |------> Makefile.in
| |------> stamp-h.in
.---+ | - - -> config.guess
| | | - - -> config.sub
| `------+-'
| | - - - -> config.guess
|libtoolize| - - - -> config.sub
| |--------> ltmain.sh
| |--------> ltconfig
`----------'

5.autoconf:将configure.ac中的宏展开,生成configure脚本。这个过程可能要用到aclocal.m4中定义的宏。

user input files   optional input   processes          output files
================ ============== ========= ============

aclocal.m4 ,autoconfig.h.in - - - - - - -.
V
.--------,
configure.ac ----------------------->|autoconf|------> configure
 
6. ./configure的过程

.-------------> [config.cache]
configure* --------------------------+-------------> config.log
|
[config.h.in] -. v .--> [autoconfig.h]
+-------> config.status* -+
Makefile.in ---' `--> Makefile
 
7. make过程
 
[autoconfig.h] -.
+--> make* ---> 程序
Makefile ---'

Linux下Makefile的automake生成全攻略

一、Makefile介绍
Makefile是用于自动编译和链接的,一个工程有很多文件组成,每一个文件的改变都会导致工程的重新链接,但是不是所有的文件都需要重新编译,Makefile中纪录有文件的信息,在make时会决定在链接的时候需要重新编译哪些文件。

Makefile的宗旨就是:让编译器知道要编译一个文件需要依赖其他的哪些文件。当那些依赖文件有了改变,编译器会自动的发现最终的生成文件已经过时,而重新编译相应的模块。

Makefile的基本结构不是很复杂,但当一个程序开发人员开始写Makefile时,经常会怀疑自 己写的是否符合惯例,而且自己写的Makefile经常和自己的开发环境相关联,当系统环境变量或路径发生了变化后,Makefile可能还要跟着修改。 这样就造成了手工书写Makefile的诸多问题,automake恰好能很好地帮助我们解决这些问题。

使用automake,程序开发人员只需要写一些简单的含有预定义宏的文件,由autoconf根据一 个宏文件生成configure,由automake根据另一个宏文件生成Makefile.in,再使用configure依据Makefile.in 来生成一个符合惯例的Makefile。下面我们将详细介绍Makefile的automake生成方法。

二、使用的环境

本文所提到的程序是基于Linux发行版本:Fedora Core release 1,它包含了我们要用到的autoconf,automake。

三、从helloworld入手

我们从大家最常使用的例子程序helloworld开始。
下面的过程如果简单地说来就是:

新建三个文件:
helloworld.c
configure.in
Makefile.am
然后执行:
aclocal; autoconf; automake --add-missing; ./configure; make; ./helloworld
就可以看到Makefile被产生出来,而且可以将helloworld.c编译通过。
很简单吧,几条命令就可以做出一个符合惯例的Makefile,感觉如何呀。

现在开始介绍详细的过程:

1、建目录
在你的工作目录下建一个helloworld目录,我们用它来存放helloworld程序及相关文件,如
在/home/my/build下:
$ mkdir helloword
$ cd helloworld

2、 helloworld.c
然后用你自己最喜欢的编辑器写一个hellowrold.c文件,如命令:vi helloworld.c。使用下面的代码作为helloworld.c的内容。
int main(int argc, char** argv)
{
        printf("Hello, Linux World!\n");
        return 0;
}
完成后保存退出。
好了,现在在helloworld目录下就应该有一个你自己写的helloworld.c了。

3、生成configure
我们使用autoscan命令来帮助我们根据目录下的源代码生成一个configure.in的模板文件。
命令:
$ autoscan
$ ls
configure.scan  helloworld.c
执行后在hellowrold目录下会生成一个文件:configure.scan,我们可以拿它作为configure.in的蓝本。
现在将configure.scan改名为configure.in,并且编辑它,按下面的内容修改,去掉无关的语句:
============================configure.in内容开始=========================================
#                                               -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.

AC_INIT(helloworld.c)
AM_INIT_AUTOMAKE(helloworld, 1.0)

# Checks for programs.
AC_PROG_CC

# Checks for libraries.

# Checks for header files.

# Checks for typedefs, structures, and compiler characteristics.

# Checks for library functions.
AC_OUTPUT(Makefile)
================configure.in内容结束===========
然后执行命令aclocal和autoconf,分别会产生aclocal.m4及configure两个文件:
$ aclocal
$ls
aclocal.m4  configure.in  helloworld.c 
$ autoconf
$ ls
aclocal.m4  autom4te.cache  configure  configure.in  helloworld.c

大家可以看到configure.in内容是一些宏定义,这些宏经autoconf处理后会变成检查系统特性、环境变量、软件必须的参数的shell脚本。

autoconf 是用来生成自动配置软件源代码脚本(configure)的工具。configure脚本能独立于autoconf运行,且在运行的过程中,不需要用户的干预。

要生成configure文件,你必须告诉autoconf如何找到你所用的宏。方式是使用aclocal程序来生成你的aclocal.m4。

aclocal根据configure.in文件的内容,自动生成aclocal.m4文件。aclocal是一个perl 脚本程序,它的定义是:“aclocal - create aclocal.m4 by scanning configure.ac”。

autoconf从configure.in这个列举编译软件时所需要各种参数的模板文件中创建configure。
autoconf需要GNU m4宏处理器来处理aclocal.m4,生成configure脚本。

m4是一个宏处理器。将输入拷贝到输出,同时将宏展开。宏可以是内嵌的,也可以是用户定义的。除了可以展开宏,m4还有一些内建的函数,用来引用文件,执行命令,整数运算,文本操作,循环等。m4既可以作为编译器的前端,也可以单独作为一个宏处理器。

4、新建Makefile.am
新建Makefile.am文件,命令:
$ vi Makefile.am
内容如下:
AUTOMAKE_OPTIONS=foreign
bin_PROGRAMS=helloworld
helloworld_SOURCES=helloworld.c

automake会根据你写的Makefile.am来自动生成Makefile.in。
Makefile.am中定义的宏和目标,会指导automake生成指定的代码。例如,宏bin_PROGRAMS将导致编译和连接的目标被生成。

5、运行automake
命令:
$ automake --add-missing
configure.in: installing `./install-sh'
configure.in: installing `./mkinstalldirs'
configure.in: installing `./missing'
Makefile.am: installing `./depcomp'

automake会根据Makefile.am文件产生一些文件,包含最重要的Makefile.in。

6、执行configure生成Makefile
$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking for gcc... gcc
checking for C compiler default output... a.out
checking whether the C compiler works... yes
checking whether we are cross compiling... no
checking for suffix of executables...
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for gcc option to accept ANSI C... none needed
checking for style of include used by make... GNU
checking dependency style of gcc... gcc3
configure: creating ./config.status
config.status: creating Makefile
config.status: executing depfiles commands
$ ls -l Makefile
-rw-rw-r--    1 yutao    yutao       15035 Oct 15 10:40 Makefile
你可以看到,此时Makefile已经产生出来了。

7、使用Makefile编译代码
$ make
if gcc -DPACKAGE_NAME=\"\" -DPACKAGE_TARNAME=\"\" -DPACKAGE_VERSION=\"\" -

DPACKAGE_STRING=\"\" -DPACKAGE_BUGREPORT=\"\" -DPACKAGE=\"helloworld\" -DVERSION=\"1.0\" 

-I. -I.     -g -O2 -MT helloworld.o -MD -MP -MF ".deps/helloworld.Tpo" \
  -c -o helloworld.o `test -f 'helloworld.c' || echo './'`helloworld.c; \
then mv -f ".deps/helloworld.Tpo" ".deps/helloworld.Po"; \
else rm -f ".deps/helloworld.Tpo"; exit 1; \
fi
gcc  -g -O2   -o helloworld  helloworld.o 

运行helloworld
$ ./helloworld
Hello, Linux World!

这样helloworld就编译出来了,你如果按上面的步骤来做的话,应该也会很容易地编译出正确的helloworld文件。

你还可以试着使用一些其他的make命令,如make clean,make install,make dist,看看它们会给你什么样的效果。

感觉如何?自己也能写出这么专业的Makefile,老板一定会对你刮目相看。

四、深入浅出

针对上面提到的各个命令,我们再做些详细的介绍。

1、 autoscan

autoscan是用来扫描源代码目录生成configure.scan文件的。autoscan可以 用目录名做为参数,但如果你不使用参数的话,那么autoscan将认为使用的是当前目录。autoscan将扫描你所指定目录中的源文件,并创建 configure.scan文件。

2、 configure.scan

configure.scan包含了系统配置的基本选项,里面都是一些宏定义。我们需要将它改名为configure.in

3、 aclocal

aclocal是一个perl 脚本程序。aclocal根据configure.in文件的内容,自动生成aclocal.m4文件。aclocal的定义是:“aclocal - create aclocal.m4 by scanning configure.ac”。

4、 autoconf

autoconf是用来产生configure文件的。configure是一个脚本,它能设置源程序来适应各种不同的平台,并且根据不同的系统来产生合适的Makefile,从而可以使你的源代码能在不同的操作系统平台上被编译出来。操作系统

configure.in文件的内容是一些宏,这些宏经过autoconf 处理后会变成检查系统特性、环境变量、软件必须的参数的shell脚本。configure.in文件中的宏的顺序并没有规定,但是你必须在所有宏的最前 面和最后面分别加上AC_INIT宏和AC_OUTPUT宏。

在configure.ini中:

#号表示注释,这个宏后面的内容将被忽略。
AC_INIT(FILE)
这个宏用来检查源代码所在的路径。
AM_INIT_AUTOMAKE(PACKAGE, VERSION)
这 个宏是必须的,它描述了我们将要生成的软件包的名字及其版本号:PACKAGE是软件包的名字,VERSION是版本号。当你使用make dist命令时,它会给你生成一个类似helloworld-1.0.tar.gz的软件发行包,其中就有对应的软件包的名字和版本号。
AC_PROG_CC
这个宏将检查系统所用的C编译器。
AC_OUTPUT(FILE)
这个宏是我们要输出的Makefile的名字。

我们在使用automake时,实际上还需要用到其他的一些宏,但我们可以用aclocal 来帮我们自动产生。执行aclocal后我们会得到aclocal.m4文件。

产生了configure.in和aclocal.m4 两个宏文件后,我们就可以使用autoconf来产生configure文件了。

5、 Makefile.am

Makefile.am是用来生成Makefile.in的,需要你手工书写。Makefile.am中定义了一些内容:
AUTOMAKE_OPTIONS

这个是automake的选项。在执行automake时,它会检查目录下是否存在标准GNU软件包中应具备的各种文件,例如AUTHORS、ChangeLog、NEWS等文件。我们将其设置成foreign时,automake会改用一般软件包的标准来检查。

bin_PROGRAMS

这个是指定我们所要产生的可执行文件的文件名。如果你要产生多个可执行文件,那么在各个名字间用空格隔开。

helloworld_SOURCES

这个是指定产生“helloworld”时所需要的源代码。如果它用到了多个源文件,那么请使用空格符 号将它们隔开。比如需要helloworld.h,helloworld.c那么请写成helloworld_SOURCES= helloworld.h helloworld.c。

如果你在bin_PROGRAMS定义了多个可执行文件,则对应每个可执行文件都要定义相对的filename_SOURCES。

6、 automake

我们使用automake --add-missing来产生Makefile.in。
选项--add-missing的定义是“add missing standard files to package”,它会让automake加入一个标准的软件包所必须的一些文件。
我们用automake产生出来的Makefile.in文件是符合GNU Makefile惯例的,接下来我们只要执行configure这个shell 脚本就可以产生合适的 Makefile 文件了。

7、 Makefile

在符合GNU Makefiel惯例的Makefile中,包含了一些基本的预先定义的操作:
make
根据Makefile编译源代码,连接,生成目标文件,可执行文件。
make clean
清除上次的make命令所产生的object文件(后缀为“.o”的文件)及可执行文件。
make install
将编译成功的可执行文件安装到系统目录中,一般为/usr/local/bin目录。
make dist
产生发布软件包文件(即distribution package)。这个命令将会将可执行文件及相关文件打包成一个tar.gz压缩的文件用来作为发布软件的软件包。
它会在当前目录下生成一个名字类似“PACKAGE-VERSION.tar.gz”的文件。PACKAGE和VERSION,是我们在configure.in中定义的AM_INIT_AUTOMAKE(PACKAGE, VERSION)。
make distcheck
生成发布软件包并对其进行测试检查,以确定发布包的正确性。这个操作将自动把压缩包文件解开,然后执行configure命令,并且执行make,来确认编译不出现错误,最后提示你软件包已经准备好,可以发布了。
===============================================
helloworld-1.0.tar.gz is ready for distribution
===============================================
make distclean
类似make clean,但同时也将configure生成的文件全部删除掉,包括Makefile。

五、结束语

通过上面的介绍,你应该可以很容易地生成一个你自己的符合GNU惯例的Makefile文件及对应的项目文件。 如果你想写出更复杂的且符合惯例的Makefile,你可以参考一些开放代码的项目中的configure.in和Makefile.am文件,比如:嵌入式sqlite,单元测试cppunit。