第八章地址和指针
C++程序设计 第八章 指针和引用

第
在程序运行时变量和函数都存放在内存中,通过变量名来访问数据、通过函数名来调用函数都是直接访问方式。还有另一种间接访问方式就是用指针。指针的本质是内存地址。指针往往用于说明函数的形参,使实参能通过指针传递,以提高函数调用的效率。利用指针能动态地使用内存,以提高内存使用效率。指针也能用来表示数据关联,以构成复杂的数据结构。指针是C程序中最常见的类型。引用是C++扩展的新概念,主要用于函数形参和返回类型。本章将详细介绍指针和引用的概念及应用。
首先,这6个变量的地址是按递减次序排列,这是因为局部变量都存储在堆栈中,堆栈是先入后出的。先入栈的数据存放在较大地址位置,后入栈的数据存放在较小地址位置。如果这些变量改为全局变量,它们的排列次序就会颠倒过来。
其次,尽管变量s只占2字节,变量c只占1字节,但却分别占用4字节空间。这是因为按字对齐(32位数据)能提高CPU访问内存的效率,而且一次压栈和出栈操作也是以32位数据为单位,代价是浪费一些内存。如果这些变量改为全局变量,它们将按实际大小存储。
怎样能知道一个变量在运行时刻的内存地址?把取地址运算符&放在变量前面就得到它的首地址。例如b是一个变量,那么&b就表示它的地址。下面例子能看到一组局部变量的首地址。
例8-1显示一组局部变量的首地址。
#include<iostream.h>
void main(){
bool b = true;
char c = 'c';
其中,<类型名>是这个指针变量所指向的对象的类型,简称指针类型,它可以是任何一种类型。*表示这个变量是一个指针变量。这个变量的类型就是“<类型名>*”。<变量名>是一个标识符。指针变量可以进行初始化,等号之后给出一个变量的地址,要求这个变量的类型与指针类型相符。
《C语言程序设计》基本知识点

《C语言程序设计》基本知识点第一章C语言基本知识1.C源程序的框架尽管各个C源程序的功能千变万化,但框架是不变的,主要有:编译预处理、主函数()、函数n()等,主函数的位置不一定在最前面,可以在程序的中部或后面,主函数的名字固定为main。
2.C语言源程序的书写规则:(1)C源程序是由一个主函数和若干个其它函数组成的。
(2)函数名后必须有小括号,函数体放在大括号内。
(3)C程序必须用小写字母书写。
(4)每句的末尾加分号。
(5)可以一行多句。
(6)可以一句多行。
(7)可以在程序的任何位置加注释。
3.语句种类语句是程序的基本成分,程序的执行就是通过一条条语句的执行而得以实现的,根据表现形式及功能的不同,C语言的基本语句可以分为五大类。
(1)流程控制语句流程控制语句的功能是控制程序的走向,程序的流程有三种基本结构:顺序结构、分支结构和循环结构,任何复杂的程序都可以由这三种基本结构复合而成。
其中后两种结构要用特定的流程控制语句实现。
(2)表达式语句表达式语句的形式是:表达式;,即表达式后跟一分号“;”,分号是语句结束符,是一个语句必不可少的成分。
表达式和表达式语句的区别在于表达式代表的是一个数值,而表达式语句则代表一种动作。
最常见的表达式语句是赋值语句。
(3)函数调用语句函数调用语句实际上也是一种表达式语句,形式为:在一次函数调用的小括号后面加上一个分号。
(4)空语句空语句的形式就是一个分号,它不代表任何动作,常常作为一个意义转折点使用。
(5)复合语句复合语句从形式上看是多个语句的组合,但在语法意义上它只相当于一个语句,在任何单一语句存在的地方都可以是复合语句。
注意复合语句中最后一个语句末尾的分号不能少。
复合语句右大括号后面没有分号。
4.运算符用来表示数据各种操作的符号称为运算符。
运算符实际上代表了一种类型数据的运算规则。
不同的运算符具有不同的运算规则,其操作的数据类型必须符合该运算符的要求,运算结果的数据类型也是固定的。
C语言 第八章

p
printf(“%d,%d\n”,a,b); printf(“%d,%d\n”,*p1,*p2);}
三、指针变量作为函数参数 作用:将一个变量的地址传送到另一个函数中。 作用:将一个变量的地址传送到另一个函数中。 例1:编写实现两个数的交换的函数 : swap(int *p1, int *p2) *p2) void swap(int *p1, int swap(int x,int y) {{ int temp; 定义为*temp? ? int temp; 定义为 { int temp; temp=*p1; temp=x; temp=*p1; *p1=*p2; x=y; *p1=*p2; y=temp; } *p2=temp; } *p2=temp; } main() main() main() { int a,b,*p1,*p2; { int a,b; { int a,b p1=&a;p2=&b; scanf("%d,%d",&a,&b); scanf(“%d%d”,&a,&b); scanf(“%d%d”,p1,p2); swap(a,b); swap(&a,&b); swap(p1,p2); printf("\n%d,%d\n",a,b); printf("%d, %d\n",a,b); printf("%d, %d\n",a,b); }} }
个字节的问题) 个字节的问题)
二、指针变量的引用 “&”(地址运算符 取变量的存储地址。如:&a求变量 的地址。 地址运算符) 取变量的存储地址。 地址运算符 求变量a的地址 求变量 的地址。 “*” (引用运算符 取指针所指向变量的内容。&与*优先级相同, 引用运算符) 取指针所指向变量的内容。 与 优先级相同 引用运算符 优先级相同, 但按自右至左的结合方向。 但按自右至左的结合方向。 例如: 例如:int i=3,*p; p=&i;
《C语言程序设计》第8章指针

10.3.3 指针变量和数组作函数参数 数组名作形参时,接收实参数组的起始地址;
作实参时,将数组的起始地址传递给形参数组。
引入指向数组的指针变量后,数组及指向数 组的指针变量作函数参数时,可有4种等价形式 (本质上是一种,即指针数据作函数参数):
(1)形参、实参都用数组名 (2)形参、实参都用指针变量 (3)形参用指针变量、实参用数组名 (4)形参用数组名、实参用指针变量
(4)指针变量的++、--与&、*的结合
对于指针变量的++、--与&、*的结合 使用,关键要注意按照运算符的优先级和 结合性进行。
例如: int a=2, *p; p=&a;
•表达式:(*p)++,按运算符的优先级,等价于 a++。其含义为:取出指针变量p所指向的内存单 元的值(即a的值),a的值加1,送回a的内存单 元,a的值变为3,p的值未发生变化,仍然指向 变量a。
程序说明:printf("%s\n",s);语句 通过指向字符串的指针变量s,整体引
用它所指向的字符串的原理:系统首先输出s 指向的第一个字符,然后使s自动加1,使 之指向下一个字符;重复上述过程,直至遇到 字符串结束标志。
main() { char string[ ]=”I love Beijing.”; printf(“%s\n”,string); }
3.数组元素的引用 数组元素的引用,既可用下标法,也可用
指针法。
10.3.2 通过指针引用数组元素 如果有“int a [10],*p=a;” ,则: (1)p+i和a+i都是数组元素a [i]的地址。
(2)*(p+i)和*(a+i)就是数组元素a [i]。 int a [3]; a [0]——*a a [1]——*(a +1) a [2]——*(a +2)
C语言 第八章.用户自定义数据类型

u2占2个字节
例:以下程序输出结果是?
union example { struct { int x,y; e.b e.a e.in.x }in; e.in int a; e.in.y int b; }e; void main() { e.a=1;e.b=2; e.in.x=e.a*e.b; e.in.y=e.a+e.b; printf("%d,%d",e.in.x,e.in.y); }
内存低地址 01100001 00001010
字符变量c占1个字节
整型变量i占2个字节 单精度实型变量f占4个字节
u1.i=10;
例:以下程序输出结果是?
union u_type { char c[2]; u1
p
内存高地址 内存低地址 ‘a’ ‘b’ c1 c2 c[1] c[0] u1占2个字节 内存高地址 内存低地址 ‘A’ ‘B’ ‘a’ ‘b’ c1 c2 c[1] c[0]
{"0208103322","lisi",19,'M'},{"0208103323","lili",20,'F'}, {"0208103324","xulin",21,'M'}};
学号(number) 姓名(name) 年龄(age) 性别(sex)
stu[0] stu[1] stu[2] stu[3]
char name[8];
int age; char sex;
定义结构体类 sizeof(stu) 型的同时创建 =? 结构体数组
C二级 第8章 指针

1.以下定义语句中正确的是A) int a=b=0; B) char A=65+1,b='b';C) float a=1,*b=&a,*c=&b; D) double a=0.0; b=1.1;参考答案:B【解析】A选项语句中b变量还没有定义不能直接用于给a变量赋值。C选项语句中*b、*c表示的是一个实型变量的地址,不能再将&b赋值给指针型变量c。D选项语句中a=0.0后面应该为逗号,不能是分号。2.有以下程序#include <stdio.h>void f(int *p,int *q);main(){ int m=1,n=2,*r=&m;f(r, &n);printf("%d,%d",m,n);}void f(int *p,int *q){ p=p+1;*q=*q+1;}程序运行后的输出结果是A) 2,3 B) 1,3 C) 1,4 D) 1,2参考答案:B【解析】在f(int *p,int*q)函数中,执行p=p+1是将p所对应的地址加1,而*q=*q+1是将q所指向的n的地址所对应的值加1,所以m的得知所对应的值没有变,而n的值则为3了。
因此B选项正确。
3.以下叙述中正确的是A) 如果p是指针变量,则&p是不合法的表达式B) 如果p是指针变量,则*p表示变量p的地址值C) 在对指针进行加、减算术运算时,数字1表示1个存储单元的长度D) 如果p是指针变量,则*p+1和*(p+1)的效果是一样的参考答案:C【解析】B选项中,如果p是指针变量,则*p表示变量p所指向的地址的值;A选项中,如果p是指针变量,则&p表示变量p的地址;D选项中,如果p是指针变量,*p+1表示将p所指的值加上1,而*(p+1)表示的是先将指针右移一位再取所指向变量的值。
因此C选项正确。
4.以下叙述中正确的是A) 基类型不同的指针变量可以相互混用B) 函数的类型不能是指针类型C) 函数的形参类型不能是指针类型D) 设有指针变量为double *p,则p+1 将指针p移动8个字节参考答案:D【解析】B选项中,所谓函数类型是指函数返回值的类型。
c语言程序设计谭浩强第四版

c语言程序设计谭浩强第四版C语言程序设计是计算机科学与技术领域中非常重要的基础课程之一。
谭浩强教授所著的《C语言程序设计》自问世以来,以其通俗易懂的语言和丰富的实例,深受广大学生和编程爱好者的喜爱。
第四版在继承前三版优点的基础上,对内容进行了更新和完善,更加符合现代编程教育的需求。
第一章:C语言概述本章主要介绍了C语言的发展历程、特点以及C语言在计算机编程领域中的应用。
C语言以其高效、灵活和可移植性,成为系统编程、嵌入式开发等领域的首选语言。
第二章:C语言的基本概念本章详细讲解了C语言的基本组成元素,包括数据类型、变量、常量、运算符和表达式等。
这些是编写C程序的基础,也是理解程序逻辑的关键。
第三章:顺序结构程序设计顺序结构是最简单的程序结构,本章通过实例讲解了如何使用顺序结构编写程序,以及如何通过输入输出函数实现数据的交互。
第四章:选择结构程序设计本章介绍了条件语句if、switch等选择结构的使用,通过这些结构可以实现程序的分支逻辑,使程序能够根据不同的条件执行不同的代码块。
第五章:循环结构程序设计循环结构是程序设计中不可或缺的部分,本章详细讲解了for、while、do-while等循环语句的用法,以及如何使用循环结构实现重复操作。
第六章:数组数组是存储多个同类型数据的集合,本章介绍了一维数组和二维数组的定义、初始化和使用,以及如何通过数组实现数据的批量处理。
第七章:函数函数是程序模块化的基础,本章讲解了函数的定义、声明、调用以及参数传递机制,包括值传递和地址传递的区别和应用。
第八章:指针指针是C语言中非常强大的特性之一,本章详细介绍了指针的基本概念、指针与数组的关系、指针的运算以及指针在函数中的应用。
第九章:结构体与联合体本章介绍了如何使用结构体和联合体来定义复杂的数据类型,以及如何通过这些复合数据类型实现数据的组织和管理。
第十章:预处理命令预处理命令是C语言编译过程中的特殊指令,本章讲解了宏定义、文件包含、条件编译等预处理命令的用法。
计算机操作系统第八章-磁盘存储器的管理

第八章磁盘存储器的管理第一节文件的物理结构和外存的分配方式一、概述磁盘是一种可直接存取的随机存储器(这一点与内存相似),一个逻辑盘可以看作一片连续的存储空间。
确定外存空间的分配方式(组织文件的物理结构)主要考虑:提高文件的访问速度、有效地利用外存空间。
常用的外存分配方法有:连续分配、链接分配、索引分配。
二、磁盘存储空间的结构磁盘说明图1盘块(扇区)是磁盘上的最小存储分配单位,每个盘块有唯一编号;地址是:磁道(柱面)号+扇区号+盘面号;从盘块编号到地址的转换由硬件完成,在OS中一个盘块的地址就是盘块编号。
一般一个盘块的大小与内存分页中页(内存块)的大小一致,一页存放到一个盘块中。
三、连续分配1、思想方法为每个文件分配一组位置相邻接的盘块(磁盘上的地址连续/盘块编号连续的盘块),文件中的逻辑页被顺序地存放到邻接的各物理盘块中。
这保证了文件中的逻辑顺序与文件占用盘块顺序的一致性。
这样物理结构的文件称为顺序文件;每个文件都从分配给它的一个盘块的第一个字节开始存放。
文件地址:在文件的目录中,存放该文件的第一个记录所在的盘块号和文件的长度(共占多少块)。
1230567491011813141512171819162122232025262724list29303128mailcountfile start length coun t 02tr 143mail 196list 284f62????tr f图 8-1 磁盘空间的连续分配2、优缺点◆存取容易,存取速度较快;◆必须事先知道文件的长度,不利于文件的动态增长; ◆存放一个文件要求足够大的连续存储空间; ◆存储空间的管理存在“碎片”问题,须定时整理。
四、链接分配1、思想方法:为每个文件分配一组位置离散的盘块,每个盘块中存放文件的一个逻辑页;通过给每个盘块设置一个指针,将属于同一个文件的盘块链接在一起,链接的顺序和文件的逻辑页的顺序一致。
这样物理结构的文件称为链接文件。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第八章地址和指针8.1变量的地址和指针在程序中变量实际上代表了内存中的某个存储单元。
那么C是怎样存取这些单元的数据内容的呢?我们知道计算机的内存是以字节为单位的一片连续的存储空间,每个内存单元都有一个唯一的编号,我们将其称为“内存地址”。
计算机对数据的存取操作都是依赖于内存地址进行的。
因为计算机的内存空间是连续的,所以内存中的地址空间也是连续的,并且用二进制数据来表示,为了方便和直观,我们将用十进制数据进行描述。
若在程序中定义了一个变量,C编译系统就会自动根据变量的类型,为其分配一定字节数量的存储空间。
如int型2个字节,float型4个字节,double型8个字节,char型1个字节等。
此后,这个变量的内存地址也就唯一的确定了。
一般情况下,我们在程序中只要给出变量名,不需要知道每个变量在内存中的具体地址,变量与地址之间的联系由C编译系统来完成。
程序中我们对变量进行存取操作,实际上就是对变量地址的存储单元进行操作。
这种直接按照变量地址进行存取的操作方式称为“直接存取”方式。
在C语言中我们还可以定义一种特殊的变量,这种变量只是用于存放内存变量地址的。
如:p93 图8.2这种通过变量p间接得到变量a的地址,然后再存取变量a的值的方式称为“间接存取”的方式。
这种用来存放地址的变量称为“指针变量”或“指针”。
由此我们可以知道,在C语言中,地址是指变量在内存中的存放的位置,即存放该变量的内存单元的名字。
而指针是指一个变量,在该变量中存放的是其指向的那个变量在内存存储单元的地址。
也就是说,变量的地址就可以理解为指针。
在C语言中,指针被广泛使用,他可以使程序简洁并高效运行,但使用不当就会产生意料不到的严重后果。
因此,正确使用和掌握指针是十分必要的。
8.2 指针变量的定义和基本类型定义指针变量的形式:类型名*指针变量名1,*指针变量名2,……;例如:int *pi,*pj;float *i,*j,*k;double *l,*m,*n;char *c,*s;在每个变量前面的星号*是一个指针说明符,用来说明该变量是指针类型。
变量前面的星号*不可以省略。
指针变量名前面的类型定义是说明指针指向的数据类型。
另外,我们还可以定义一个指向指针的指针变量。
定义形式为:类型名**指针变量名1,**指针变量名2,……;例如:int * * p,*s,k=90;s=&k; p=&s;其中&符号表示取变量的内存单元地址运算,后面介绍。
为什么指针变量要有类型之分呢?如前面所述,指针变量中存放的是一个内存单元的地址,这里“一个内存单元”中的“一”所代表的字节数是不同的。
如int型2个字节,float型4个字节,double型8个字节,char型1个字节等。
在后面的指针运算中我们经常要对指针进行加、减、移动等操作,此时指针操作的单位是一个存储单元,而不是一个字节,因此对于不同类型的指针变量来说,其操作时所跨越的内存字节数是不同的。
如:定义一个指针变量i(存放的初始地址为2060H),当我们执行i++操作时,不同类型的指针变量所移动的字节数是不同的。
8.3 指针变量赋值一、给指针变量赋地址值一个指针变量可以通过不同的方式获得一个确定的地址值,从而指向一个具体的操作对象。
1、通过取地址运算符(&)获得地址值单目运算符“&”的作用是用来获取变量的内存单元地址。
其使用格式为:&变量名。
如:int k=80, h=100, *p, *q; 则赋值语句为p=&k; q=&h;其指向关系见书p94图8.3我们可以说:指针p指向了整型变量k,和指针q指向了整型变量h。
即:指针变量p中存放了整型变量k所在的存储单元地址,和指针变量q中存放了整型变量h所在的存储单元地址。
取地址运算符(&)只能够用于变量和以后将要介绍的数组元素,不能够应用于表达式、常量或者被说明为register的变量(第12章介绍)。
另外,&符号必须要放在运算对象的左边,而且运算对象的类型必须与指针变量的基本类型相同。
在调用scanf函数时,我们输入的各变量之前必须要加&符号。
如:scanf (“%d%c%f”, &a, &b, &c );2、通过指针变量获得地址值我们可以通过赋值,把一个指针变量中地址赋给另一个指针变量,从而使两个指针变量指向同一个地址。
如:int k=80, *p, *q;p=&k; q=p;此时,指针变量p 和q都同时指向了整型变量k 所在的内存单元地址。
但是要注意,进行赋值运算时,赋值号两边指针变量的基本类型必须要相同。
二、给指针变量赋“空”值有时我们还需要给指针变量赋“空”值,形如:p=NULL;NULL是在”stdio.h”头文件中定义的预定义符号,因此在使用NULL时,应在程序的前面出现预定义行:# include “stdio.h”。
NULL的ASCII代码值为0,当执行了以上的赋值语句后,称指针p为空指针。
因此上述语句也可以书写为:p=’\0’;或p=0;这时p并不指向地址为0的存储单元,而是具有一个确定的值——“空”。
8.4 对指针变量的应用一、通过指针来引用一个存储单元C语言提供了一个可以间接访问地址的单目运算符:星号“*”。
当指针变量中存放了一个确定的地址值时,就可以使用指针访问运算符对存储单元中的数据进行访问。
如:int *p, i=10, j;p=&i;则以下赋值语句:j=*p;的作用是将变量i 的值赋予变量j,这里*p 代表指针p 所指向的变量i。
等价于j=i;间接地址访问运算符是一个单目运算符,必须出现在运算对象的左边,其运算对象或者是存放地址的指针变量或者是内存单元地址。
例如:j=*(&i);由于运算符*和&的优先级别相同,且结合方向为自右至左,因此表达式中的括号可以省略。
写成:j=*&i;又例如:j=*p+1;由于*的运算优先级高于+,因此语句功能为:取指针变量p所指向的存储单元中的内容并加1后送给变量j。
有例如:int *p, k=0;p=&k;则语句*p=100;的作用是将整数100送给变量k。
等价于k=100。
此后若有语句*p=*p+1;则功能为:取指针变量p中所指存储单元中的数据,加1后再存放到指针变量p 所指的存储单元中。
相当于k++或k=k+1;以上语句还可以书写成:*p+=1;或++*p;或(*p)++;注意:在表达式+ + * p中,++和*两个运算符的优先级相同,但按照自右至左的方向结合,因此++*p等价于++(*p)。
而在表达式(*p)++中,一对括号不可以缺少,(*p)++表示先取指针变量p所指向的存储单元中的值,然后加1作为表达式的值。
不可以写成*p++,否则将先计算*p作为表达式的值(在此为100),然后使指针变量p自身增1,所以*p++并不使p所指内存单元的数据加1,而是将指针向后移动了一个存储单元。
例如:用指针指向两个变量,通过指针运算选出值大的那个数据。
# include “stdio.h”main ( ){ int a, b,max, *pa, *pb, *pmax;pa=&a; pb=&b; pmax=&max;printf (“input a and b:”);scanf (“%d%d”, &a,&b); // scanf (“%d%d”, pa,pb);printf ( “a=%d b=%d\n”, a, b);*pmax=*pa;if ( *pa<*pb ) *pmax=*pb;printf ( “max=%d\n”, max );//printf ( "max=%d\n", *pmax );}二、移动指针所谓移动指针就是对指针变量加上或减去一个整数,或通过运算使指针变量指向其他存储单元。
因此,只有当指针指向一个连续的存储单元时,指针的移动才有意义。
当指针指向一个连续的存储单元时,即可以对指针变量进行加减运输,也可以对指针进行逻辑和其它C语言支持的运算。
例如:假定在内存中开辟了下列6个int型存储单元,如下表所示编号a[0] a[1] a[2] a[3] a[4] a[5]数据地址2001H 2003H 2005H 2007H 2009H 200BH 指针p↑q↑则分别执行下列语句后,运行结果分别是什么?(1)p++; (2)q++; (3)q=p+2; (4)a=q-p;(5)a=*p; (6)a=*q; (7)a=*(p+2) (8)a=*p++;(9)a=*p+2; (10)a=p<=q;用下列程序进行演示# include “stdio.h”main ( ){ int a[6], a,, *p, *q;a[0]=11; a[1]=22; a[2]=33; a[3]=44; a[4]=55; a[5]=66;p=&a[1]; q=&a[4];带入上面语句printf ( 根据情况进行打印操作); }通过上面例子我们可以知道,指针的运算是以存储单元为基本单位的,而不是以字节为基本单位的,因此,不同类型的数据(int、char、float、double)存储单元是不一样的。
且当指针进行比较运算时,是以其所指向的地址值的大小作为依据的。
8.5 函数之间地址值的传递一、形参为指针变量时实参和形参之间的数据传递若函数的形参为指针变量,在调用该函数时,对应的实参也必须是基本类型相同的地址值或是已经指向某个存储单元的指针变量。
例如:从键盘输入两个数据,在子函数中完成数据交换,主函数与子函数之间的数据传递以地址的形式进行。
# include “stdio.h”main( ){ void swap(int * ,int * ); /* 函数声明,参数是指针类型*/ int a,b;printf("Please input two integer numbers(a,b):");scanf("%d,%d",&a,&b);swap(&a,&b); /* 引用调用 */printf("a=%d\tb=%d\n",a,b); /* 调用swap( )后a 与b 的值 */ }void swap(int *x,int *y){ int temp;temp=*x;*x=*y;*y=temp; /* 数据值交换,而不是地址交换*/ printf("x=%d\ty=%d\n",*x,*y);}在程序执行过程中,主函数调用swap 函数时,系统自动为swap 函数的形参x 和y 开辟了两个类型为int 型的指针变量,并通过实参&a 和&b 将变量a 和b 的地址传递给它们,如下图所示,这时,指针x 指向变量a ,指针y 指向变量b 。