zoukankan      html  css  js  c++  java
  • c语言学习笔记(9)——指针

    指针是c语言的灵魂
    -----------------------------------------------------------------------------
    # include <stdio.h>


    int main(){
    int *p;  //p是变量名,int *表示p变量存放的是int类型变量的地址,p是一个指针变量
    int i = 3;

    //p = i; 这样写是错误的
    //p = 4; 这样写是错误的
    p = &i;  //将i变量的地址给p变量
    //p保存了i的地址,因此p指向i,修改p的值不影响i的值,修改i的值也不影响p的值

    return 0;
    }
    -----------------------------------------------------------------------------
    # include <stdio.h>


    int main(){
    int *p; //不表示定义了一个名字叫做 *p的变量
    //应该这样理解:p是变量名,p变量的数据类型是 int *类型
    //int *类型实际就是存放int变量地址的类型


    int i = 3;

    p = &i;

    printf("*P = %d ", *p);
    printf("i = %d ", i);
    return 0;
    }
    1.如果p是个指针变量,并且p存放了普通变量i的地址,则p指向了普通变量
    2.*P 完全等同于 普通变量i  (有i出现的地方都可以替换成*p)
    ------------------------------------------------------------------------------
    指针和指针变量的区别:
    1.指针就是地址,地址就是指针
    地址就是内存单元的编号,所以指针就是内存单元的编号
    2.指针变量存放地址的变量
    指针变量是存放指针的变量,也就是说指针变量是存放内存单元编号的变量
    3.指针和指针变量是两个不同的概念
    但是要注意通常我们叙述时会把指针变量简称为指针,实际它们的含义不一样
    一、指针的重要性
    1.表示一些复杂的数据结构
    2.快速的传递数据
    3.使函数返回一个以上的值(函数只能返回一个值)
    ---------------------------------------------------------
    # include <stdio.h>


    int f(int a, int b);
    void g(int * p ,int * q);


    int main(void){

    int a = 100;
    int b = 200;

    // a = f(a, b);
    g(&a, &b);
    printf("a = %d, b = %d ", a, b);

    return 0;
    }
    //只能修改一个值
    int f(int a, int b){

    return 1;
    }
    //这样被调函数可以修改主调函数一个以上的值
    void g(int * p ,int * q){
    *p = 1;
    *q = 2;
    }
    ---------------------------------------------------------
    4.能直接访问硬件
    5.能够方便的处理字符串
    6.是理解面向对象语言中引用的基础
    总结:指针是c语言的灵魂,是和其他语言的区别
    二、指针的定义
    地址
    内存单元的编号,地址是从零开始的非负整数。
    范围:
         控制总线
    CPU <---->    数据总线  <----->  内存条
         地址总线
    控制线控制数据传输的方向 
    数据线是传输数据
    地址线是确定是控制哪个内存单元

    cup<----->  数据总线 <-----> 内存条
    一根线控制两个 0和1
    两根线控制四个
    n根线控制2的n次方个单元(字节)(每个单元是8位)

    32位机 2x10^32
    1G=2X10^30B(字节)
    2x10^32 ~= 2X10^30*4  所以内存最大4G
    4G  [0——4G-1]
    指针
    指针就是地址,地址就是指针,指针变量就是存放内存单元编号的变量。
    指针和指针变量是两个不同的概念
    指针本质就是一个操作受限(不能运算,只是编号)的非负整数(地址)
    三、指针的分类
    1.基本类型指针
     //就是上面的代码
    --------------------------------------------------------------------------
    指针常见错误1
    # include <stdio.h>


    int main(void){
    int *p;  //p指向一个垃圾值的地址,也就是一个垃圾地址
    int i = 5;

    *p = i;  // 就是将i的值给了一个不知道的地址,这样写不对
    printf("%d ", *p);

    return 0;
    }
    常见错误2
    # include <stdio.h>


    int main(void){
    int i = 5;
    int *p;
    int *q;

    p = &i
    //*q = p;  语法编译出错
    //*q = *p;  error  q指向一个垃圾地址
    q = p;  // error 可以读 q里面的垃圾地址,但是不能读*q的值,没有控制权限。
    printf("%d ", *q);

    return 0;
    }
    --------------------------------------------------------------------------
    /*
    一个经典指针程序
    */
    # include <stdio.h>


    void huhuan(int i, int j);
    void zhizhenhuhuan(int * a, int * b);
    void huhuan3(int * a, int * b);


    int main(void){
    int a = 3;
    int b = 5;

    // huhuan(a, b);
    // zhizhenhuhuan(&a, &b);
    huhuan3(&a, &b);


    printf("a = %d,b = %d ", a, b);


    return 0;
    }


    void huhuan(int a, int b){  //不能完成互换,a,b是形参,单独分配内存
    int t;

    t = a;
    a = b;
    b = t;
    }


    void zhizhenhuhuan(int * a, int * b){ //不能完成互换,互换了指针的指向
    int * t;


    t = a;
    a = b;
    b = t;
    }


    void huhuan3(int * a, int * b){  //可以完成互换,传递的是地址,交换的是地址指向的值
    int t;


    t = *a;
    *a = *b;
    *b = t;
    }
    ---------------------------------------------------------------------------
    *的含义
    1.乘法   c = a*b;
    2.定义指针变量 int * p;
    3.取值运算符 *p
    ---------------------------------------------------------------------------
    2.指针和数组的关系
     指针和一维数组
    数组名
    一维数组名是个指针常量
    它存放的是数组第一个元素地址
    ----------------------------------------------------------------
    int a[5];
    int b[5];
    a = b 是错误的  a,b都是常量
    -----------------------------------------------------------------
    # include <stdio.h>


    int main(void){
    int a[5];

    printf("%#x ", &a[0]);
    printf("%#x ", a);


    return 0;
    }
    输出结果:0x12ff6c
     0x12ff6c
    ----------------------------------------------------------------
    下标和指针的关系
    如果p是个指针变量,则
    p[i]永远等价于 *(p+i)
    确定一个一维数组需要两个参数
    数组第一个元素的地址
    数组的长度
    -------------------------------------------------------------------------
    //f函数可以输出任何一个一维数组的内容
    # include <stdio.h>


    void f(int * pArr, int len){
    int i;
    for (i=0; i<len; i++)
    printf("%d  ", *(pArr+i));
    printf(" ");
    }
    int main(void){
    int a[5] = {1, 2, 3, 4, 5};
    int b[6] = {-1, -2, -3, 4, 5, -6};
    int c[100] = {1, 99, 22, 33};

    f(a, 5); //确定一个数组:数组首地址和长度
    f(b, 6);
    f(c, 100);

    return 0;
    }
    -------------------------------------------------------------------------
    # include <stdio.h>


    void f(int * pArr, int len){
    pArr[3] = 88;  //pArr[3]等价于a[3]也等价于*(a+3)和*(pArr+3) 
      // *a==a[0]
    }


    int main(void){
    int a[6] = {1, 2, 3, 4, 5, 6};

    printf("%d ", a[3]);
    f(a, 6);  // a和pArr都指向数组的第一个元素
    printf("%d ", *(a+3));

    return 0;
    }
    -------------------------------------------------------------------------
    指针变量的运算
    指针变量不能相加,不能相乘,也不能相除(这些运算没有意义)
    如果两个指针变量指向的是同一块连续空间中得不同的存储单元
    则这两个指针才可以相减(这样减才有意义)
    ------------------------------------------------------------------------------
    # include <stdio.h>


    int main(void){
    int i = 5;
    int j = 10;
    int * p = &i;
    int * q = &j;
    //此时p和q不能相减
    int a[5];
    p = &a[2];
    q = &a[4];


    printf("相减的结果为:%d ", p-q);
    //此时p和q可以相减,相减的值指p和q单元相隔的个数


    return 0;
    }
    结果为:相减的结果为:-2
    ---------------------------------------------------------------------------------
    一个指针变量到底占几个字节
    预备知识:
    sizeof(数据类型);或者 sizeof(变量名);
    返回该数据类型所占的字节数
    例如: sizeof(int) = 4 sizeof(char) = 1
    假设p指向的char类型变量(1个字节)
    假设p指向的int类型变量(4个字节)
    假设p指向的double类型变量(8个字节)
    p q r 本身所占的字节数是否一样
    ---------------------------------------------------------------------------
    # include <stdio.h>


    int main(void){
    char ch = 'A';
    int i = 90;
    double x = 66.6;
    char * p = &ch;
    int *q = &i;
    double *r = &x;

    printf("%d  %d  %d ", sizeof(p), sizeof(q), sizeof(r));
    }
    输出的结果:4  4  4
    内存中一个字节为一个单元,所以double中有8个编号,
    只用首字节的编号代表该变量的内存编号,用该变量类型
    指明下面连续单元的个数
    如: int * p;  int i = 5; p = &i;
    p------>地址1101011(i的首地址)
    int-------表示有四个字节
    ----------------------------------------------------------------------------
     指针和二维数组
    3.指针和函数的关系

    4.指针和结构体的关系

    5.多级指针
    ----------------------------------------------------------------------------------
    # include <stdio.h>


    int main(void){
    int i = 10;
    int * p = &i;
    int ** q = &p;
    int *** r = &q;

    printf("i = %d ", ***r);


    return 0;
    }
    结果: i = 10
    -----------------------------------------------------------------------------------
    专题:
    动态内存分配
    传统数组的缺点
    1.数组的长度必须事先制定,且只能是常整数,不能是变量
     例如: int len = 5; int a[len];  //error
    2.传统形式定义的数组,该数组的内存程序员无法手动释放
    ------------------------------------------------------------------------------
    # include <stdio.h>


    void f(void){
    int a[5] = {1, 2, 3, 4, 5};
    //这二十个字节的存储空间程序员无法手动编程释放它
    //只能在本函数运行完毕时由系统自动释放
    }


    int main(void){
    return 0;
    }
    ------------------------------------------------------------------------------
    3.数组的长度不能再函数运行的过程中动态的扩充或缩小
    4.A函数定义的数组,在A函数运行期间可以被其它函数使用,
     但A函数运行完毕之后,A函数中的数组将无法在被其它函数使用
     静态数组不能跨函数使用
    -------------------------------------------------------------------------
    # include <stdio.h>
    void g(int *pArr, int len){
    pArr[2] = 88;
    }
    void f(void){
    int a[5] = {1, 2, 3, 4, 5};  //f运行期间g();函数可以使用
    g(a, 5); //当f运行完毕数组a空间被释放
    printf("%d ", a[2]);
    }


    int main(void){
    return 0;
    }
    --------------------------------------------------------------------------
    为什么需要动态分配
    动态数组很好的解决了传统数组的上面四个缺陷
    动态内存分配的举例——动态数组的构造
    ----------------------------------------------------------------------------
    /*
    2012年2月5日15:00:25
    malloc 是 memory(内存) allocate(分配)的缩写
    */
    # include <stdio.h.
    # include <malloc.h>  //头文件


    int main(void){
    int i = 5; //静态分配了四个字节
    int * p = (int *)malloc(4);  //把返回的地址强制转换为整形变量的地址
    /*
    1.要使用malloc函数,必须添加malloc.h这个头文件
    2.malloc函数只有一个形参,并且是整型
    3.4表示请求系统为本程序分配4个字节
    4.malloc函数只能返回第一个字节的地址
    5.上面一行代码分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
    6.p本身所占的内存是静态分配的,p所指向的内存是动态分配的
    */
    *p = 5; //*p代表整形变量,只不过*p这个整形变量的内存分配方式和i分配不同
    free(p);  //表示把p所指向的内存给释放掉
    printf("同志们好! ");

    return 0;
    }
    ----------------------------------------------------------------------------
    # include <stdio.h>
    # include <malloc.h>


    void f(int * q){
    *q = 200;
    free(q);  //把q指向的内存释放掉 和 free(p)等价
    }
    int main(void){
    int * p = (int *)malloc(sizeof(int));
    *p = 10;

    printf("%d ", *p);   //10
    f(p);  //通过f()修改p指向内存的值
    printf("%d ", *p); //p指向的内存已经释放,所以会出错

    return 0;
    }
    ----------------------------------------------------------------------------
    /*
    2012年2月5日15:37:36
    动态一维数组示例
    */
    # include <stdio.h>
    # include <malloc.h>


    int main(void){
    int a[5]; //包含20字节
    int i;
    int len;
    int *pArr;

    printf("请输入你要存放的元素个数:");
    scanf("%d", &len);
    pArr = (int *)malloc(4*len);  //pArr指向前4个字节  类似于 int pArr[len];
    // pArr+1 就指向第5到第8个字节
    //动态的构造了一个int类型的一维数组,数组名 pArr
    for (i=0; i<len; i++)  //对一维数组进行赋值
    scanf("%d", &pArr[i]);

    printf("一维数组的内容是: ");
    for(i=0; i<len; i++)
    printf("%d ", pArr[i]);

    return 0;
    }
    输出结果:
    请输入你要存放的元素个数:5
    1 2 3 4 5
    一维数组的内容是:
    1
    2
    3
    4
    5
    ----------------------------------------------------------------------------
    静态内存和动态内存的比较
    静态内存是由系统自动分配,由系统自动释放
    静态内存是在栈中分配的
    动态内存是由程序员手动分配,手动释放
    动态内存是在堆分配的
    跨函数使用内存的问题
    ----------------------------------------------------------------------------
    # include <stdio.h>


    void f(int ** q){
    int i = 5;
    //*q 等价于p q 和**q都不等价于p
    *q = &i;
    }
    int main(void){
    int * p;

    f(&p);  //访问完后释放内存
    printf("%d ", *p); //本语句语法没有问题, 但逻辑上有问题
    //访问了不该访问的内存,f()函数结束后i的空间已经被释放
    //静态变量,不能跨函数使用,当函数结束后变量不能被访问

    return 0;
    }
    ------------------------------------------------------------------------------
    # include <stdio.h>
    # include <malloc.h>


    void f(int **q){
    *q = (int*)malloc(sizeof(int)); //动态分配内存
    //等价于 p = (int *)malloc(sizeof(int));
    **q = 5;
    }


    int main(void){
    int * p;
    f(&p);
    printf("%d ", *p); //f()结束后,p指向的动态内存没有释放

    return 0;
    }
    ----------------------------------------------------------------------------------
  • 相关阅读:
    Chapter 17_1 弱引用table
    Chapter 16_5 单一方法
    Chapter 16_4 私密性
    Chapter 16_3 多重继承
    Chapter 16_2 继承
    Chapter 16_1 Class
    Chapter 16_0 面向对象编程
    小米2s刷机
    Chapter 15_4 子模块和包
    ASP.NET Core MVC 泛型接口的声明调用与注入服务
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6469919.html
Copyright © 2011-2022 走看看