《c和指针》之总结 五

9、字符串、字符和字节

         字符串就是零个或多个字符的序列,该序列以一个NUL 字节结尾。字符串的长度就是它所包含的字符的数目。标准库提供了一些函数用于处理字符串,它们的原型位于头文件string.h中。

strlen函数用于计算一个字符串的长度,它的返回值是一个无符号整数,所以把它用于表达式时应该小心。strcpy函数把一个字符从一个位置复制到另一个位置,而strcat函数把一个字符串的一份拷贝添加到另一个字符串的后面。这两个函数都假定它们的参数是有效的字符串,而且如果源字符串和目标字符串出现重叠,函数的结果是未定义的。strcmp对两个字符串进行词典序的比较。它的返回值提示第1个字符串是大于、小于还是等于第2个字符串。

长度受限的函数 strncpy、strncat、strncmp都类似它们对应的不受限制版本。区别在于这些函数还接受一个长度参数。在strncpy中,长度指定了多少个字符将被写入到目标字符数组中。如果源字符串比指定长度更长,结果字符串将不会以NUL 字节结尾。strncat函数的长度参数指定从源字符串复制过来的字符的最大数目,但它的结果始终以一个NUL 字节结尾。strcmp函数的长度参数用于限定字符比较的数目。如果两个字符串在指定的数目里不存在区别,它们便被认为是相等的。

用于查找字符串的函数有好几个。strchr函数查找一个字符串中某个字符第1个次出现的位置。strrchr 函数查找一个字符串中某个字符最后一次出现的位置。strpbrk 在一个字符串中查找一个指定字符集中任意字符第1次出现的位置。strstr函数在一个字符串中查找另一个字符串第1次出现的位置。

标准库还是提供了一些更加高级的字符串查找函数。strspn 函数计算一个字符串的起始部分匹配一个指定字符集中任意字符的数量。strtok 函数把一个字符串分割成几个标记。每次当它调用时,都返回一个指向字符串中下一个标记位置的指针。这些标记由一个指定字符集的一个或多个字符分隔。

strerror把一个错误代码作为他的参数。返回一个指向字符串的指针,该字符串用于描述这个错误。

         标准库还提供了各种用于测试和转换字符的函数。使用这些函数的程序比那些自己执行字符测试和转换的程序更具移值性。toupper 函数把一个小写字母字符转换为大写形式,tolower 函数则执行相反的任务。iscntrl 函数检查它的参数是不是一个控制字符,isspace函数测试它的参数是否为空白字符。isdigit 函数用于测试它的参数是否为一个十进制数字符,isxdigit函数则检查它的参数是否为一个十六进制数字字符。islower和isupper函数分别检查他们的参数是否为大写和小写字母。isalpha 函数检查它的参数是否为字母字符,isalnum 函数检查它的参数是否为字母或数字字符,ispunct 函数检查它的参数是否为标点符号字符。最后,isgraph 函数检查它的参数是否为图形字符,isprint 函数检查它的参数是否为图形字符或空白字符。

         memxxx函数提供了类似字符串函数的能力,但他们可以处理包括NUL 字节在内的任意字节。这些函数都接受一个长度参数。memcpy 从源参数向目标参数复制由长度参数指定的字节数。memmove 函数执行相同的功能,但它能够正确处理源参数和目标参数出现重叠的情况。memcmp 函数比较两个序列的字节,memchr 函数在一个字节序列中查找一个特定的值。最后,memset函数把一序列的字节初始化为一个特定的值。

         应该使用有符号数的表达式中使用strlen 函数。使用strncpy函数产生不以NUL 字节结尾的字符串。

 

10、结构和联合

          在结构(struct)中,不同类型的值可以存储在一起。结构中的值称为成员,他们是通过名字访问的。结构变量是一个标量,可以出现在普通标量中可以出现的任何场合。

         结构的声明列出了结构包含的成员列表。不同的结构声明即使他们的成员列表相同也被认为是不同类型的。结构标签是一个名字,它与一个成员列表相关联。你可以使用结构标签在不同的声明中创建相同类型的结构变量,这样就不用每次在声明中重复成员列表。typedef 也可以用于实现这个目标。

         结构的成员可以使标量、数组或指针。结构也可以包含本身也是结构的成员。在不同的结构中出现同样的成员是不会引起冲突的。你使用点操作符访问结构变量的成员。如果你拥有一个指向结构的指针,你可以使用箭头操作符访问这个结构的成员。

         结构不能包含类型也是这个结构的成员,但它的成员可以是一个指向这个结构的指针。这个技巧常常用于链式数据结构中。为了声明两个结构,每个结构都包含一个指向对方的指针的成员,我们需要使用不完整的声明来定义一个结构标签名。结构变量可以用一个由花括号包围的值列表进行初始化。这些值的类型必须适合它所初始化的那些成员。

         编译器为一个结构变量的成员分配内存时要满足它们的边界对齐要求。在实现结构存储的边界对齐时,可能会浪费一部分内存空间。根据边界对齐要求降序排列结构成员可以最大限度地减少结构存储中浪费的内存空间。sizeof 返回的值包含了结构中浪费的内存空间。

         结构可以作为参数传递给函数,也可以作为返回值从函数返回。但是,向函数传递的一个指向结构的指针往往效率更高。在结构指针参数的声明中可以加上const 关键字防止函数修改指针所指向的结构。

         位段是结构的一种,但它的成员长度以位为单位指定。位段声明在本质上是不可移植的,因为它涉及许多与实现有关的因素。但是,位段允许你把长度为奇数的值包装在一起以节省存储空间。源代码如果需要访问一个值内部任意的一些位,使用位段比较简便。

         一个联合的所有成员都存储于同一个内存位置。通过访问不同类型的联合成员,内存中相同的位组合可以解释为不用的东西。联合在实现变体记录时很有用,但程序员必须负责确认实际存储的是哪个变体并选择正确的联合成语以便访问数据。联合变量也可以进行初始化,但初始化值必须与联合第1个成员的类型匹配,而且它必须位于一对花括号里面。在联合中存储指向不同成员的指针而不是直接存储成员本身,可以解决内存浪费问题。

         想函数传递结构参数是低效的,应该用指向结构的指针传递参数。把结构体标签声明和结构的typedef 声明放在头文件中,当源文件需要这些声明时可以通过#include 指令把他们包含进来。把位段成员显示声明为signed int 或unsigned int 类型。

位段是不可移植的。

11、动态内存分配

         当数组被声明时,必须在编译时知道他的长度。动态内存分配允许程序为一个长度在运行时才知道的数组分配内存空间。

         malloc 和calloc 函数用于动态分配一块内存,并返回一个指向该快内存的指针。malloc的参数就是需要分配的内存的字节数。和他不同的是,calloc 的参数是你需要分配的元素个数和每个元素的长度。calloc 函数在返回前把内存初始化为0,而malloc 函数返回时内存并未以任何方式进行初始化。调用realloc 函数可以改变一块已经动态分配的内存的大小。增加内存块大小时有可能采取的方法是把原来内存块上的所有数据复制到一个新的、更大的内存块上。当一个动态分配的内存块不再使用时,应该调用free 函数把它归还给可用内存池。内存被释放之后便不能再被访问。

         如果请求的内存分配失败,malloc、calloc和realloc 函数返回的将是一个NULL 指针。错误地访问分配内存之外的区域所引起的后果类似越界访问一个数组,但这个错误还可能破坏可用内存池,导致程序失败。如果一个指针不是从早先的malloc、calloc 或realloc 函数返回的,它是不能作为参数传递个free 函数的。你也不能只释放一块内存的一部分。

         内存泄露是指内存被动态分配以后,当它不再使用时未释放。内存泄露会增加程序的体积,有可能导致程序或系统的崩溃。

         使用sizeof 计算数据类型的长度,提高程序的可移植性。

 

12、使用结构和指针

         单链表是一种是用指针来存储值的数据结构。链表中的每个节点包含一个字段,用于指向链表的下一个节点。另外有一个独立的根指针指向链表的第1个节点。由于节点在创建时是采用动态分配内存的方式,所以他们可能分布于内存之中。但是,遍历链表是根据指针进行的,所以节点的物理排列无关紧要。单链表只能以一个方向进行遍历。

         为了把一个新值插入到一个有序的单链表中,你首先必须找到链表中合适的插入位置。对于无序单链表,新值可以插入到任何位置。把一个新节点链表中需要两个步骤。首先,新节点的link 字段(结构中指向下一个节点的指针)必须设置为指向它的目标后续节点。其次,前一个节点的link 字段必须设置为指向这个新节点。在许多其他语言中,插入函数保存一个指向一个节点的指针来完成第2个步骤。但是,这个技巧使插入到链表的起始位置成为一种特殊情况,需要单独处理。在c语言中,你可以通过保存一个指向必须进行修改的link 字段的指针,而不是保存一个指向前一个节点的指针,从而消除了这个特殊情况。

         双链表中的每个节点包含两个link 字段:其中一个指向链表的下一个节点,另一个指向链表的前一个节点。双链表有两个根指针,分别指向第1个节点和最后一个节点。因此,遍历双链表可以从任何一端开始,而且在遍历过程中可以改变方向。为了把一个新节点插入到双链表中,我们必须修改4个指针。新节点的前向和后向 link 字段必须被设置,前一个节点的后向link 字段和一个节点的前向link 字段也必须进行修改,使他们指向这个新节点。

         语句提炼是一种简化程序的技巧,其方法是消除程序中冗余的语句。如果一个if 语句的“then”和“else”子句以相同序列的语句结尾,它们可以被一份单独的出现if 语句之后的拷贝代替。相同序列的语句也可以从if 语句的起始位置提炼出来,但这种提炼不能改变if 的测试结构。如果不同的语句事实上执行相同的功能,你可以把他们写成相同的样子,然后再使用语句提炼简化程序。

        

posted @ 2010-05-23 16:23  stardream  阅读(398)  评论(0)    收藏  举报