收藏 分享(赏)

C程序设计语言(第二版).doc

上传人:精品资料 文档编号:10641670 上传时间:2019-12-13 格式:DOC 页数:11 大小:39.84KB
下载 相关 举报
C程序设计语言(第二版).doc_第1页
第1页 / 共11页
C程序设计语言(第二版).doc_第2页
第2页 / 共11页
C程序设计语言(第二版).doc_第3页
第3页 / 共11页
C程序设计语言(第二版).doc_第4页
第4页 / 共11页
C程序设计语言(第二版).doc_第5页
第5页 / 共11页
点击查看更多>>
资源描述

1、第一章 基本概念第二章 类型、运算符与表达式一个对象的类型决定着该对象可取值的集合以及可以对该对象施行的运算。2.2 数据类型与大小1. 在 C 语言中只有如下几个基本数据类型:char 单字节,可以存放字符集中一个字符。int 整数,一般反映了宿主机上整数的自然大小。Float 单精度浮点数。Double 双精度浮点数。此外,还有一些可用于限定这些基本类型的限定符。2.3 常量1诸如 1234 一类的整数常量是 int 常量。Long 常量要以字母 l 或 L 结尾。无符号常量以字母 u 或 U 结尾,后缀 ul 或 UL 用于表示 unsigned long 常量。 (这里的常量其实就是指

2、直接指定的一般数字或是字符字符串什么的)浮点常量必须包含一个小数点或指数(如 1e-1)或两者都包含,在没有后缀时类型为double。后缀 f 与 F 用于指定 float 常量,而后缀 l 或 L 则用于指定字符常量是一个整数,写成用单引号括住单个字符的形式,如x 。字符常量的值是该字符在机器字符集中的数值。常量表达式时其中只涉及到常量的表达式。这种表达式可以在编译时计算而不必推迟到运算时,因而可以用在常量可以出现的任何位置,例如由 define 定义的宏。字符串常量也叫字符串字面值,是用双引号括住的由 0 个或多个字符组成的字符序列。从技术绝度看,字符串常量就是字符数组。在内部表示字符串时

3、要用一个空字符0来结尾,故用于存储字符串的物理存储单元数比括在双引号中的字符数多一个。这种表示发意味着,C 语言对字符串的长度没有限制,但是程序必须扫描完整个字符串才能决定这个字符串的长度。枚举常量。枚举是常量整数值的列表。不同的枚举中的名字必须各不相同,同一枚举中各个名字的值不要求不同。枚举是使常量值与名字相关联的又一种方便的方法,其相对于#define 语句的优势是常量值可以由自己控制。2.4 说明其实就是声明。如果所涉及的变量不是自动变量(就是局部变量) ,那么只初始化一次,而且从概念上讲应该在程序开始执行之前进行,此时要求初始化符必须为常量表达式。显示初始化的自动变量每当进入其所在的函

4、数或分程序时就进行一次初始化,其初始化符可以是任何表达式。外部变量与静态变量的缺省值为 0。未经显式初始化的自动变量的值为未定义值(即垃圾) 。2.5 算术运算符2.6 关系运算符与逻辑运算符2.7 类型转换1. 当一个运算符的几个运算分量的类型不同时,要根据一些规则把它们转换成某个共同的类型。一般而言,只能把“比较窄的”运算分量自动转换成“比较宽的”运算分量,这样才能不丢失信息。2. char 类型就是小整数类型,在算术表达式中可以自由地使用 char 类型的变量或常量。这就使得在某些字符转换中有了很大的灵活性。但是在将字符转换成整数时有一点微妙。C 语言没有指定 char 类型变量时无符号

5、还是有符号量。当把一个 char 类型的值转换成 int类型的值时,其结果是不是负整数?结果视机器的不同而有所变化,反映了不同机器结构之间的区别。在某些机器上,如果字符的最左一位为 1,那么就被转换成负整数。在另一些机器上,采取的是提升的方法,通过在最左边加上 0 把字符提升为整数,这样的转换结果总是正的。C 语言的定义保证了机器的标准可打印字符集中的字符不会是负的,故在表达式中这些字符总是正的。但是,字符变量存储的位模式在某些机器上可能是负的,而在另一些机器上却是正的。为了保证程序的可移植性,如果要在 char 变量中存储非字符数据,那么最好指定 signed 或 unsigned 限定符。

6、3. 当表达式中包含 unsigned 类型的运算分量时,转换规则要复杂一些。主要问题是,在有符号与无符号值之间的比较运算取决于机器,因为它们取决于各个整数类型的大小。例如,假定 int 对象占 16 位, long 对象占 32 位,那么,-1L1UL,这是因为-1L 被提升为 unsinged long 类型,因此它是一个比较大的正数。4. 在进行赋值时要进行类型转换,=右边的值要转换成左边变量的类型,后者即赋值表达式结果的类型。不管是否要进行符号扩展,字符值都要转换成整数值。当把较长的整型数转换成较短的整型数或字符时,要把超出的高位部分截掉。5. 由于函数调用的变元是表达式,当把变元传递

7、给函数时也可能引起类型转换。在没有函数原型的情况下,char 与 short 类型转换为 int 类型, float 转换为 double 型,这就是即使在函数使用 char 与 float 类型的变元表达式调用时仍把参数说明成 int 与 float 的原因。2.8 加一与减一运算符2.9 按位运算符2.10 赋值运算符与赋值表达式2.11 条件表达式2.12 运算符优先级与表达式求值次序同一行的各个运算符具有相同的优先级,纵向看越往下优先级越低。运算符 结合律() - 从左至右! + - + - * /*定义一个数组*/char *pmessage = “now is the time”;

8、 /*定义一个指针*/上述说明中,amessage 是一个不可改变的常量,它总指向同一片存储区。另一方面,pmessage 是一个指针,其初值指向一个字符串常量,之后它可以被修改指向其他地址,但是如果试图修改字符串的内容,结果将不确定。5.6 指针数组与指向指针的指针1. 例如定义 char *line20,那么请注意,这时首先根据运算符优先级规则,line 是一个数组,又由于有* 的修饰,所以他是一个指针数组,即数组里面存储的全部是指针。5.7 多维数组1.数组在内存中按行存储。如果要将二维数组作为变元传递给函数,那么函数的参数说明中应该指明相应数组的列数,数组的行数不必指定。5.9 指针与

9、多维数组注意int a1020;int *b10;这两个的区别,对于 b 来说,每一维的长度可以不一致。5.10 命令行变元5.11 指向函数的指针1.在 C 语言中,函数本身不是变量,但可以定义指向函数的指针,这种指针可以被赋值、存放于数组中、传递给函数及作为函数返回值等等。2.定义指向函数的指针:return_type (*fun_name) (参数列表);调用则为(*fun_name ) (实参);5.12 复杂说明1. 见定义:char *argv; argv:指向字符指针的指针int (*daytab) 13; daytab:指向由 13 个整形类型元素组成的一维数组的指针(这就是数

10、组指针,指向数组的指针。就是一指针) 。(这里这么用,int nArray3 = 1, 2, 3;int (*pArray)3 = )int *daytab13; daytab:由 13 个指向整数类型对象的指针组成的一维数组(存储的是 13 个指针) 。void *comp(); comp:返回值为指向通用类型的指针的函数。void (*comp)(); comp:指向返回值为通用类型的函数的指针。char (*(*X()(); X:返回值为指向一维数组的指针的函数,该一维数组由指向返回字符类型的函数指针组成。char (*(*X3)() 5; X:由 3 个指向函数的指针组成的一维数组,该

11、函数返回指向由5 个字符组成的一维数组的指针。对于这种复杂的声明,可以采用基于说明符的方式进行解读:说明符:可选的* 序列 直接说明符直接说明符: 名字(说明符)直接说明符()直接说明符可选的大小简而言之,说明符即前面也许带有符号*的直接说明符。直接说明符可以是名字、由一对圆括号括住的说明符、后面跟有一对圆括号的直接说明符或后面跟有由方括号括住可选大小的直接说明符。例如:(*pfa)(),pfa 首先是一个直接说明符,于是 pfa也是一个直接说明符。接着*pfa被识别出是一个说明符,因而(*pfa) 是一个直接说明符。于是,按照此规则,对于 char (*(*X3)() 5作分析:X 是一个直

12、接说明符,那么*X3是一个说明符,这就定义了一个指针数组,类型待定。接着(*X3)是一个直接说明符,那么(*X3)()也是,它表示这是一个函数指针数组,接着*(*X3)()表示这个函数返回的是一个指针,(*(*X3)() 表示这是一个直接说明符, char (*(*X3)() 5到这里表示这个函数返回由 5个字符组成的一维数组的指针。2. 对于这种复杂的定义,有著名的右左法则:首先从最里面的圆括号看起,然后往右看,在往左看。每当遇到圆括号时,就应该掉转阅读方向。一旦解析完圆括号里面所有的东西,就跳出圆括号。重复这个过程直到整个声明解析完毕。这里,应该对这个法则作一个小小的修正,应该从未定义的标

13、识符开始阅读,即不是C 语言的关键字开始。仍旧对上例说明:首先 X 是个未定义的标识符,往右看是个,说明这是个数组,再往右,遇到括号则往左,有*说明这是个指针数组。在往左,遇到括号,此时括号里面的东西解析完毕。接着括号外面往由,直接遇到括号,说明这是个函数,即指针数组里的指针式函数指针。跳转往左,遇到*说明函数返回值为指针,跳转往右,括号则整个跳出,再往右, ,说明这是一个指向数组的指针。第六章 结构6.2 结构与函数1. 对结构的合法操作只有拷贝、作为一个单元对其赋值、通过文件 2:extern int *mango;这种情况是不行的。虽说对数组的引用总是可以写成对指针的引用,而且确实存在一

14、种指针和数组的定义完全相同的上下文环境。但是并不是总是这样。4.3 什么是声明,什么是定义1.C 语言中的对象必须有且只有一个定义,但他可以有多个 extern 声明。定义是一种特殊的声明,它创建了一个对象;声明简单的说明了在其他地方创建的对象的名字,它允许你使用这个名字。extern 对象声明告诉编译器对象的类型和名字,对象的内存分配则在别处进行。2.C 语言引入了“可修改的左值”这个术语。它表示左值允许出现在赋值语句的左边。这个奇怪的术语是为与数组名区分,数组名也用于确定对象在内存中的位置,也是左值,但它不能作为赋值的对象。因此,数组名是个左值但不是可修改的左值。标准规定赋值符必须用可修改

15、的左值作为他左侧的操作数。通俗的说,只能给可以修改的东西赋值。3.出现在赋值符左边的符号有时被称为左值,出现在赋值符右边的符号有时则被称为右值。编译器为每个变量分配一个地址(左值) 。这个地址在编译时可知,而且该变量在运行时一直保存于这个地址。相反,存储于变量中的值(它的右值) (对于这个地方解释,例如 x=y,这时 y 便称为“它的右值 ”)只有在运行时才可知。如果需要用到变量中存储的值,编译器就发出指令从指定地址读入变量值并将它存于寄存器中。4.这里强调一点,对于所有的变量,在编译时都会分配一个地址,而且在编译时可知。那么我们引用这个变量,就是相当于直接对应了这个地址。对于一般的变量,我们

16、引用定义的名字就引用了编译时分配的对应地址里的内容;对于指针变量,由于变量对应的是一个表示地址的地址,所以我们要解引用。请特别注意理解这里!5.extern char a与 extern char a100等价的原因:这两个声明都提示 a 是一个数组,也就是一个内存地址,数组内的字符可以从这个地址找到。编译器并不需要知道数组总共有多长,因为它只产生偏离起始地址的偏移地址。6.当你“定义为指针,但以数组方式引用”时会发生什么:其实,无论对于你定义为指针却以数组的方式引用,还是定义为数字却以指针的方式引用,都会出现错误(作为函数参数可以) 。因为当另外去声明这个变量的时候,接下来就会以你声明的这种

17、方式去引用这个变量。但是二者引用的原理却不一样:定义为指针声明为数组,引用的时候会直接根据这个地址去算偏移量,而不是先解引用,所以会出错。定义为数组声明为指针则会先去解引用,这样对一个不是地址的变量去解引用必然会出错。4.5 数组和指针的其他区别1.指针和数组都可以在它们的定义中用字符串常量进行初始化。尽管看上去一样,底层的机理却不相同。定义指针时,编译器并不为指针所指向的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化。2.在 ANSI C 中,初始化指针时所创建的字符串常量被定义为只读。与指针相反,由字符串常量初始化的数组是可以修改的。第五章 对链接

18、的思考5.1 函数库、链接和载入1. 链接器基础知识:编译器创建一个输出文件,这个文件包含了可重定位的对象。这些对象就是与源程序对应的数据和机器指令。绝大多数编译器并不是一个单一的庞大程序。他们通常由多大六七个稍小的程序所组成,这些程序由一个叫做“编译器驱动器”的控制程序来调用。这些可以方便地从编译器中分离出来的单独程序包括:预处理器、语法和语义检查器、代码生成器、汇编程序、优化器、链接器,还包括一个调用所有这些程序并向各个程序传递正确选项的驱动器程序。2. Gcc 的编译流程:(1 )预处理阶段:将必要的文件包含进来 gcc -E hello.c -o hello.i (2 )编译阶段:这个

19、对应的是语法和语义检查器、代码生成器。在这个阶段中,Gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,Gcc 把代码翻译成汇编语言 gcc -s hello.i -o hello.s(3)汇编阶段:汇编阶段是把编译阶段生成的”.s”文件转成目标文件,读者在此可使用选项”-c”就可看到汇编代码已转化为”.o”的二进制目标代码了 gcc -c hello.s -o hello.o(4 )链接阶段:3. 目标文件(这里只编译进行到汇编(把汇编语言代码翻译成目标机器指令的过程)之后的文件,等链接完后就成了可执行文件)不能直接执行,它首先需要载入到链接器中。链接器

20、确认 main 函数为初始进入点,把符号引用绑定到内存地址,把所有的目标文件集中在一起,在加上库文件,从而产生可执行文件。4. 如果函数库(这个函数库是指已经编译好的二进制文件)的一份拷贝是可执行文件的物理组成部分,那么我们称之为静态链接;如果可执行文件只是包含了文件名,让载入器在运行时能够寻找程序所需要的函数库,那么我们称之为动态链接。收集模块准备执行的三个阶段的规范名称是链接-编辑、载入和运行时链接。静态链接的模块被链接编辑并载入以便运行。动态链接的模块被链接编辑后载入,并在运行时进行链接以便运行。程序执行时,在 main()函数被调用前,运行时载入器把共享的数据对象载入到进程的地址空间。

21、外部函数被真正调用之前,运行时载入器并不解析它们。所以即使链接了函数库,如果并没有实际调用,也不会带来额外开销。5.2 动态链接的优点1. 可执行文件的体积非常小。虽然运行速度稍微慢一些,但动态链接能够更加有效的利用磁盘空间,而且链接-编辑阶段的时间也会缩短(因为链接器的有些共组被推迟到载入时) 。2. 尽管单个可执行文件的启动速度稍受影响,但动态链接可以从两个方面提高性能:(1 )动态链接可执行文件比功能相同的静态链接可执行文件的体积小。它能够节省磁盘空间和虚拟内存,因为函数库只有在需要时才被映射到进程中。以前,避免把函数库的拷贝绑定到每个可执行文件的唯一方法就是把服务至于内核中而不是函数库

22、中,这就带来了可怕的“内核膨胀”问题。(2 )所有动态链接到某个特定函数库的可执行文件在运行时共享改函数库的一个单独拷贝。操作系统内核保证映射到内存中的函数库可以被所有使用他们的进程共享。这就提供了更好的 I/O 和交换空间利用率,节省了物理内存,从而提高了系统的整体性能。如果可执行文件时静态链接的,每个文件都拥有一份函数库的拷贝,显然极其浪费。3. 编译时地址问题:在编译阶段,所有非局部变量地址已经确定。这些这里有与位置无关的代码和与位置有关的代码。与位置无关的代码表示这种代码保证对于任何全局数据的访问都是通过间接地方法完成的。而与位置无关的代码,其代码会被对应到固定的地址。对于这个问题,有

23、待接下来的研究,还有进程间的共享,以及与位置无关以及与位置有关的代码,这个位置指的是直接对应内存还是相对于程序本身,都有待研究。第六章 运动的诗章:运行时数据结构编程语言理论的经典对立之一就是代码和数据的区别。有些把二者视为一体。C 语言通常维持二者的区别。代码和数据的区别也可以认为是编译时和运行时的分界线。编译器的绝大部分工作都跟翻译代码有关,必要的数据存储管理的绝大部分都在运行时进行。6.2 段1. 在 UNIX 中,段表示一个二进制文件相关的内容块。在 Inter x86 的内存模型中,段表示一种设计的结果。在这种设计中(基于兼容性的原因) ,地址空间并非一个整体,而是分成一些 64K

24、大小的区域,称之为段。2. 对于 unix 中的可执行文件,是以段形式组织的。一般有文本段,数据段和 bss 段。3. 段可以方便地映射到链接器在运行时可以直接载入的对象中。载入器只是去文件中每个段的映像,并直接将他们放入内存中。从本质上说,段在正在执行的程序中是一块内存区域,每个区域都有特定的目的。4. 每个段的作用:(1 )文本段包含程序的指令。链接器把指令直接从文件拷贝到内存中,以后便再也不用管它。(2 )数据段包含经过初始化的全局和静态变量以及它们的值。(3 ) bss 段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段之后。当这个内存区进入程序的地址空间后全部清

25、零。包括数据段和 bss 段的整个区段此时通常称为数据区。这是因为在操作系统的内存管理术语中,段就是一片连续的虚拟地址,所以相邻的段被结合。6.4 C 语言运行时系统在 a.out 里干了什么运行时数据结构有好几种:堆栈、活动记录、数据、堆等。1. 堆栈段主要有三个用途:(1 ) 堆栈为函数内部声明的局部变量提供存储空间。(2 ) 进行函数调用时,堆栈存储与此有关的一些维护性信息。(3 ) 堆栈也可以被用作暂时存储区。有时候程序需要一些临时存储,比如计算一个很长的算术表达式时,它可以把部分计算结果压到堆栈中,当需要时再把它从堆栈中取出。除了递归调用之外,堆栈并非必须。因为在编译时可以知道局部变

26、量、参数和返回地址所需空间的固定大小,并可以将它们分配于 BSS 段。6.5 当函数被调用时发生了什么;过程活动记录1. C 语言自动提供的服务之一就是跟踪调用链哪些函数调用了哪些函数,当下一个“return”语句执行后,控制将返回何处等。解决这个问题的经典机制是堆栈中的过程活动记录。当每个函数被调用时,都会产生一个过程活动记录(或类似的结构) 。过程活动记录是一种数据结构,用于支持过程调用,并记录调用结束以后返回调用点所需要的全部信息。 (如下图):2. 绝 大多数的现代算法语言允许函数和数据一样在 函数内部定义。C 语言不允许以这种语法进行 函数的嵌套。C 语言中的所有函数在词法层次 中都

27、是位于最顶层。局部变量(local varibales)参数(arguments)静态链接(stack link)指向先前结构的指针返回地址(return address)在允许嵌套过程的语言中,活动记录一般要包含一个指向它的外层函数的活动记录的指针。这个指针被称为静态链接,它允许内层过程访问外层过程的活动记录,因此也可以访问外层过程的局部数据。记住在同一时刻一个外层过程可能有好几个处于活动状态的调用。内层过程活动记录的静态链接将指向合适的活动记录,允许访问局部数据的正确实例。这种类型的访问(一个指向词法上外层范围的数据项的引用)被称作上层引用。静态链接(指向从词法上讲属于外层过程的活动记录,

28、由编译时决定)之所以如此命名是因为它与动态链接相对照,后者是一个活动记录指针链(在运行时指向最靠近自己的前一个过程调用的活动记录) 。6.6 auto 和 static 关键字1. 对于在函数内部定义的自动变量,函数调用结束就被回收。如果要返回指针,此时指针不是有 malloc 或静态变量,那么当函数调用结束时,返回了改变量地址的副本,但是该地址(此时为副本代表的地址)所指向的内存块实际上已经被回收。所以当你再去调用这个返回的指针时,其指向的内容其实是未知的。这个指针也叫悬垂指针。2. 存储类型说明符 auto 关键字在实际中从来用不着。它通常由编译器设计者使用,用于标记符号表的条目它表示“在

29、进入该块后,自动分配存储” 。3. 过程活动记录并不一定要存在于堆栈中。事实上,尽可能地把过程活动记录的内容放到寄存器中会使函数调用速度更快。SPARC 架构引入了一个概念,称为“寄存器窗口” ,CPU 拥有一组寄存器,它们只用于保存过程活动记录中的参数。每当函数调用时,空的活动记录依然到堆栈中。当函数调用链非常深而寄存器窗口不够用时,寄存器的内容就会被保存到堆栈中保留的活动记录空间中,以便重新利用这些寄存器。第七章 对内存的思考7.2 Inter 80x86 内存模型以及它的工作原理1. 在 Inter 80x86 内存模型中,段是内存模型设计的结果,在 80x86 的内存模型中,个各处理器

30、的地址空间并不一致(因为要保持兼容性) ,但他们都被分割成以 64KB 为单位的区域,每个这样的区域便称为段。2. 作为 80x86 内存模型最基本的形式,8086 中的段是一块 64KB 的内存区域,由一个段寄存器所指向。内存地址的形成过程是:取得段寄存器的值,左移 4 位,然后加上 16 位的偏移地址(表示段内的地址) ,就是最终的地址。3. 8086 有 20 位地址总线,总共是 1MB 的内存。但是只有 640KB 可供应用程序使用。因为其他的要预留给系统使用。7.3 虚拟内存1. 虚拟内存:为了去除安装在机器上的物理内存数量的限制,提出虚拟内存这个概念。基本思路是用廉价但缓慢的磁盘来

31、扩充快速却昂贵的内存。在任一给定时刻,程序实际需要使用的虚拟内存区段的内容就被载入物理内存中。当物理内存中的数据有一段时间未被使用,他们就可能被转移到硬盘中,节省下来的物理内存空间用于载入需要使用的其它数据。2. 以 SunOS 为例说明:SunOS 中的进程执行于 32 位地址空间。操作系统负责具体细节,使每个进程都以为自己拥有整个地址空间的独家访问权。这个幻觉是通过“虚拟内存”实现的。所有进程共享机器的物理内存,当内存用完时就用磁盘保存数据。在进程运行时,数据在磁盘和内存之间来回移动。内存管理硬件负责把虚拟地址翻译为物理地址,并让一个进程始终运行于系统的真正内存中。应用程序员只看到虚拟地址

32、,并不知道自己的进程在磁盘和内存之间来回切换。虚拟内存通过“页”的形式组织。页就是操作系统在磁盘和内存之间移来移去或进行保护的单位,一般为几 K 字节。从潜在的可能性上说,与进程有关的所有内存都将被系统所使用。如果该进程可能不会马上运行,操作系统可以暂时取回所有分配的给他的物理内存资源,将该进程的所有相关信息都备份到磁盘上。这样这个进程就被“换出” 。在磁盘中有一个特殊的“交换区” ,用于保存从内存中换出的进程。进程只能操作位于物理内存中的页面。当进程引用一个不在物理内存中的页面时,MMU(内存管理单元)就会产生一个页错误。内核对此事件做出响应,并判断该引用是否有效。如果无效,内核像进程发出一

33、个“段违规”的信号。如果有效,内核从磁盘取回该页,换入到内存中。一旦页面进入内存,进程便被解锁,可以重新运行进程本身并不知道它曾经因为页面换入事件等待了一会。7.4 Cache 存储器1. 位于 CPU 和内存之间,所有对内存的读取和写入操作都要经过 Cache。7.5 数据段和堆1. 就像堆栈段能够根据需要自动增长一样,数据段也包含了一个对象,用于完成这项工作,这就是堆。堆区域用于动态分配的存储。2. 被分配的内存总是经过对齐,以适合机器上最大尺寸的原子访问。第八章 第九章 再论数组9.1 什么时候数组与指针相同1. 表达式中的数组名(与声明不同)被编译器当作一个指向该数组第一个元素的指针。

34、对数组的引用如 ai在编译时总是被编译器改写成*(a+i)的形式。2. C 语言把数组下标作为指针的偏移量。C 语言把数组下标改写成指针偏移量的根本原因是指针和偏移量是底层硬件所使用的基本模型。3. “作为函数参数的数组名”等同于指针。在函数形参定义这个特殊情况下,编译器必须把数组形式改写成指向数组第一个元素的指针形式。9.3 为什么 C 语言把数组形参当作指针1. 这个主要是为了避免值传递时发生的大数据量的拷贝。2. 必须注意的是对于数组和指针,数组定义了之后是不能修改它的值的(可以修改它里面的元素,这个修改它的值指的是不能修改这个变量名所代表的数组,例如 int a; int b; a=b

35、;这样是不允许的) 。9.4 C 语言的多维数组第十章 再论指针10.4 向函数传递一个一维数组1. 当像一个函数传递一个二维数组作为参数时,传递给他的是指向该数组第一行的指针。2. C 语言中,对于函数参数的二维数组,一般是使用指针形式(因为要记录每一维的大小以防止越界) 。C 语言无法向函数传递一个普通的多维数组的。10.6 使用指针从函数返回一个数组1. 严格的说,无法直接从函数返回一个数组。但是,可以让函数返回一个指向任何数据结构的指针,当然也可以是指向数组的指针。这个地方比较麻烦,例子:int (*paf() 20; /paf 是一个函数,返回一个指向包含 20 个 int 元素的数组的指针定义可能为:int (*paf()20int (*pear)20;pear = calloc(20, sizeof(int);if(!pear) longjemp(error, 1);return pear;这种用法平时并不多见。需要注意的是千万不能从函数中返回一个指向函数的局部变量的指针。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报