C与指针 7-9章

来源:互联网 发布:淘宝分店怎么开 编辑:程序博客网 时间:2024/06/04 21:07

第7章 函数

7.1 函数定义

函数体就是一个代码块,它在函数被调用时执行

return语句允许你从函数体的任何位置返回

 

子程序不论是否存在返回值,均被称为函数。调用一个真函数(即返回一个值的函数)但不在任何表达式中使用这个返回值是完全可能的。在这种情况下,返回值被丢弃。但是从表达式内部调用一个过程类型的函数(无返回值)是一个严重的错误。

 

7.2函数原型

在函数原型中加入描述性的参数名是明智的,因为它可以给希望调用该函数的客户提供有用的信息

没有参数的函数原型应该写成:

int *func(void);

关键字void提示没有任何参数

当函数调用一个无法见到原型的函数时,编译器便认为该函数返回一个整型值

 

7.5 递归

堆栈非常适合递归

递归函数调用将涉及一些运行时开销-参数必须压到堆栈中,为局部变量分配内存空间,寄存器的值必须保存

 

7.7 总结

对于没有原型的函数,传递给函数的实参将进行缺省参数提升:charshort类型转换成int型;float型的实参转换成double

 

如果递归函数内部所执行的最后一条语句是调用自身,那它就被称为尾部递归。尾部递归很容易改写为循环的形式,它的效率通常更高一些

抽象数据类型可以减少程序对模块实现细节的依赖,提高程序的可靠性

当递归定义清晰的优点可以补偿它的效率开销时,就可以使用这个工具

 

第8章 数组

8.1 一维数组

只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量

取一个数组名的地址产生的是一个指向数组的指针;

除了优先级以外,下标引用和间接访问完全相同。例如下面两个表达式完全相同

array[3];

*(array +3);

 

int array[10];

int *ap=array+2;

*aparray[0]相同

ap+6,与它相当的是array+8&array[8]

ap[-1]就是array[1];

ap[9]这个表达式是非法的,但少有编译器检测到这类错误

 

下标引用可以作用于任意的指针,而不仅仅是数组

 

警告:2[array]array[2]是相等的,但绝不应该编写2[array],因为它会大大影响程序的可读性

 

8.1.3指针与下标

假定这两种方法都是正确的,下标绝不会比指针更有效率,但指针有时会比下标更有效率

根据某个固定数目的增量在一个数组中移动时,使用指针比使用下标产生效率更高的代码

提示:为了一点点效率,付出的代价是:难于编写和维护。如果程序无法运行或编写,它的执行速度再快也无济于事。

指针比下标更有速率的场合—当你在数组中一次一步(或某个固定的数字)地移动时,与固定数字相乘的运算在编译时完成,所以在运行时所需的指令就少一些。

 

8.1.4 指针的效率

指针有时比下标更有效率,前提是它们被正确地使用

编写代码的方法不仅影响程序运行时的效率,而且影响它的可读性。不要为了效率上的细微差别而牺牲可读性,这点非常重要。

提示:效率不是唯一的因素,通常代码的简洁性更为重要

8.1.8 初始化

静态与初始化

存储于静态内存的数组只初始化一次,也就是在程序开始执行之前

如果数组未被初始化,数组元素的初始值将会自动设置为0

 

8.1.9 不完整的初始化

int vector[5]={1,2,3,4};

编译器只允许省略最后几个初始值

 

8.1.11字符数组的初始化

char message[]=”hello”;

尽管看上去像字符串常量,实际上并不是。

当用于初始化一个字符数组时,它就是一个初始化列表。在其他任何地方,都表示一个字符串常量

char *message=”hello”;

前者初始化一个字符数组的元素,而后者则是一个真正的字符串常量

 

8.2 多维数组

指向数组的指针

例:int matrix[3][10],*mp=matrix;

mp的初始化是不正确的,因为matrix不是一个指向整型的指针,而是一个指向整型数组的指针

int (*p)[10]=matrix;

p是一个指向整型数组的指针

 

警告:

应该避免这种声明:

int (*p)[]=matrix;

p仍是一个指向整型数组的指针,但数组的长度却不见了。当某个整数与这种类型的指针进行指针运算时,它的值将根据空数组的值进行调整(也就是说,与0相乘)

 

8.2.5作为函数参数的多维数组

int matrix[3][10];

....

func(matrix);

func2的原型可以使用下面的任意一种:

void func2(int (*mat)[10]);

void func2(int mat[][10]);

 

编译器必须知道第二个以及以后各维的长度,才能对各下标进行求值,因此在原型中必须声明这些维的长度,第1维的长度并不需要,因为在计算下标值时用不到

func2写成下面这样是不对的:

void func2(int **mat);

这个例子把mat声明成指向指针的指针,和指向整型数组的指针不是一回事

 

8.2.6 初始化

Int two_dim[3][5]={{0,1},{,4,5},{7,6}};

加上花括号有利于显示数组的结构。对于不完整的初始化列表,花括号就相当有用

 

8.3 指针数组

字符串的列表可以以矩阵的形式储存,也可以以指向字符串常量的指针数组形式存储

在矩阵中,每行必须与最长字符串的长度一样长,但它不需要任何指针

指针数组本身要占用空间,但每个指针所指向的字符串所占的内存空间就是字符串本身的长度

 

8.4 总结

指针和数组并不相等,数组的属性和指针的属性大相径庭。

静态变量(包括数组)在程序载入到内存时得到初始值,自动变量(包括数组)每次当执行流进入它们声明所在的代码块时都要使用隐形的赋值语句重新进行初始化

 

只要有可能,函数的指针形参都应该声明为const

在多维数组的初始值列表中,使用完整的多层花括号能提高可读性

在有些环境中,使用register关键字提高程序运行时效率

 

9章 字符串,字符和字节

字符串以字符串常量的形式出现或者存储于字符数组中

9.1 字符串基础

字符串就是一串零个或多个字符,并且以一个位模式为全零的NUL字节结尾

警告:

strlen返回一个size_t,它是一个无符号整型。在表达式中使用无符号数可能导致意想不到的结果

 

9.3 不受限制的字符串函数

复制字符串

char *strcpy(char *dest,char const* src);

目标参数以前的内容将被覆盖,即使新的字符串比原来的内存更短,由于新字符串是以NUL结尾,所以老字符串最后剩余的几个字符也会被有效地删除

警告:程序员必须保证目标字符数组的空间足以容纳需要复制的字符串,如果字符串比较长,多余的字符仍被复制,它们将覆盖原先存储于数组后面的内存空间的值

连接字符串 strcat

和前面一样,必须保证目标字符数组剩余的空间足以保存整个源字符串

 

9.4 长度受限的字符串函数

strncpy正好向dest中写入len个字符,如果strlen(src)的值小于lendest数组就用额外的NUL字节填充到 len长度。如果strlen(src)的值大于等于len,那么只有len个字符被复制到dst中,注意!它的结果将不会以NUL结尾

 

在使用不受限制的函数之前,首先必须确定字符串实际上是以NUL字节结尾的

strncat也是长度受限的函数,它从src处最多复制len个字符到目标数组的后面,但是,strncat总是在结尾字符串后面添加一个NUL字节,而且它不会像strncpy那样对目标数组用NUL填充,它才不管目标参数除去原先存在的字符串之后留下的空间够不够

 

9.5字符串查找基础

9.5.1 查找一个字符

使用strchrstrrchr函数

strchr查找一个字符串中某个字符第一次出现的位置

strrchr查找一个字符串中某个字符最后一次出现的位置

9.5.2 查找任何几个字符

strpbrk函数在一个字符串中查找一个指定字符集中任意字符第一次出现的位置

9.5.3查找一个子串

使用strstr函数

 

9.7错误信息

操作系统是通过设置一个外部的整型变量errno进行错误代码报告的

 

9.8 字符操作

标准库包含两组函数,用于操作单独的字符,它们的原型位于<ctype.h>

第一组函数用于对字符分类。第二组函数用于转换字符

toupper函数把一个小写字母字符转换成大写,tolower则执行相反的任务

 

9.9内存操作

memxxx提供了类似字符串函数的能力,但它们可以处理包括NUL字节在内的所有字节,这些函数都接受一个长度参数

 

9.12编程提示的总结

不要试图自己编写功能相同的函数

使用字符分类和转换函数可以提高函数移植性

原创粉丝点击