C语言(十五)指针

一、指针

1. 指针的重要性

  表示一些复杂的数据结构

  快速的传递数据,减少了内存的耗用

  使函数返回一个以上的值

  能直接访问硬件

  能够方便的处理字符串

  是理解面向对象语言中引用的基础

  

  总结:指针是C语言的灵魂

2. 指针的定义

  地址

    内存单元的编号

    从0开始的非负整数

    范围:4G [0--4G-1]

  指针

    指针就是地址,地址就是指针
    地址就是内存单元的编号
    指针变量是存放地址的变量
    指针和指针变量是两个不同的概念
    但是要注意通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样

    指针的本质就是一个操作受限的非负整数

    

    

 

3. 指针的分类

   1. 基本类型指针

   2. 指针和数组

      指针和一维数组

        一维数组名

          一维数组是个指针常量

          他存放的是一维数组第一个元素的地址

      下标和指针的关系

          如果p是个指针变量,则

            p[i] 永远等价于 *(p+i)

      确定一个一维数组需要几个参数(如果一个函数要处理一个一维数组,则需要接收该数组的哪些信息)

        需要两个参数:

            数组第一个元素的地址

            数组的长度

        

      指针变量的运算

          指针变量不能相加 不能相乘 也不能相除

          如果两个指针变量只想的是同一块连续的空间中的不同的存储单元

          则两个指针变量才可以相减

      一个指针变量到底占几个字节

          预备知识:

              sizeof(数据类型)

              功能:返回值就是该数据类型所占的字节数

              例子: sizeof(int) = 4 sizeof(char) = 1

                  sizeof(double) = 8

              sizeof(变量名)

               功能:返回值是该变了所占的字节数

          

          假设p指向char类型变量(1个字节)

          假设q指向int类型变量(4个字节)

          假设r指向double类型变量(8个字节)

          p   q   r本身所占的字节数是否一样

            答案: p q r 本身所占的字节都是一样的

           总结:一个指针变量,无论它指向的变量占几个字节

              该指针变量本身只占四个字节

              一个变量的地址,是用该变量的地址使用该变量首字节的地址来表示。

    指针和二维数组

   3. 指针和函数

   4. 指针和结构体

   5. 多级指针

 

专题:

    动态内存分配(重点难点)

        传统数组的缺点:

          1.  数组的长度必须事先制定,且只能是常整数,不能是变量

             例子:

                int a[5]; //Ok

                int len = 5; int a[len]; //error

           2.  传统形式定义的数组,该数组的内存程序员无法手动释放

           在一个函数运行期间,系统为该函数中数组所分配的空间

           会一直存在,直到该函数运行完毕时,数组的空间才会被

           系统释放  

          3.   数组的长度一旦定义,其长度就不能再更改

            数组的长度不能在函数运行的过程中动态的扩充或缩小

          4.     A函数定义的数组,在A函数运行期间可以被其他函数使用,

            但A函数运行完毕之后,A函数中的数组将无法在被其他函数使用。

            

          传统方式定义的数组不能跨函数使用

 

 

        为什么需要动态内存

          动态数组很好的解决了传动数组的这4个缺陷

          传动数组也叫静态数组

        动态内存分配举例_  动态数组的构造

        静态内存和动态内存的比较

          静态内存是由系统自动分配,由系统自动释放

          静态内存是在在栈分配的

 

          动态内存是由程序员手动分配,手动释放

          动态内存是在堆分配的

 

        跨函数使用内存的问题

 

4. *的含义

  1. 乘法

  2. 定义指针变量

    int * p;(int * p 和 *p = 5    此时*号的含义不一样)

    //定义了一个名字叫p的变量, int * 表示p只存放变量的地址

  3. 指针运算符

    该运算符放在已经定义好的指针变量的前面

    如果p是一个已经定义好的指针变量

    则 *p表示以p的内容为地址的变量

 

5. 如何通过被调函数修改主调函数普通变量的值

  1. 实参必须为该普通变量的地址

  2. 形参必须为指针变量

  3. 在被调函数中通过

      *形参名 = .....

  的方式就可以修改主调函数相关变量的值

 

 

二、指针简单介绍

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main()
 4 {
 5     int *p;  //p是变量的名字,int * 表示p变量存放的是int类型的地址
 6     int i = 3;
 7     
 8     p = &i;     //OK
 9     //p = i;    //error,因为类型不一致,p只能存放int类型变量的地址,不能存放int类型变量的值
10     //p = 55; //error,原因同上。
11     system("pause");
12     return 0;
13 }
指针用法_1  
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main()
 4 {
 5     int * p;  //p是变量的名字,int * 表示p变量存放的是int类型的地址
 6             //int *p ; 不表示定义的一个名字叫做*p的变量
 7             //int *p ; 应该这样理解:p是变量名,p变量的数据类型是int *类型
 8             //            所谓int * 类型 实际就是存放int变量地址的类型
 9     int i = 3;
10     int j;
11         
12     p = &i;     
13     /*
14         1. p保存的i的地址,因此p指向i
15         2. p不是i,i也不是p,更准确的说:修改p的值不影响i的值,修改i的值也不影响p的值
16         3. 如果一个指针变量指向某个普通变量,则*指针变量 就完全等同于 普通变量
17         例子:
18             如果p是个指针变量,并且存放了普通变量i的地址
19             则p指向了普通变量i
20             *p 就完全等同于 i
21             或者说:在所有出现*p的地方都可以替换成i
22                     在所有出现i的地方都可以替换成*p
23                     
24                     
25             *p就是以p的内容为地址的变量
26         
27     */
28     j = *p; //等价于 j = i;
29     printf("i = %d, j = %d\n",i,j);
30     system("pause");
31     return 0;
32 }
33 
34 /*
35     指针就是地址,地址就是指针
36     地址就是内存单元的编号
37     指针变量是存放地址的变量
38     指针和指针变量是两个不同的概念
39     但是要注意通常我们叙述时会把指针变量简称为指针,实际它们含义并不一样
40 
41 */
指针用法_2 
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main()
 4 {
 5     int *p;
 6     int i = 5;
 7     
 8     p = &i;  //没有这一步 这个程序就是错误的
 9     *p = i;
10     printf("%d\n",*p);
11     system("pause");
12     return 0;
13 }
指针的常见错误_1 
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main()
 4 {
 5     int * p;
 6     int * q;
 7     int i = 5;
 8     
 9     p = &i;
10     //*q = p  //error 语法编译会出错
11     //*q = *p //error
12     p = q; //q是垃圾值,q赋给p,p也变成垃圾值
13     printf("%d\n",*q);
14      /*
15         q的空间是属于本程序的,所以本程序可以读写q的内容,
16         但是如果q内部是垃圾值,则本程序不能读写*p的内容
17         因为*q所代表的内存单元的控制权限并没有分配给本程序
18         所以本程序运行到13行就会立即出错
19      */
20     system("pause");
21     return 0;
22 }
指针的常见错误_2
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void huhuan(int a, int b )
 5 {
 6     int t;
 7     
 8     t = a;
 9     a = b;
10     b = t;
11     
12     return;
13 }
14 
15 
16 int main()
17 {
18     int a = 3;
19     int b = 5;
20     
21     huhuan(a,b);
22     
23     printf("%d %d",a,b);
24     
25     system("pause");
26     return 0;
27 }
28 /*
29 
30     这个程序是错误的
31 */
互换两个数字_1
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 //不能完成互换功能
 4 void huhuan(int * p , int * q )
 5 {
 6     int * t; //如果要互换p和q的值,则t必须是int *,不能是int,否则会出错
 7     
 8     t = p;
 9     p = q;
10     q = t;
11     
12     
13     return;
14 }
15 
16 
17 int main()
18 {
19     int a = 3;
20     int b = 5;
21     
22     huhuan(&a,&b); //huhuan_2(*p,*q);是错误的,huhuan(a,b);也是错误的
23     
24     printf("%d %d",a,b);
25     
26     system("pause");
27     return 0;
28 }
29 /*
30 
31     这个程序是错误的
32 */
互换两个数字_2
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 void huhuan(int * p , int * q )
 4 {
 5     int t;  //如果要互换*p和*q的值,则t必须的定义成int,不能定义成 int*,否则语法会出错
 6     
 7     t = *p;   //p是int *, *p是int
 8     *p = *q;
 9     *q = t;
10     
11     
12     return;
13 }
14 
15 
16 int main()
17 {
18     int a = 3;
19     int b = 5;
20     
21     huhuan(&a,&b); //huhuan_2(*p,*q);是错误的,huhuan(a,b);也是错误的
22     
23     printf("%d %d",a,b);
24     
25     system("pause");
26     return 0;
27 }
28 /*
29     这个程序是正确的
30 */
互换两个数字_3 
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main(void)
 4 {
 5     int * p; //等价于int *p; 也等价于 int* p;
 6     int i = 5;
 7     char ch = 'A';
 8     
 9     p = &i;  // *p 以p的内容为地址的变量
10     *p = 99;
11     printf("%d\n",i);
12     //p = &ch; //error 不能是字符变量地址
13     //p = ch; //error
14     // p = 5; //error
15     
16     system("pause");
17     return 0;
18 }
基本类型指针 
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 //f函数可以输出任何一个一维数组的内容
 5 void f(int * pArr, int len)
 6 {
 7     int i ;
 8     
 9     for(i=0; i<len; ++i)
10         printf("%d ",*(pArr+i)); //*pArr *(pArr+1) *(pArr+2)
11     printf("\n");
12 }
13 int main(void)
14 {
15     int a[5] = {1,2,3,4,5,6};
16     int b[6] = {-1,-2,-3,-4,5,-5};
17     int c[100] = {1,99,22,33};
18     
19     f(a,5);
20     f(b,6);
21     f(c,100);
22      
23     system("pause");
24     return 0;
25 }
一维数组指针_1 
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void f(int * pArr, int len)
 5 {
 6     int i;
 7     for(i=0; i<len; ++i)
 8         printf("%d ",pArr[i]); //*(pArr+i0等价于 pArr[i] 也等价于b[i] 也等价于 *(b+i)
 9 }
10 int main(void)
11 {
12     int b[6] = {1,2,3,4,5,6};
13     f(b,6);
14     b[i]
15     system("pause");
16     return 0;
17 }
一维数组指针_1 
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 int main(void)
 5 {
 6     int i = 5;
 7     int j = 10;
 8     int * p = &i;
 9     int * q = &j;
10     int a[5];
11     p = &a[1];
12     q = &a[4];
13     printf("p和q所指向的单元相隔%d个单元\n",q-p);
14     system("pause");
15     return 0;
16 }
指针变量运算
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 int main()
 4 {
 5 
 6     char ch = 'A';
 7     double x = 66.6;
 8     int i = 99;
 9     char *p = &ch;
10     int *q = &i;
11     double *r = &x;
12     printf("%d  %d  %d",sizeof(p),sizeof(q),sizeof(r));
13    
14      system("pause");
15     return 0;
16 }
指针占用多少个字节
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 void g(int * pArr, int len)
 4 {
 5     pArr[2] = 88;    //pArr[2] == a[2]
 6 }
 7 
 8 void f(void)
 9 {
10     int a[5] = {1,2,3,4,5}; //20个字节的存储空间程序员无法手动编程释放它
11                             //它只能在本函数运行完毕时由系统自动释放                    
12     g(a,5);
13     printf("%d\n",a[2]);
14 }
15 
16 int main(void)
17 {
18 
19     f();
20      system("pause");
21     return 0;
22 }
静态数组
 1 /*
 2     malloc 是 memory(内存) allocate(分配)的缩写
 3 */
 4 
 5 #include <stdio.h>
 6 #include <stdlib.h>
 7 #include <malloc.h>  //不能省
 8 
 9 int main(void)
10 {
11     int i = 5; //分配的4个字节 静态分配
12     int * p = (int *)malloc(4);
13         /*
14             1. 要使用malloc函数,必须添加malloc.h,这个头文件
15             2. malloc函数只有1个形参,并且形参是整数类型
16             3. 4表示请求系统为本程序分配4个字节
17             4. malloc函数只能返回第一个字节的地址
18             5. 12行分配了8个字节,p变量占4个字节,p指向的内存也占4个字节
19             6. p本身所占的内存是静态的分配的,p所指向的内存是动态分配的
20         */
21     *p = 5; //*p 代表的就是一个int变量,只不过*p这个整型变量的内存分配方式和11行的i变量分配方式不同
22     free(p); //free(p)表示把p所指向的内存释放掉,p本身的内存是静态对,不能有程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放
23     printf("同志们好!\n");
24      system("pause");
25     return 0;
26 }
malloc函数的用法_1 
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h> 
 4 
 5 void f(int * q)
 6 {
 7     //*p = 200; //error
 8     //q = 200; //error
 9     *q = 200;
10     //free(q); //把q所指向的内存释放掉 , 本语句必须得注释掉,否则会导致第20行的代码出错       
11 }
12 int main(void)
13 {
14     int *p = (int *)malloc(sizeof(int)); //sizeof(int)返回值是int所占的字节数
15     *p = 10;
16     
17     printf("%d\n",*p);
18     f(p);  //p是int *类型
19     printf("%d\n",*p);
20     
21     system("pause");
22     return 0;
23 }
malloc函数的用法_2
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h> 
 4 
 5 int main(void)
 6 {
 7     int a[5]; //如果int占4个字节的话,则本数组总共包含20个字节,每四个字节被当作一个int变量来使用
 8     int len;
 9     int * pArr;
10     int i;
11     
12     //动态的构造一维数组
13     printf("请输入你要存放的元素的个数:");
14     scanf("%d",&len);
15     pArr = (int *)malloc(4*len); //本行动态的构造了一个一维数组,该一维数组的长度是len ,该数组的数组名是pArr,该数组的每个元素是int类型,类似于 int pArr[len];
16    
17    //对一维数组进行操作
18    //对动态一维数组进行赋值  如:对动态一维数组进行赋值
19     for(i=0; i<len; ++i)
20         scanf("%d",&pArr[i]);
21         
22     //对一维数组进行输出
23     printf("一维数组的内容是:\n");
24     for(i=0; i<len; ++i)
25         printf("%d\n",pArr[i]);
26     
27     free(pArr); //释放掉动态分配的数组
28     system("pause");
29     return 0;
30 }
动态内存分配举例
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h> 
 4 
 5 int main(void)
 6 {
 7     int i = 10;
 8     int * p = &i;
 9     int ** q = &p;
10     int *** r = &q;
11     
12     //r = &p; //因为r是int ***类型,r只能存放int **类型变量的地址
13     printf("i = %d\n",***r);
14     
15     system("pause");
16     return 0;
17 }
多级指针 
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h>
 4 
 5 void f(int *** r)
 6 {
 7     int i = 15;
 8     **r = &i;
 9 
10 }
11 int main(void)
12 {
13     int i = 10;
14     int * p = &i;
15     int ** q = &p;
16     
17     printf("%d\n",**q);
18     
19     f(&q);
20     printf("%d\n",**q);
21     system("pause");
22     return 0;
23 }
使用多级指针_1
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h>
 4 
 5 void f(int ** q)
 6 {
 7     
 8     **q = 1000;
 9 }
10 
11 void g()
12 {
13     int i = 10;
14     int * p = &i;
15     f(&p);
16     printf("%d\n",*p);
17 }
18 int main(void)
19 {
20     g();
21     system("pause");
22     return 0;
23 }
使用多级指针_2
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h>
 4 
 5 void f(int ** q) //q是个指针变量,无论q是什么类型的指针变量,都只占4个字节
 6 {
 7     int i = 5;
 8     //*q等价于p q和**q都不等价于p
 9     //*q = i; //error 因为*q = i; 等价于 p = i;这样写是错误的
10     *q = &i; //p = &i;
11 }
12 int main(void)
13 {
14     int *p;
15     f(&p);
16     // 15行已经把f()给释放了
17     printf("%d\n",*p);  //本语句语法没有问题,但逻辑上有问题。
18     
19     system("pause");
20     return 0;
21 }
静态内存不可以跨函数使用
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <malloc.h>
 4 
 5 void f(int ** q) 
 6 {
 7     *q = (int *)malloc(sizeof(int)); //sizeof(数据类型)返回值是该数据类型所占的字节
 8                 //等价于 p = (int *)malloc(sizeof(int));
 9     //q = 5;
10     //*q = 5; //error p = 5;
11     **q = 5;//*p = 5;
12 }
13 int main(void)
14 {
15     int *p;
16     f(&p);
17 
18     printf("%d\n",*p); 
19     
20     system("pause");
21     return 0;
22 }
动态内存可以跨行数使用

 

 

 

 

 

posted @ 2013-07-06 21:14  tongtian  阅读(247)  评论(0编辑  收藏  举报