• C语言基础(二)——指针


    C语言基础(二)——指针
      iwehdio的博客园:https://www.cnblogs.com/iwehdio/

    导图

    1、指针的概念

    • 取地址运算符 & :获得变量的地址,它的操作数必须是变量。
    • 输出变量 i 的地址:printf("%p", &i)。(int)&i 与取到的地址是否一致取决于编译器位数。
    • 相邻的变量的地址是紧挨着的,但是由于内存模型是堆栈,地址分配是自顶而下的,后面的元素地址反而靠前。
    • & 的结果的地址 sizeof 在32位下是 4,在64位下是 8。
    • 数组的地址:
      • 对于数组 int a[10],a 与 &a 都表示数组a中第一个元素的地址,即 &a[0]。
      • 每两个相邻的数组单元的地址是顺序紧邻的。
    • 指针:保存地址的变量。
      • 指针的定义:int *p = &i,无论 * 号靠近 int 还是靠近 p,表示的都是 p 是指向 int 类型的指针, 变量 p 存储的地址指向变量 i 。
      • 指针变量的值是内存的地址。
      • 指针作为函数的参数时:
        • 函数原型中,指定了参数类型是 指向 int 的指针:void f(int *p)
        • 调用时,传入的要是某个变量的地址:f(&i)
      • 建议在指针变量定义时,赋值为所指向的变量或者指向0。
    • 取内容运算符 * :访问指针的值所表示的地址上的变量的值。
      • 如果指针 p 是指向 int 类型的,则 *p 可以被看作一个整体,这个整体是 int 类型的变量。
      • 可以通过将 *p 作为左值,更改 p 存储的地址指向的变量的值:*p = 新值
      • 左值:出现的赋值号左边的不是变量,而是值,是表达式运算的结果。

    2、指针的使用

    • 指针的应用场景:

      • 需要传入较大的数据时用作参数;
      • 传入数组后对数组做操作;
      • 函数返回不止一个结果;
      • 需要用函数来修改不止一个变量;
      • 动态申请的内存。
    • 指针的使用:

      • 经典的交换两个变量的值:

        void swap(int *a, int *b){
            int t = *a;
            *a = *b;
            *b = t;
        }
        
      • 函数返回多个值,某些值就只能通过指针返回。

        • 传入的参数实际上是需要保存带回的结果的变量。
        • 或者,函数返回运算的状态,结果通过指针返回。
        • C++/Java 会使用异常来处理。
      • 不能对没有设置指向的指针进行操作。

      • 如果在函数声明或指针定义时,* 只是表示所接收的变量或定义的变量是指针类型。这时 * 表示其后定义的变量是指针类型。

      • 如果在函数声明或指针定义以外,* 表示取地址运算符,构成一个整体表示指向的变量内容。

    • 指针与数组:

      • 当数组作为参数传入函数时,实际上传入的是指针。

      • 以下表示在函数的参数列表中是等价的:int *aint *int a[]int []

      • 数组变量是特殊的指针,数组变量本身就表示地址。

      • 但是数组的元素表达的是变量,需要用 & 取地址。

      • * 运算符可以对指针做,也可以对数组做。

      • 数组变量是 const 的指针,所以不能被赋值。

    • 指针与 const :

      • const 表示其修饰的变量是不可改变的。

      • 如果指针是 const :

        • 表示一旦得到了某个变量的地址,不能再指向其他变量。

        • 如:

          int * const q = &i;		//q是const
          *q = 26;			//可以做
          q++;				//不可以做
          
      • 如果指针指向的变量是 const :

        • 表示不能通过指针去修改这个变量。

        • 如:

          const int * p = &i;		//*p是const
          *p = 26;			//不可以做
          i = 26;				//可以做
          p = &j;				//可以做
          
      • 判断哪个被 const 修饰的标志是 const 在 * 的前面还是后面,在星号前边则所指被修饰,在星号后边则指针被修饰。

      • const 数组:

        • 定义格式:const int a[]={1,2,3}
        • 数组变量已经是 const 的指针了,这里的 const 表明数组的每个单元都是 const int。
        • 所以必须通过初始化进行赋值。
        • 因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值。
        • 为了保护数组不被函数破坏,可以设置参数为 const 。int f(const int a[])
    • 转换:总是可以把一个非 const 的值转换成 const 的。

      • 如:

        void f(const int* x);
        //如果传入的不是 const
        int a = 15;
        f(&a);		//在函数f内部转换成const,也就是说在函数f内部不会更改指针所指向的值
        //如果传入的是const
        const int b = a;
        f(&b);
        
      • 当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改。比如传入数据结构时。

    • 指针运算:

      • 对指针加一时,指针存储的地址实际上是加了 sizeof(指针指向的数据类型) 。表示的意义是将指针指向下一个元素。
      • 如:对于 数组 a,char *p = a*p表示 a[0] 时,则 *(p+n)表示 a[n]。
      • 如果指针不是指向一片连续分配的空间如数组,则这种运算没有意义。
      • 指针相减,得到的是两个地址的差除以 sizeof(指针指向的数据类型) 。表示的意义时两个指针之间的元素个数。
      • 经典用法:*p++
        • 取出 p 所指的那个数据来,完事之后顺便把 p 移到下一个位置去。
        • * 的优先级虽然高,但是没有 ++ 高。
        • 常用于数组类的连续空间操作。
        • 在某些CPU上,这可以直接被翻译成一条汇编指令。
      • 指针的比较:
        • 大于、等于、相等,比较的是内存中的地址。
        • 数组中单元的地址是线性递增的。
    • 0 地址:

      • 内存中有0地址,但是0地址通常是个不能随便碰的地址。
      • 所以指针不应该具有0值,因此可以用0地址来表示特殊的事情:
        • 返回的指针是无效的。
        • 指针没有被真正初始化(先初始化为0)。
      • NULL是一个预定义的符号,表示0地址。
    • 指针的类型:

      • 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址。
      • 但是指向不同类型的指针是不能直接互相赋值的,这是为了避免用错指针。
    • 指针的类型转换:

      • void* 表示不知道指向什么东西的指针。
        • 计算时与 char* 相同(但不相通)。
      • 指针可以转换类型:
        • 指针的强制类型转换:int*p=&i;void*q=(void*)p
        • 这并没有改变 p 所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量。
    • 动态内存分配:

      • 导入头文件:#include <stdlib.h>
      • 申请空间:int* a = (int*)malloc(n * sizeof(int))
        • 用指向 int 的指针 a 指向分配到的数组。
        • n * sizeof(int) 表示存储 n 个 int 类型单元的空间。
        • (int*) 表示强制类型转换 从 void* 到 int* 。
      • 将申请到的空间返还给系统:free(a)
        • 只能还申请来的空间的首地址。
      • 向 malloc 申请的空间的大小是以字节为单位的。
      • 返回的结果是 void*,需要类型转换为自己需要的类型。
      • 内存申请失败时会返回 null 。

    参考:C语言程序设计(浙江大学):https://www.bilibili.com/video/av15267247
    iwehdio的博客园:https://www.cnblogs.com/iwehdio/
  • 相关阅读:
    手机自动化
    记录
    Linux 死机了怎么办
    前端源码
    LNMP环境
    PHP学习之路(一)
    py
    蜘蛛问题
    mongodb
    【HTML/XML 2】XML基础知识点总结
  • 原文地址:https://www.cnblogs.com/iwehdio/p/12435967.html
Copyright © 2020-2023  润新知