《C语言程序设计》课程教学资源(讲义资料)C语言中详解指针

指针 1指针是什么 1.1指针是一类数据类型的统称 对于C语言来说,计算机的内存由连续的字节(byt)构成。这些连续的字节同样被连续地编 上了号码以相互区别,这个号码就是所谓的地址(Address),如图9-1所示。 02T68 022T8 002FT6 022FD 图9-1内存单元与地址 指针(Pointer)是C语言中的一类数据类型的统称。这种类型的数据专门用来存储和表示内存 单元的编号,以实现通过地址得以完成的各种运算。 这样看来指针似平就是地址,然而,事实上却并非如此。后面将会看到,地址只是指针内涵中的 一部分,甚至只是一小部分内容而远非其全部。片面地把地址理解为指针的全部,永远学不好指 为了使得语言具有广泛的适用性,C语言标准允许编译器自行选择指针类型数据的长度。在不同 的编译环境下,指针数据类型的长度可能不同:甚至相同的编译环境中不同的指针数据类型,也 可能有不同的大小。 为了叙述的方便,本书中的指针数据类型一律假设为具有32bt的长度。这样并不影响对指针 本质的描述,但涉及指针数据类型长度的代码(极少)在不同的编译环境中可能具有不同的结果 这点请读者加以注意。 C语言同样不规定地址这种内存单元的编号在内存中的存储格式,但在现实中目前这种编号多数 是与二进制的unsigned int数据类型的存储格式一样,这是本章的另一个假定。这意味着程序 可以访问的内存的大小最大为2的32次方(4GB)。但这绝对不意味着指针类型等同于 unsigned int数据类型,因为它们的运算规则截然不同, 1.2指针是派生数据类型 指针数据类型和数组、结构体、联合体等一样,也是一种派生数据类型(Derived Types)。 也就是说,指针数据类型是一种借助其他数据类型构造出来的数据类型。对于任何类型,都可 以构造出与之相对应的指针数据类型。因此指针数据类型实际上有无穷多种。 没有纯粹的指针,正如同没有纯粹的数组一样。数组是在其他数据类型的基础上构造出来的,指 针也必须与其他数据类型一道才能构成自己
指针 1 指针是什么 1.1 指针是一类数据类型的统称 对于 C 语言来说,计算机的内存由连续的字节(byte)构成。这些连续的字节同样被连续地编 上了号码以相互区别,这个号码就是所谓的地址(Address),如图 9-1 所示。 图 9-1 内存单元与地址 指针(Pointer)是 C 语言中的一类数据类型的统称。这种类型的数据专门用来存储和表示内存 单元的编号,以实现通过地址得以完成的各种运算。 这样看来指针似乎就是地址,然而,事实上却并非如此。后面将会看到,地址只是指针内涵中的 一部分,甚至只是一小部分内容而远非其全部。片面地把地址理解为指针的全部,永远学不好指 针。 为了使得语言具有广泛的适用性,C 语言标准允许编译器自行选择指针类型数据的长度。在不同 的编译环境下,指针数据类型的长度可能不同;甚至相同的编译环境中不同的指针数据类型,也 可能有不同的大小。 为了叙述的方便,本书中的指针数据类型一律假设为具有 32bit 的长度。这样并不影响对指针 本质的描述,但涉及指针数据类型长度的代码(极少)在不同的编译环境中可能具有不同的结果, 这点请读者加以注意。 C 语言同样不规定地址这种内存单元的编号在内存中的存储格式,但在现实中目前这种编号多数 是与二进制的 unsigned int 数据类型的存储格式一样,这是本章的另一个假定。这意味着程序 可以访问的内存的大小最大为 2 的 32 次方 (4GB)。但这绝对不意味着指针类型等同于 unsigned int 数据类型,因为它们的运算规则截然不同。 1.2 指针是派生数据类型 指针数据类型和数组、结构体、联合体等一样,也是一种派生数据类型(Derived Types)。 也就是说,指针数据类型是一种借助其他数据类型构造出来的数据类型。对于任何类型 ,都可 以构造出与之相对应的指针数据类型。因此指针数据类型实际上有无穷多种。 没有纯粹的指针,正如同没有纯粹的数组一样。数组是在其他数据类型的基础上构造出来的,指 针也必须与其他数据类型一道才能构成自己

指针让人感到比较复杂的原因之一在于,各种不同类型的指针都有自己的运算规则,尽管它们都 被叫做指针。这一点请特别留意,不同类型的指针有不同的运算种类和不同的运算规则。 综上所述,每一种特定的指针类型都是一种派生数据类型,其值表示某个内存单元的地址,其用 途是完成与地址有关的计算。 1.3指针是一类数据的泛称 当某个数据的数据类型是指针时,通常也简称这个数据是一个指针。很显然,在这里“指针”具有 “名词"的含义。而指针表示”数据类型“”含义时,显然具有形容词”的意味。这种”一词多用“的现 象,对于熟悉C语言特点的人来说并不值得大惊小怪,C语言本身也是这样的。比如,"[门“既可 以作为类型说明符也可以作为运算符。 1.4指针专用的类型说明符一“*” 数组这种构造性的数据类型有自己特定的类型说明符一“[门”,这种类型说明符用于定义数组或描 述数组名的类型。 结构体和联合体数据类型特定的类型说明符分别是关健字"struct“和"union”。 指针也有自己的特定的类型说明符一”*“。 和仅靠"口"无法完成数组的描述一样,指针也需要”*与其他的类型说明符一道才能完成对指针 类型的完整描述。由于”其他的类型说明符”有无限多种,所以指针的类型也有无限种可能。可以 构造出int*"类型的指针、“char本"类型的指针、"double*"类型的指针、"vold*"类型的指 针. 指针的一个重要特点是,它总是和另外一种数据类型联系在一起的 1.5指针的分类 尽管有无穷多种指针类型,但从指针所关联的数据类型方面看,指针可以分为3类:指向数据 对象的指针(Object Pointer)、指向函数的指针(Function Pointer)、指向虚无的指针("void *”类型)·前两者都与内存中的实体(数据和一段函数的执行代码)有关,而”V01d*"类型的 指针则仅仅是一个值,是纯粹的地址。“指针就是地址”这样的说法对于"voi*"这种类型的指针 是成立的。但对于与一段具体内存实体相关联的指针类型来说,这种说法是极其片面的,甚至片 面到了几乎完全忽略了指针的本质而只剩下了指针的皮毛的地步。正确的说法是,指针的值(右 值)是地址,这与”指针就是地址“是完全不同的概念。学习指针最重要的内容通常是关心指针的 值以外的东西,而指针的值一下面将会看到,那几乎倒是无关紧要的。 从所具有的运算方面看,这3类指针各自拥有不同的运算种类的集合。有的运算种类多些,有 的少些。 2指向数据对象的指针 2.1什么是"数据对象
指针让人感到比较复杂的原因之一在于,各种不同类型的指针都有自己的运算规则,尽管它们都 被叫做指针。这一点请特别留意,不同类型的指针有不同的运算种类和不同的运算规则。 综上所述,每一种特定的指针类型都是一种派生数据类型,其值表示某个内存单元的地址,其用 途是完成与地址有关的计算。 1.3 指针是一类数据的泛称 当某个数据的数据类型是指针时,通常也简称这个数据是一个指针。很显然,在这里“指针”具有 “名词”的含义。而指针表示“数据类型”含义时,显然具有“形容词”的意味。这种“一词多用”的现 象,对于熟悉 C 语言特点的人来说并不值得大惊小怪,C 语言本身也是这样的。比如,“[]”既可 以作为类型说明符也可以作为运算符。 1.4 指针专用的类型说明符—“*” 数组这种构造性的数据类型有自己特定的类型说明符—“[]”,这种类型说明符用于定义数组或描 述数组名的类型。 结构体和联合体数据类型特定的类型说明符分别是关键字“struct”和“union”。 指针也有自己的特定的类型说明符—“*”。 和仅靠“[]”无法完成数组的描述一样,指针也需要“*”与其他的类型说明符一道才能完成对指针 类型的完整描述。由于“其他的类型说明符”有无限多种,所以指针的类型也有无限种可能。可以 构造出“int *”类型的指针、“char *”类型的指针、“double *”类型的指针、“void *”类型的指 针.。 指针的一个重要特点是,它总是和另外一种数据类型联系在一起的。 1.5 指针的分类 尽管有无穷多种指针类型,但从指针所关联的数据类型方面看,指针可以分为 3 类:指向数据 对象的指针(Object Pointer)、指向函数的指针(Function Pointer)、指向虚无的指针(“void *”类型)。前两者都与内存中的实体(数据和一段函数的执行代码)有关,而“void *”类型的 指针则仅仅是一个值,是纯粹的地址。“指针就是地址”这样的说法对于“void *”这种类型的指针 是成立的。但对于与一段具体内存实体相关联的指针类型来说,这种说法是极其片面的,甚至片 面到了几乎完全忽略了指针的本质而只剩下了指针的皮毛的地步。正确的说法是,指针的值(右 值)是地址,这与“指针就是地址”是完全不同的概念。学习指针最重要的内容通常是关心指针的 值以外的东西,而指针的值—下面将会看到,那几乎倒是无关紧要的 。 从所具有的运算方面看,这 3 类指针各自拥有不同的运算种类的集合。有的运算种类多些,有 的少些。 2 指向数据对象的指针 2.1 什么是“数据对象

所谓数据对象”(Object),含义如下, (1)是内存中一段定长的、以byte为基本单位的连续区域。 (2)这段内存区域中的内容表示具有某种类型的一个数据。 数据对象的类型不一定是简单数据类型(int、long、double等),也可以是派生类型,比如 数组,甚至指针等。 而所谓的"指向”(Pointer to)的含义是指针与这块具有类型含义的整体的关联。例如,对于 int i; “”可以表示它所占据的内存块,当说到某个指针指向"”时,其确切的含义是指向”所占据内存 的整体。显然这里提到的”是左值意义上的“”。 函数类型不属于数据对象。 2.2一元&”运算 尽管前面各章从来没有提到指针,但实际上在前面编程的过程中已经和指针打过无数次交道了。 这可能令人感到吃惊,但却是事实。 比如,在调用scanf()函数输入变量值的时候,在实参中经常可以看到的&”,实际上就是在求 一个指向某个数据对象的指针。 对于下面的变量定义 double d; 表达式“&d"就是一个指针类型的数据,类型是double*”,这种类型的指针被称为是指向 "double"类型数据的指针。 前面讲过,作为二元运算符,“&”是按位与运算。当&”作为一个一元运算符时,要求它的运算 对象是一个左值表达式(一块内存),得到的是指向这块内存(类型)的指针。而一个变量的名 字的含义之一就是这个变量所占据的内存。大多数人在多数情况下关心的只是变量名的另一个含 义一值,这可能是学不好指针以及C语言的一个主要原因。在此,简要地复习一下C语言的 些最基本的内容。假如有如下定义: double d=3.0; 那么,应该如何理解表达式"d=d+5.0"呢? 这是一个赋值表达式,表示的确切含义是“取出变量d的值与常量5.0相加,然后把结果放到变 量d'所在的内存中去”,请特别注意在赋值号”=”的左边和右边,d”"这个标识符的含义是不同的: 在赋值号”=”右边的"表示的是”“的值,计算机的动作是取出这个值(本质上是在运算器中建 立"d"的副本),并不关心d存放在内存中的什么地方:而在赋值号”="左边的"d"表示的是"d” 所在的内存空间,是把一个值放入这块内存中去,后一个动作与"中的值没有什么关系(只是 把原来的值擦除),""中原来有什么值都不妨碍把一个新的值放入其中,也对新的值没有任何
所谓“数据对象”(Object),含义如下。 (1)是内存中一段定长的、以 byte 为基本单位的连续区域。 (2)这段内存区域中的内容表示具有某种类型的一个数据。 数据对象的类型不一定是简单数据类型(int、long、double 等),也可以是派生类型,比如 数组,甚至指针等。 而所谓的“指向”(Pointer to)的含义是指针与这块具有类型含义的整体的关联。例如,对于 int i; “i”可以表示它所占据的内存块,当说到某个指针指向“i”时,其确切的含义是指向“i”所占据内存 的整体。显然这里提到的“i”是左值意义上的“i”。 函数类型不属于数据对象。 2.2 一元“&”运算 尽管前面各章从来没有提到指针,但实际上在前面编程的过程中已经和指针打过无数次交道了。 这可能令人感到吃惊,但却是事实。 比如,在调用 scanf()函数输入变量值的时候,在实参中经常可以看到的“&”,实际上就是在求 一个指向某个数据对象的指针。 对于下面的变量定义 double d; 表达式“&d”就是一个指针类型的数据,类型是“double *”,这种类型的指针被称为是指向 “double”类型数据的指针。 前面讲过,作为二元运算符,“&”是按位与运算。当“&”作为一个一元运算符时,要求它的运算 对象是一个左值表达式(一块内存),得到的是指向这块内存(类型)的指针。而一个变量的名 字的含义之一就是这个变量所占据的内存。大多数人在多数情况下关心的只是变量名的另一个含 义—值,这可能是学不好指针以及 C 语言的一个主要原因。在此,简要地复习一下 C 语言的一 些最基本的内容。假如有如下定义: double d=3.0; 那么,应该如何理解表达式“d = d + 5.0”呢? 这是一个赋值表达式,表示的确切含义是“取出变量‘d’的值与常量‘5.0’相加,然后把结果放到变 量‘d’所在的内存中去”。请特别注意在赋值号“=”的左边和右边,“d”这个标识符的含义是不同的: 在赋值号“=”右边的“d”表示的是“d”的值,计算机的动作是取出这个值(本质上是在运算器中建 立“d”的副本),并不关心“d”存放在内存中的什么地方;而在赋值号“=”左边的“d”表示的是“d” 所在的内存空间,是把一个值放入这块内存中去,后一个动作与“d”中的值没有什么关系(只是 把原来的值擦除),“d”中原来有什么值都不妨碍把一个新的值放入其中,也对新的值没有任何

影响。 由此可见,同一个变量名确实有两种含义。针对两种不同的含义,计算机能进行的操作也不同。 换句话说,对于某些运算,变量名的含义是其右值:而对于另一些运算,变量名的含义是其左值。 编译器根据上下文来分辨变量名究竞是哪种含义。对于用C语言编程的人来说,不分辨清楚这 两种含义就不可能透彻地理解C语言。 再举个例子,在"sizeof d"这个表达式中,“d"的含义也是"d”占据的内存而不是”d“的值一无论 d"的值是多少,表达式"sizeof d"的值都为8。 在表达式8d"中,“d”的含义也是d"所在的内存而不是d"的值,“d”的值是多少都对"&"的运 算结果没有任何影响。 有一种说法称一元”&”运算是求地址运算,这种说法既是片面的,也是不严格的,同时对于学习 指针有很大的负面作用。理由如下。 在C语言中根本没有地址”这种数据类型,只有指针“数据类型,而指针的值才是一个地址。 用地址即指针的值的概念偷换指针的概念,显然是以偏概全。更为严重的是,这种说法使得许多 人根本就不知道”8d“是个指针,也掩盖了“&d”指向一块内存的事实,因为”&d”的值仅仅是d 所占据的那块内存单元中第一个byte的编号。 那么“8”的值是多少呢?实际上多数情况下,尤其是对于初学者来说,根本没必要关心这个值 是多少,也不可能事先知道这个值。因为为变量”安排存储空间是编译器的工作,编译器是根 据程序运行时内存中的实际情况“随机“为变量“"安排内存的。源程序的作者是水远不可能为变 量"指定”一块特定的存储空间,同样也不可能改变”"在内存中的存储位置 这样,“&”就是一个既不可能通过代码被赋值也不可能通过代码被改变的值,因而是个常量, 叫做指针常量,类型是"d0uble*”。这样的常量不可以被赋值也不可以进行类似"++”、”-_” 之类的运算,因为改变"&d”"的值就相当于改变了变量"d"的存储空间的位置,然而这是根本不可 能的。 当然,在程序运行之后,具体来说是”"的存储空间确定之后(也就是定义了变量”d“之后,因 为这时"“才开始存在),“&d”的值是确实可以知道的(其实知道了也没什么用)。如果想查看 一下,可以通过调用printf)函数用"%p"格式输出(指针类型数据的给出格式是"%p")。如 下面所示。 程序代码9-1 #include #include int main(void
影响。 由此可见,同一个变量名确实有两种含义。针对两种不同的含义,计算机能进行的操作也不同。 换句话说,对于某些运算,变量名的含义是其右值;而对于另一些运算,变量名的含义是其左值。 编译器根据上下文来分辨变量名究竟是哪种含义。对于用 C 语言编程的人来说,不分辨清楚这 两种含义就不可能透彻地理解 C 语言。 再举个例子,在“sizeof d”这个表达式中,“d”的含义也是“d”占据的内存而不是“d”的值—无论 “d”的值是多少,表达式“sizeof d”的值都为 8。 在表达式“&d”中,“d”的含义也是“d”所在的内存而不是“d”的值,“d”的值是多少都对“&”的运 算结果没有任何影响。 有一种说法称一元“&”运算是求地址运算,这种说法既是片面的,也是不严格的,同时对于学习 指针有很大的负面作用。理由如下。 在 C 语言中根本没有“地址”这种数据类型,只有“指针”数据类型,而指针的值才是一个地址。 用地址即指针的值的概念偷换指针的概念,显然是以偏概全。更为严重的是,这种说法使得许多 人根本就不知道“&d”是个指针,也掩盖了“&d”指向一块内存的事实,因为“&d”的值仅仅是“d” 所占据的那块内存单元中第一个 by te 的编号。 那么“&d”的值是多少呢?实际上多数情况下,尤其是对于初学者来说,根本没必要关心这个值 是多少,也不可能事先知道这个值。因为为变量“d”安排存储空间是编译器的工作,编译器是根 据程序运行时内存中的实际情况“随机”为变量“d”安排内存的。源程序的作者是永远不可能为变 量“指定”一块特定的存储空间,同样也不可能改变“d”在内存中的存储位置。 这样,“&d”就是一个既不可能通过代码被赋值也不可能通过代码被改变的值,因而是个常量, 叫做指针常量 ,类型是“double *”。这样的常量不可以被赋值也不可以进行类似“++”、“− −” 之类的运算,因为改变“&d”的值就相当于改变了变量“d”的存储空间的位置,然而这是根本不可 能的。 当然,在程序运行之后,具体来说是“d”的存储空间确定之后(也就是定义了变量“d”之后,因 为这时“d”才开始存在),“&d”的值是确实可以知道的(其实知道了也没什么用)。如果想查看 一下,可以通过调用 printf()函数用“%p”格式输出(指针类型数据的输出格式是“%p”)。如 下面所示。 程序代码 9-1 #include #include int main( void ) {

double d; printf("%pIn",&d ) system("PAUSE"); return 0; 这段代码的程序运行结果并不能事先确定,这和程序运行的具体环境有关。在作者的计算机上, 其运行结果如图9-2所示。 E:\Dev-Cpp程序代9-0.ee 衢继续。·· 图9-2一元&"运算 这个运行结果表示的含义如图9-3所示。 0022578 d("db址'袋均 〔d的值) d〔省值) d(左值】 图9-3指针与地址 应该注意到d"没有被赋值,但程序没有任何问题。这再次说明了“&d"与d“的值没有任何关系, 在表达式"&d”中的d”表示的仅仅是变量所在的内存而不是这块内存的值。 一元&"运算符的优先级和其他一元运算符(比如逻辑非"")一样,次于"()”、"[口”等运算符, 结合性为从右向左。这个运算符叫做关联运算符(Referencing Operator)。其确切的含义是, 运算所得到与运算对象所占据的那块内存相关联的指针,其值为那块内存单元中起始byte的地 址,也可以将之称为求指针运算符。 大多数情况下,“&"的运算对象是一个变量名(或数组名、函数名)。但一般的,它的运算对象 可以是一个表达式,只要这个表达式能够表示一块内存,比如对于数组 long a[100]: "a[0]"就是一个表达式,由于这个表达式既可以表示a[0]"的值,也可以表示"a[0]"所占据的 内存,所以"&a[0]"是合法的、有意义的C语言运算,结果就是一个“long*"类型的指针
double d; printf("%p\n", & d ); system("PAUSE"); return 0; } 这段代码的程序运行结果并不能事先确定,这和程序运行的具体环境有关。在作者的计算机上, 其运行结果如图 9-2 所示。 图 9-2 一元“&”运算 这个运行结果表示的含义如图 9-3 所示。 图 9-3 指针与地址 应该注意到“d”没有被赋值,但程序没有任何问题。这再次说明了“&d”与“d”的值没有任何关系, 在表达式“&d”中的“d”表示的仅仅是变量所在的内存而不是这块内存的值。 一元“&”运算符的优先级和其他一元运算符(比如逻辑非“!”)一样,次于“()”、“[]”等运算符, 结合性为从右向左。这个运算符叫做关联运算符(Referencing Operator)。其确切的含义是, 运算所得到与运算对象所占据的那块内存相关联的指针,其值为那块内存单元中起始 byte 的地 址,也可以将之称为求指针运算符。 大多数情况下,“&”的运算对象是一个变量名(或数组名、函数名)。但一般的,它的运算对象 可以是一个表达式,只要这个表达式能够表示一块内存 ,比如对于数组 long a[100]; “a[0]”就是一个表达式,由于这个表达式既可以表示“a[0]”的值,也可以表示“a[0]”所占据的 内存,所以“&a[0]”是合法的、有意义的 C 语言运算,结果就是一个“long *”类型的指针

而另一些表达式,比如"[0]+3”,由于只有值(右值)的含义而不代表一块内存,所以 “8(a[0]+3)"是没有意义的非法的表达式。 代码中的常量,由于只有右值的含义,因而不可以进行&”运算。比如“&5”,是没有意义的非法 的表达式。对于符号常量也同样不可以做”&"运算。 练习编写程序验证一下&”不可以被赋值也不可以进行类似”++”、”一一”之类的运算。 2.3数据指针变量的定义 数据指针变量的定义,是指用完整的指针类型说明符(这里所谓的”完整“是指用*和另一种完粉 数据类型的名称共同的意思)来说明一个变量标识符的性质,并为这个变量标识符开辟存储空间。 比如: int*p_i; 这样就定义了一个指向"it"类型数据的指针变量"p_严。其中"int"是另一种数据对象的类型的名 称,“幸“是指针类型说明符。类似地,定义 char *p_c; double*p_d; 分别被称为定义了一个指向char类型"数据的指针变量"p_c"和定义了一个指向”double类型 数据的指针变量”p_d” 至于所谓指向it'类型数据"的含义,是指:如果"p_r的值为34S6H,那么"p_r指向的是 3456H、3457H、3458H、3459H这4个字节,因为int"类型数据占据的内存空间的大小是 ^sizeof(nt),即4,如图9-4所示。 34% 345分 3458 340 p000000m0000011o100001010 p1的但为3斗56H二煤南为001101000010110) 图9-4数据指针类型的含义 由此可见"指向int'类型数据"的确切含义是指向一块大小为sizeof(int)的内存空间(但是指针 的值只记录最前面一个byte的地址而不是记录所指向的全部内存单元的地址),这比指针的值
而另一些表达式,比如“a[0]+3”,由于只有值(右值)的含义而不代表一块内存,所以 “&( a[0]+3)”是没有意义的非法的表达式。 代码中的常量,由于只有右值的含义,因而不可以进行“&”运算。比如“&5”,是没有意义的非法 的表达式。对于符号常量也同样不可以做“&”运算。 练习 编写程序验证一下“&d”不可以被赋值也不可以进行类似“+ +”、“− −”之类的运算。 2.3 数据指针变量的定义 数据指针变量的定义,是指用完整的指针类型说明符(这里所谓的“完整”是指用*和另一种完整 数据类型的名称共同的意思)来说明一个变量标识符的性质,并为这个变量标识符开辟存储空间。 比如: int *p_i; 这样就定义了一个指向“int”类型数据的指针变量“p_i”。其中“int”是另一种数据对象的类型的名 称,“*”是指针类型说明符。类似地,定义: char *p_c; double *p_d; 分别被称为定义了一个指向“char 类型”数据的指针变量“p_c”和定义了一个指向“double 类型” 数据的指针变量“p_d”。 至于所谓“指向‘int’类型数据”的含义,是指:如果“p_i”的值为 3456H,那么“p_i”指向的是 3456H、3457H、3458H、3459H 这 4 个字节,因为“int”类型数据占据的内存空间的大小是 “sizeof(int)”,即 4,如图 9-4 所示。 图 9-4 数据指针类型的含义 由此可见“指向‘int’类型数据”的确切含义是指向一块大小为“sizeof(int)”的内存空间(但是指针 的值只记录最前面一个 byte 的地址而不是记录所指向的全部内存单元的地址),这比指针的值

要重要得多,指针具体的值对掌握指针这种数据类型通常没有什么意义。 学习指针最重要的是要时刻关注指针指向一块多大的或者一块什么样的内存。因为这将决定这个 指针的几乎所有运算。 对于任何一种数据类型(除了某些不完全类型),都可以用和上面相仿的方式定义相应的指针变 量,指向对应类型数据所占据的内存空间的大小。 练习画一下p_c”、“p_"这两个指针变量在内存中的存储情况和指向的含义的示意图。假 设”p_c”、"p_d"的定义为: char *p_c; double*p_d; 2.4指针的赋值运算 对于指针类型的数据,唯一一个普可以进行的运算是赋值运算,各种指针都可以用来赋值,指 针变量都可以被赋值(除非用cost关键字限制),其余的指针运算都没有普递性。 对于下面的代码片段: 程序代码9-2(片段) int *p_i: int i; p_i=&i; 在表达式"pj=&严中,&i“是一个指向int类型数据的指针常量,p_”是一个指向“int" 类型数据的指针变量。 对指针变量进行赋值运算的一般原则是,应该(本章所提到的应该"”的含义指的是普遍认同的、 良好的编程风格,而不是语法的必须要求)用同样类型的指针进行赋值。例如下面的赋值就是似 是而非的,尽管有的编译器是能容忍的。 程序代码9-3(片段) double d; long *p_l; P=&d;/这两个指针的类型是不同的 本质上,不同类型的指针是不可以互相赋值的。但是对于表达式"pI=&d“,编译器会对这个 不合逻辑的赋值表达式做一个隐式的类型转换,如果不是桔确清醒地知道编译器会进行什么样的 转化,就不要写这种连自已都不清楚确切含义的语句。如果一定要类型转换,不如显式地表达出 来。比如: p」=(long*)&d;
要重要得多,指针具体的值对掌握指针这种数据类型通常没有什么意义。 学习指针最重要的是要时刻关注指针指向一块多大的或者一块什么样的内存。因为这将决定这个 指针的几乎所有运算。 对于任何一种数据类型(除了某些不完全类型),都可以用和上面相仿的方式定义相应的指针变 量,指向对应类型数据所占据的内存空间的大小。 练习 画一下“p_c”、“p_d”这两个指针变量在内存中的存储情况和指向的含义的示意图。假 设“p_c”、“p_d”的定义为: char *p_c; double *p_d; 2.4 指针的赋值运算 对于指针类型的数据,唯一一个普遍可以进行的运算是赋值运算,各种指针都可以用来赋值,指 针变量都可以被赋值(除非用 const 关键字限制),其余的指针运算都没有普遍性。 对于下面的代码片段: 程序代码 9-2(片段) int *p_i; int i; p_i = & i ; 在表达式“p_i = & i”中,“& i ”是一个指向“int”类型数据的指针常量,“p_i”是一个指向“int” 类型数据的指针变量。 对指针变量进行赋值运算的一般原则是,应该(本章所提到的“应该”的含义指的是普遍认同的、 良好的编程风格,而不是语法的必须要求)用同样类型的指针进行赋值。例如下面的赋值就是似 是而非的,尽管有的编译器是能容忍的。 程序代码 9-3(片段) double d; long *p_l; p_l = & d;//这两个指针的类型是不同的 本质上,不同类型的指针是不可以互相赋值的。但是对于表达式“p_l = & d”,编译器会对这个 不合逻辑的赋值表达式做一个隐式的类型转换。如果不是精确清醒地知道编译器会进行什么样的 转化,就不要写这种连自己都不清楚确切含义的语句。如果一定要类型转换,不如显式地表达出 来。比如: p_l = ( long * ) & d;

一种不多见的对指针变量的赋值是把一个”地址常数”赋值给它,这时一般也应该把"地址常数” 用”类型转换“运算转换为一个”指针常数”再进行赋值,如: int *p_i=(int +)0XABCD; 2.5不是乘法的"*“运算 “*“是指针类型说明符,同时也可以充当”乘法”运算符(作为二元运算符时),此外*“也可以是 一个一元运算符。这是C语言中典型的”一词多义“的现象(变量名也是如此),符号具体的含 义需要由符号所处的语境一代码的上下文确定。这是C语言的一个特点,也是难点。 一元*“运算是指针特有的一个运算,下面通过具体的例子讲述”*运算的含义。 对于变量定义: int i; 根据前面所讲,对"int“类型变量"i“做”&”运算可得到一个指向it“类型变量"严的指针,这个指 针的数据类型是"int*”。而对于“int*"类型的指针“&i”,*(8的含义就是"&”所指向的那块内 存或者是那块内存的值,换句话说”*(&)”就是“”一可以作为左值使用也可以作为右值使用。 因此,对”的一切操作也都可以通过指向”的指针与”*“来实现。例如对”这块内存赋值 i=2; 另一种完全等效的方式是 *(8)=2; 如果需要取得"”的值也是一样,比如对于表达式“*3”(这里”的意义是”的值),完全等价 的表达式是(*(8则)*3” 这里出现的第二个“*”运算符,由于前后都有运算对象,因此是乘法运算。而(&)”前面的”* 则不是乘法运算。这也是在不同语境上下文中一词多义的例子。 此外由于”*“作为一个一元运算优先级与”&”相同,且一元运算符的结合性为从右向左,所以表 达式“(*(8则)*3"的另一种等价写法是”*8则*3” "*"运算符叫做"间接引用运算符”(Indirection Operator或Dereferencing Operator),其 运算对象是一个指针,运算结果得到的是指针所指向的那块内存(左值)或那块内存中数据的值 (右值)。 从“&"和*“运算的含义中完全可以发现这样的事实:对于任何一个变量"V”,"*&v”就是"v”; 反过来,对于任何一个指针“p”,只要p"指向一个变量(可以进行*“运算),那么,“&*p”就 是"D”. 前面两条结论还可以适当推广。实际上,这对透彻地理解指针非常有帮助。比如第一条规律,不 仅仅对变量成立,实际上对任何内存中的有完整意义的实体”St”(一段连续的内存空间,可能代 表某种类型的一个数据或者是一个函数的执行代码)都成立:"*85t”就是"st”,反过来只要
一种不多见的对指针变量的赋值是把一个“地址常数”赋值给它,这时一般也应该把“地址常数” 用“类型转换”运算转换为一个“指针常数”再进行赋值,如: int *p_i=(int *)0XABCD; 2.5 不是乘法的“*”运算 “*”是指针类型说明符,同时也可以充当“乘法”运算符(作为二元运算符时),此外“*”也可以是 一个一元运算符。这是 C 语言中典型的“一词多义”的现象(变量名也是如此),符号具体的含 义需要由符号所处的语境—代码的上下文确定。这是 C 语言的一个特点,也是难点。 一元“*”运算是指针特有的一个运算,下面通过具体的例子讲述“*”运算的含义。 对于变量定义: int i ; 根据前面所讲,对“int”类型变量“i”做“&”运算可得到一个指向“int”类型变量“i”的指针,这个指 针的数据类型是“int *”。而对于“int *”类型的指针“&i”,*(&i)的含义就是“&i”所指向的那块内 存或者是那块内存的值,换句话说“*(&i)”就是“i”—可以作为左值使用也可以作为右值使用。 因此,对“i”的一切操作也都可以通过指向“i”的指针与“*”来实现。例如对“i”这块内存赋值: i = 2 ; 另一种完全等效的方式是: *(&i) = 2 ; 如果需要取得“i”的值也是一样,比如对于表达式“i*3”(这里“i”的意义是“i”的值),完全等价 的表达式是“( * ( &i ) ) * 3 ”。 这里出现的第二个“*”运算符,由于前后都有运算对象,因此是乘法运算。而“( &i )”前面的“*” 则不是乘法运算。这也是在不同语境上下文中一词多义的例子。 此外由于“*”作为一个一元运算优先级与“&”相同,且一元运算符的结合性为从右向左,所以表 达式“( * ( &i ) ) * 3 ”的另一种等价写法是“ * &i * 3 ”。 “*”运算符叫做“间接引用运算符”(Indirection Operator 或 Dereferencing Operator),其 运算对象是一个指针,运算结果得到的是指针所指向的那块内存(左值)或那块内存中数据的值 (右值)。 从“&”和“*”运算的含义中完全可以发现这样的事实:对于任何一个变量“v”,“*&v”就是“v”; 反过来,对于任何一个指针“p”,只要“p”指向一个变量(可以进行“*”运算),那么,“&*p”就 是“p”。 前面两条结论还可以适当推广。实际上,这对透彻地理解指针非常有帮助。比如第一条规律,不 仅仅对变量成立,实际上对任何内存中的有完整意义的实体“st”(一段连续的内存空间,可能代 表某种类型的一个数据或者是一个函数的执行代码 )都成立:“*&st”就是“st”,反过来只要一

个指针p"不是"void*"类型,那么“&*p"就是”p”。由此可见,“&”与"*“是一对逆运算 (Referencing与Dereferencing)。 对手下面的变量定义: 程序代码94(片段) 假设在内存中的存陆圆像如园95所乐。试问经过程序代码95(片俊)运算后,内存 中的存储状态为何? 程序代码95(片段) , BABE EA 9BABA 14 BACA pi t BACA BACE BAD6 7 BACA BAD6 00 pd 图95内存有储示蜜图 3指针的应用与误用 3.1指针有什么用 在了解了指针的一些基本概念之后,自然而然会想到的一个问题就是指针究竞有什么用处。如果 对于变量定义 int i; 既然"1=2"与”*8=2”是完全等价的操作,那么两个完全等价的操作中难道不是必然会有一 个是多余的吗? 想到这些问愿非常自然。实际上指针菲常有用,指针是C语言的精华。下面将逐步介绍如何应 用指针。 指针的用途之一是通过函数改变函数调用处本地局部变量的值。如果没有指针的话,改变本地局 部变量的值,只能通过把函数返回值赋值给这个本地局部变量的办法。但是由于函数只能返回 个值,所以这种办法有很大的局限性
个指针“p”不是“void *”类型,那么“&*p”就是“p”。由此可见,“&”与“*”是一对逆运算 (Referencing 与 Dereferencing)。 3 指针的应用与误用 3.1 指针有什么用 在了解了指针的一些基本概念之后,自然而然会想到的一个问题就是指针究竟有什么用处。如果 对于变量定义 int i ; 既然“i = 2”与“*&i = 2”是完全等价的操作,那么两个完全等价的操作中难道不是必然会有一 个是多余的吗? 想到这些问题非常自然。实际上指针非常有用,指针是 C 语言的精华。下面将逐步介绍如何应 用指针。 指针的用途之一是通过函数改变函数调用处本地局部变量的值。如果没有指针的话,改变本地局 部变量的值,只能通过把函数返回值赋值给这个本地局部变量的办法。但是由于函数只能返回一 个值,所以这种办法有很大的局限性

首先看一个简单的例子。 程序代码9-6 #include #include void f(int); int main(void) int i=5; f0: printf("i=%d小n",i): system("PAUSE"); return O; void f(int n) n++ printf("n=%d\n",n ) return; 这段程序的输出是: n=6 i=5 请按任意键继续。· 可以看到在f()函数中,形参n”的值的改变对main()函数中的i没有影响。这是因为在C语言 中,实参与形参之间是"传值"的关系,形参""是把”的值(右值)而不是”本身作为自已的初 始值。在计算实参时求出的”的值可能被放在运算器中,也可能被放在内存中的另一个地方, 这样无论“n“如何变化都不会使得“发生改变。这个过程如图9-6所示。 也就是说,尽管在f(O函数中,可以获得main()中当地变量”"的值(右值),然而由于”是 main(O中的局部变量,f)函数并不能直接使用这个变量的左值
首先看一个简单的例子。 程序代码 9-6 #include #include void f(int); int main(void) { int i=5; f(i); printf("i=%d\n",i); system("PAUSE"); return 0; } void f(int n) { n++; printf("n=%d\n" , n ); return ; } 这段程序的输出是: n=6 i=5 请按任意键继续. . . 可以看到在 f()函数中,形参“n”的值的改变对 main()函数中的 i 没有影响。这是因为在 C 语言 中,实参与形参之间是“传值”的关系,形参“n”是把“i”的值(右值)而不是“i”本身作为自己的初 始值。在计算实参时求出的“i”的值可能被放在运算器中,也可能被放在内存中的另一个地方, 这样无论“n”如何变化都不会使得“i”发生改变。这个过程如图 9-6 所示。 也就是说,尽管在 f()函数中,可以获得 main()中当地变量“i”的值(右值),然而由于“i”是 main()中的局部变量,f()函数并不能直接使用这个变量的左值
按次数下载不扣除下载券;
注册用户24小时内重复下载只扣除一次;
顺序:VIP每日次数-->可用次数-->下载券;
- 《计算机导论》课程教学课件(英文讲稿)7-a- Web.pdf
- 《计算机导论》课程教学课件(英文讲稿)6-b- The Internet.pdf
- 《计算机导论》课程教学课件(英文讲稿)6-a- The Internet.pdf
- 《计算机导论》课程教学课件(英文讲稿)5-b- LANS_WANS.pdf
- 《计算机导论》课程教学课件(英文讲稿)5-a- LANS_WANS.pdf
- 《计算机导论》课程教学课件(英文讲稿)4-a- File mangement.pdf
- 《计算机导论》课程教学课件(英文讲稿)4- operating system.pdf
- 《计算机导论》课程教学课件(英文讲稿)3-a-b-Computer Software.pdf
- 《计算机导论》课程教学课件(英文讲稿)2-b-Computer Hardware.pdf
- 《计算机导论》课程教学课件(英文讲稿)2-a-Computer Hardware.pdf
- 《计算机导论》课程教学课件(英文讲稿)1-b-Digital Data Representation.pdf
- 《计算机导论》课程教学课件(英文讲稿)1-a-Computer History+ Di Devices.pdf
- 《计算机导论》课程教学大纲 Computer Concepts.pdf
- 《微型计算机技术及应用》课程电子教案(PPT教学课件,共十五章,完整版).pptx
- 《微机技术及应用》课程教学大纲 Microcmputer Technology and aplications.doc
- 《C++面向对象程序设计》课程教学资源(PPT课件)Chapter 14 Polymorphism and Virtual Functions.ppt
- 《C++面向对象程序设计》课程教学资源(PPT课件)Chapter 13 Inheritance.ppt
- 《C++面向对象程序设计》课程教学资源(PPT课件)Chapter 9 Strings.ppt
- 《C++面向对象程序设计》课程教学资源(PPT课件)Chapter 10 Pointers and Dynamic Arrays.ppt
- 《C++面向对象程序设计》课程教学资源(PPT课件)Chapter 12 Streams and File IO.ppt
- 《C语言程序设计》课程教学资源(讲义资料)C指针详解(经典详细).pdf
- 《C语言程序设计》课程教学资源(讲义资料)C语言指针用法详解.pdf
- 《C语言程序设计》课程教学课件(PPT讲稿)C语言指针详解.ppt
- 《C语言程序设计》课程教学课件(PPT讲稿)c语言指针完整教程.ppt
- 《C语言程序设计》课程教学资源(讲义资料)C语言程序设计期中测试(函数,带答案).pdf
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第06章 指针.ppt
- 《C语言程序设计》课程教学资源(讲义资料)C语言程序设计期中测试(数组,带答案).pdf
- 《C语言程序设计》课程教学资源(讲义资料)C语言程序设计期中测试(分支与循环以前知识点,带答案).pdf
- 中国农业大学:《C语言程序设计》课程教学资源(试卷习题)C程序设计讲义与习题(含参考答案).pdf
- 《C语言程序设计》课程教学资源(讲义资料)考试知识点复习(C语言程序设计复习样题及部分解析).doc
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第09章 文件.ppt
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第08章 结构体.ppt
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第07章 预处理命令.ppt
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第05章 函数.ppt
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第04章 数组.ppt
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第03章 三种基本控制结构(下).ppt
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第04章 三种基本控制结构(上).ppt
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第02章 数据类型、运算符和表达式.ppt
- 中国农业大学:《C语言程序设计》课程教学课件(PPT讲稿)第01章 C语言概述(主讲:李辉).ppt
- 《数据结构》课程实验教学大纲 Data Structure.doc