第八章 指针

  1. 8.1 指针是什么
      1. 地址可称为指针,地址指向变量单元;地址包括:位置信息(内存编号或纯地址值),所指向的存储单元的类型信息
      1. 对变量的访问方式:直接访问和间接访问(注意:对变量的访问是通过地址进行的)
    1. 3.(1)直接访问:直接按变量名访问;(2)间接访问:将变量i的地址存放在另一变量中,然后通过该变量来找到变量i的地址,从而访问i(指针法)。
    2. 4. *pointer表示pointer所指向的对象,__“*” 表示指向,通过地址来体现,“&” 取地址符;指针变量:**用来存放变量的地址的变量,指针变量的值地址。
  2. 8.2 指针变量
    1. 8.2.1 定义指针变量
      1. 1.格式:类型名 * 指针变量名;例如:int *pointer ,int 是必须指定的基类型,用来指定此指针变量可以指向何种类型的变量;pointer 为变量名。
      2. 2.注意:① 错误写法:pointer =&a; 应该是赋值给一个变量名。②指针(地址)是有类型的信息,赋值时左右两侧的类型要一,指针的类型要和它所指向的变量类型一致。③ 如何表示指针:指向整型数据的指针表示为 int ,“指向int 的指针”。④ 指针变量只能存放地址。
    2. 8.2.2 怎样引用指针变量
        1. 引用指针变量的三种情况:(1)给指针变量赋值 ,p=&a;(2) 引用指针变量指向的变量 printf(“%d”,*p);(3) 引用指针变量的值-地址,printf(“%d”,p);
        1. 用指针变量作函数参数时同样要遵循单向的值传递方式,调用函数不能改变实参指针变量的值,但是可以改变它所指向的变量的值
  3. 8.3 通过指针引用数组(详见P229)
    1. 8.3.1 数组元素的指针(地址)
      1. 每个数组元素即为单个变量,有相应的地址,指针就可以指向它们(元素的地址存放在指针中)。
      2. 引用数组元素的方法:①下标法(a[ i ]);②指针法 (通过指向元素的指针找到所需的元素)。
    2. 8.3.2 在引用数组元素时的指针运算
      1. 包括:加减一个整数p+1;自增自减 p++ , p—;两个指针相减p2-p1(指针变量p1、p2都指向同一数组的元素)。
      2. 说明:(1)如果指针变量p指向一个元素,p+1 表示指向下一个元素;“加一” :表示加上一个数组元素所占的字节数。(2)如果p的值为&a0, p+i 和a+i就是a[i]的地址;(p+i)和(a+i)表示 p+i ,a+i所指向的数组元素a[i]。(3)如果指针变量p1、p2都指向同一数组的元素,执行p1-p2,结果是p1-p2(地址之差)的值除以数组元素长度。
    3. 8.3.3 通过指针引用数组元素
      1. 方法:两大类:下标法和指针法;三小类:(1)下标法,a[i];(2)通过数组名计算数组元素的地址找出元素(a+i),(3)用指针变量指向元素,(p+i);
      2. 注意:数组a[ ]中的a是数组名也是指针型常量,它的值不可变。
    4. 8.3.4 用数组名作函数参数
      1. 用数组名作函数参数,形参数组中各与元素的值发生变化则实参数组的值也随之变化。
      2. 原因:实参数组名代表数组首元素的地址,而形参是用来接收实参传递来的地址的,形参为一个指针变量或数组名;形参数组与实参数组共同占用一段内存单元。
      3. 注意:在定义函数时可以不指定形参数组X的大小,形参数组为指针变量并未真正开辟数组空间。改变实参数组中元素的值:当实参为数组名\指针变量,形参为数组名\指针变量。但是如果让指针变量作实参要先使它指向一个确定的值。
    5. *8.3.5 通过指针引用多维数组
        1. 多维数组元素的地址(见P244):int a[3][2] ={{1,1},{1,2},{1,3}}; 解释:①二维角度 :a 代表首行的开始地址,a+1代表序号为1的行的起始地址即它指向a[1]; ② 一维数组名a[0],a[1]代表一维数组0列与1列元素的地址即&a[0][0],a[0]+1:第一行第2列的元素的地址;③ a[i] 等价于(a+i),a[0]+1等价于 (a+0)+i =&a[0][i]; ((a+i)+j)=(a[i]+j)=a[i][j]的值。④a[i]的说明,它代表a数组序号为i的存储单元且有确定地址,如果a为二维数组,a[i]表示一维数组名仅仅是一个地址。⑤ 二维数组a中a+1 表示的是数组第二行第一列的起始地址,而*(a+1)等价于a[1]表示数组a 0行1列的地址,指向a[1][0]。
      1. 总结:a+1 与a[0]+1是不同的,a+1指的是序号为1的行(第二行)的起始地址,指向序号为1的行,而(a+0)+1或a[0]+1都指向0行1列元素,二者地址相同当指向的数据类型不同。在指向行的指针前加个 就变为指向列的指针,&a[i]和a+i指向行而 a[i]和*(a+i)指向列。
        1. 指向多维数组的指针变量(P248)
        2. (1)指向数组元素的指针变量 :int p ;printf(“%d”,p);
        3. (2)指向由m个元素组成的一维数组的指针变量:格式 int (*p)[4]指向由4个整型元素组成的一维数组p的类型不是int 而是int ()[4];p=&a[0], p+1指向a[1](第二行)
        4. (3)指向数组的指针作函数参数:用指针变量做形参可接收实参传递的地址 ,① 用指向变量的指针变量 float p,②指向一维数组的指针变量 float (p)[i] 。
  4. 8.4 通过指针引用字符串
    1. 8.4.1 引用字符串的方式
      1. (1)用字符数组存放一个字符串。可以通过数组名和下标引用字符串中一个字符,也可以通过数组名和格式声明符%s输出整个字符串。
      2. (2)用字符指针变量指向一个字符串常量,通过该指针变量引用字符串常量。char str =”I love study”; printf(“%s\n”,str);C语言中对字符串常量按字符数组处理的。在内存中,字符串末尾会自动加一个”\0”即结束符。
    2. 8.4.2 字符指针作函数参数
      1. 把一个字符串从一个函数传递到另一函数,可以用字符数组名作形参传递地址值;也可以用字符指针变量作参数。
      2. (1)用字符数组名作参数:void func(char f[]); char a[] ;func(a); (2)字符指针变量作形参实参:void func(char f); char a={“hello”} ;char *p =a;func(p);
    3. 8.4.3 使用字符指针变量和字符数组的比较
      1. (1)字符数组是由若干元素组成,元素用来存放字符,而字符指针变量中存放的是地址(字符串第一字符的地址)。
      2. (2)赋值:可对字符指针变量赋值,但绝对不可对数组名赋值。
      3. (3)存储单元的内容:编译时为字符数组若干存储单元,而对指针变量只分配一个存储单元。
      4. (4)在定义指针变量后要指定其指向,注意:不能给一个未指定其指向的指针变量赋值。
      5. (5)指针变量的值可以改变,但字符数组名代表的是一个常量(地址)。
      6. (6)字符数组中各元素的值可变,但字符指针变量指向一个字符串**常量的值是不能改变的;若字符指针变量指向字符串常量,则可用变量名+下标**形式引用字符串中的字符。
  5. *8.5 指向函数的指针
    1. 8.5.1 定义:函数在被编译时会将其源码转化为执行代码,并分配一段存储空间,该段存储空间有一个起始地址(函数名或指针);例如:int (*p)(int , int);表示定义一个指向函数的指针变量P,指向函数类型为整型且有两个整型参数的函数。
    2. 8.5.2 用函数指针变量调用函数
      1. (1)通过函数名调用:int max(){ ,, }; max(a,b);
      2. (2)通过指针变量调用它所指向的函数:int (p) (int ,int)// p为指向函数的指针变量,函数的类型是整型(函数返回值),带有形参; p=max ; c= (p) (a,b);// 通过指针变量调用函数;注意:p只能指向函数的入口处,即不能用*(p+1)。
    3. *8.5.3 怎样定义和使用指向函数的指针变量
      1. 定义指针变量的形式:类型名 (*指针变量名) (函数参数表列);
      2. 说明:(1)该指针变量只能指向在定义时指定的类型的函数,在程序中把哪个函数(相同函数值类型和参数表列)的地址赋给它,它就指向哪个函数。(2)在调用函数前,要先使指针变量指向该函数。(3)对指向函数的指针变量不能进行算术运算。
    4. *8.5.4 用指向函数的指针作函数参数
      1. 指向函数的指针变量的作用之一就是把函数的入口地址作参数传递到其他函数,条件是:指向函数的指针作函数参数。
      2. 例:void fun(int (x1)(int), int (x2) (int, int)){ int a, b, i=3 ,j=5 ; a=(x1)(i);b= (x2) (i , j); }//f1,f2的入口地址传递给形参指针变量x1,x2;调用函数x1,x2就是调用函数f1,f2;
  6. *8.6 返回指针值的函数
    1. 一个函数可以返回数值型数据,也可以返回指针型的数据即地址。
    2. 格式:类型名 函数名 (参数表列),int a(int,int);**说明:a(int,int)表示指针型的函数,int 表示返回的指针指向整型变量。*
  7. *8.7指针数组和多重指针
    1. 8.7.1 什么是指针数组
      1. 定义:一个数组中的元素均为指针类型的数据,即数组中的每个元素均为**指针变量**(存放地址)
      2. 定义一维指针数组的格式: 类型名 数组名[ 数组长度 ],int p[ 4 ] 。适合用于指向若干字符串。
    2. 8.7.2 指向指针数据的指针变量(p279)
      1. 就是指向指针的指针,指针数组的每一个元素都是一个指针变量,值为地址。
      2. 例:int *num [3], num+i就是num[i]的地址,它指向指针数组的类型为指针的元素;
      3. 定义 :int *p ; p=name+i ;//定义指向int 型的指针变量p,p指向name[i].注意指针数组的只能存放地址,不能存放数据。
  8. *8.8 动态内存分配和指向它的指针变量
    1. 8.8.1 什么是内存的动态分配
      1. 栈:全局变量是分配在内存的静态存储区,非静态的局部变量包括形参是分配在内存的动态存储区——栈
      2. 堆:一些临时用的数据不必在程序声明部分定义,是需要时才开辟不需要时就释放,存放于内存动态分配区——堆
    2. 8.8.2 如何建立内存的动态分配
      1. 系统库函数实现:malloc, calloc , realloc ,free .
      2. 1.用malloc 函数开辟动态存储区:①函数原型 void malloc (unsiged int size); malloc(100);②说明:在内存中分配一个长度为size大小的连续存储空间。形参size的类型为无符号整型(不能为负数);*此函数值是所分配区域的第一个字节的地址,或者说返回的指针指向所分配域的第一字节。
        1. 用calloc 函数开辟动态存储区:①函数原型:void calloc (unsiged n, unsiged size); calloc(20,4 )//分配20×4个字节的临时分配区 ② 说明 :在内存中分配n个长度为size的连续空间,足够*保存一个数组,用于为一维数组开辟动态存储空间(动态数组);函数返回所分配域的第一字节的指针,分配不成功返回NULL。
        1. 用realloc函数重新分配动态存储区:①函数原型:void realloc(void p, unsiged int size);realloc(p,50)//将p所指向的动态空间改为50字节;② 说明:将p所指向的动态分配空间的大小改为size,p值不变。
        1. 用free 函数释放动态存储区:① void free(void *p); free(p);② 释放指针变量p所指向的动态空间。
    3. 8.8.3 void 指针类型
      1. C99允许使用基类型为void的指针类型,定义一个void *型变量,它不指向任何类型的数据;在将其值赋值给另一指针变量时由系统对其进行类型转换,转换为适合赋值的类型。
      2. 说明:① 地址应包含基类型的信息,否则无法实现对数据的存取,这种无指向的地址标记的存储单元不能存放任何数据。②void func(int );定义一个返回值为void 型的函数,void *型的指针“空类型”指针,该种指针不指向任何类型数据,只提供一个纯地址值。
  9. 8.9有关指针的小结(P288)