• 指针


    21.1 没有指针的世界

    以下不能处理的问题:

    • 在呼叫的函式中修改引数
    • 直接复制阵列
    • 直接复制字串
    • 动态改变阵列长度

    在被呼叫的函式中修改引数值

    • 函式呼叫时,引数值会复制一份到被呼叫的函式内做参数使用。在函式内部对参数做任何的变动不会改变到原本的引数值

    指标型别

    指标是一种资料型别,用来储存记忆体位址

    • 一般情况我们是不需要指针的
    • 增加这个型别可以处理的问题

    21.2 指标变数宣告

    • 指标是C语言的主要特征,是种【储存记忆体位址的资料型别】
    • 指标变数宣告语法 

             资料型别    *变数名称

           表示变数内存放的是一个存放这种 【资料型】 值的 【记忆体位址】

    取址运算子 

    变数 依照资料型别会占据一定的记忆体空间。我们可以利用取址运算子(&)去取得变数开头的记忆体位址

     1 int count = 9;
     2 
     3 int a;
     4 int *b;
     5 
     6 a = count;  // (O) (int)   = (int)
     7 b = count;  // (X) (int *) = (int)
     8 
     9 a = &count;  // (X) (int)   = (int *)
    10 b = &count;  // (O) (int *) = (int *)
    11 
    12 要记得型别一致

     21.3 指标间接运算

    间接运算子(*)

    相对地,我们可以利用间接运算子(*)从记忆体位址取得开头位于该位址的变数(别跟宣告指标变数时用的*搞混)

     1 int count = 9;
     2 
     3 int a;
     4 int *b;
     5 
     6 a = count;  // (O) (int)   = (int)
     7 b = count;  // (X) (int *) = (int)
     8 
     9 a = &count;  // (X) (int)   = (int *)
    10 b = &count;  // (O) (int *) = (int *)
    11 
    12 
    13 count = *a;  // (X) 型别不一致
    14 count = *b;  // (O) *b 表示记忆体位址的变数是多少,这里相当于复制一份count给count

    指标的特殊功能

     1 int countA = 9;
     2 int countB = 10;
     3 int *countAddr;
     4 
     5 countAddr = &countA;
     6 
     7 *countAddr = 0;
     8 
     9 
    10 countAddr = &countB;
    11 *countAddr = 0;
    12 
    13 //相等于
    14 
    15 int countA = 9;
    16 int countB = 10;
    17 countA = 0;
    18 countB = 0;

     21.4 更多指标与取址间接运算的细节

    指标型别与取址和间接运算

    • 指标(type*):可储存记忆体位址的型别
    • 取址运算子(&):可取得变数的记忆体起始位址   &变数
    • 间接运算子(*):取得以该记忆体位址起始的变数  *记忆体位址
     1 //取址与间接运算的关系
     2 
     3 int count = 9;
     4 int *countAddr = &count;
     5 
     6 int result = *&count;
     7 
     8 //看到相邻的*&可以抵消
     9 
    10 //所以
    11 
    12 int result = count;

    22 指标与函式呼叫

    函式呼叫的特性

    • 呼叫函式时,作为引数的变数会被复制一份到函式内成为参数。在被呼叫的函式内对参数做任何变动都不会改变到原本的变数
     1 #include <stdio.h>
     2 
     3 void addone(int n) {
     4     n = n + 1;
     5 }
     6 
     7 int main() {
     8     int a = 3;
     9     addone(a);
    10     printf("%d", a);
    11     return 0;
    12 }
    13 
    14 
    15 3
    16 Process returned 0 (0x0)   execution time : 0.976 s
    17 Press any key to continue.

    函式呼叫时复制记忆体位址

    在呼叫函式时,可以将变数的 记忆体位址 作为引数传入函数执行。 此时在函式内部对该参数透过 间接运算子 赋予新的数值时就可以改变原本的变数值。

     1 #include <stdio.h>
     2 
     3 void addone(int *n)
     4 {
     5     *n = *n + 1;
     6 }
     7 
     8 int main()
     9 {
    10     int a = 3;
    11     addone(&a);  /*复制 a 的记忆体位址给 addone*/
    12     printf("%d", a); /* 4 */
    13     return 0;
    14 }
    15 
    16 
    17 4
    18 Process returned 0 (0x0)   execution time : 7.710 s
    19 Press any key to continue.

    22.1 两个变数数值交换(使用函式)

     1 #include <stdio.h>
     2 
     3 void swap(int *, int *);
     4 
     5 int main()
     6 {
     7     int a = 3,b =5;
     8     swap(&a, &b);
     9     printf("a: %d
    ", a);
    10     printf("b: %d
    ", b);
    11     return 0;
    12 }
    13 
    14 void swap(int *a, int *b) {
    15     int t = *a;
    16     *a = *b;
    17     *b = t;
    18 }
    19 
    20 
    21 a: 5
    22 b: 3
    23 
    24 Process returned 0 (0x0)   execution time : 14.025 s
    25 Press any key to continue.

     22.2 两个整数的排序(使用函式)

     1 #include <stdio.h>
     2 
     3 void sort(int *, int *);
     4 void swap(int *, int *);
     5 
     6 int main()
     7 {
     8     int a = 5, b = 3;
     9     sort(&a, &b);
    10     printf("a : %d
    ", a);
    11     printf("b : %d
    ", b);
    12     return 0;
    13 }
    14 
    15 void sort(int *a, int *b)
    16 {
    17     if (*a > *b)
    18     {
    19         swap(&*a, &*b);//swap(a, b)
    20     }
    21 }
    22 
    23 void swap(int *a, int *b) {
    24     int t = *a;
    25     *a = *b;
    26     *b = t;
    27 }
    28 
    29 
    30 a : 3
    31 b : 5
    32 
    33 Process returned 0 (0x0)   execution time : 3.630 s
    34 Press any key to continue.

    22.3 该传变数值还是址

    基本原则

    1. 可以传值就传值
    • 复制一份比较安全,不怕被偷改,确保函式间干净的关系。
    • 用起来比较方便,可以传一般常数。

    例外规则

    • 作为引数的变数在呼叫后值会变动的时候(例如数值交换的范例)
    • 无法直接复制值的时候(例如阵列和字串)
    • 复制成本太高的时候(例如较复杂的结构)

    23.1  指标对整数的加减运算

    1 int v[5];
    2 
    3 &v[0] + 1 == &v[1];
    4 &v[1] + 1 == &v[2];
    5 &v[1] - 1 == &v[0];
    6 
    7 &v[0] + &v[1]    // 编译失败(X)
    8 &v[2] - &v[1] == 1 //从 v[2] 位址到 V[1] 位址距离 1 个元素

    23.2 指标与阵列

    指标与阵列的不同

    1 int v[5]; // 宣告定义一个有 5 个元素的 int 阵列, 占据 5 个 int 大小的记忆体
     1 // 阵列型别可转型为指标
     2 
     3 int v[5]; // 宣告定义一个有 5 个元素的 int 阵列, 占据 5 个 int 大小的记忆体
     4 int *n;   // 宣告定义一个 int 指标, 占据 1 个 int * 大小的记忆体
     5 n = &v[0];
     6           //阵列型别可隐性转型成该阵列第一个元素记忆体位址的指标
     7 n = v;    // 相当于 n = &v[0]
     8 
     9 
    10 // 透过指标运算存取阵列元素
    11 
    12 int v[5];
    13 int *n = v;
    14                          
    15 n   == &v[0];          *n     == v[0];   // *n = 0   等值于 v[0] = 0         
    16 n+1 == &v[1];          *(n+1) == v[1];   // *n(n+1) = 0 等值于 v[1] = 0     
    17 n+2 == &v[2];          *(n+2) == v[2];   // *(n+2)  = 0 等值于 v[2] = 0

     23.3 循序存取阵列元素(使用指标)

     1 #include <stdio.h>
     2 int main()
     3 {
     4     int v[5] = {1, 2, 3, 4, 5};
     5     int *n = v;  // int *n = &v[0];
     6     int i;
     7     for (i = 0; i < 5; i++) {
     8         printf("%d
    ", *(n+i));
     9     }
    10     return 0;
    11 }
    12 
    13 #include <stdio.h>
    14 int main()
    15 {
    16     int v[5] = {1, 2, 3, 4, 5};
    17     int *n = v;              // n == &v[0]
    18     printf("%d
    ", *n);
    19     n++;               // n == &v[1]
    20     printf("%d
    ", *n);
    21     n++;               // n == &v[2]
    22     printf("%d
    ", *n);
    23     n++;               // n == &v[3]
    24     printf("%d
    ", *n);
    25     n++;               // n == &v[4]
    26     printf("%d
    ", *n);
    27     return 0;
    28 
    29 
    30 }
    31 
    32 1
    33 2
    34 3
    35 4
    36 5
    37 
    38 Process returned 0 (0x0)   execution time : 14.484 s
    39 Press any key to continue.
    40 
    41 
    42 #include <stdio.h>
    43 
    44 int main()
    45 {
    46     int v[5] = {1, 2, 3, 4, 5};
    47     int *n;
    48     for (n = v; n != &v[5]; n++) {
    49         printf("%d
    ", *n);
    50     }
    51     return 0;
    52 }
    53 
    54 
    55 1
    56 2
    57 3
    58 4
    59 5
    60 
    61 Process returned 0 (0x0)   execution time : 6.158 s
    62 Press any key to continue.
    63 
    64 #include <stdio.h>
    65 int main()
    66 {
    67     int v[5] = {1, 2, 3, 4, 5};
    68     int *n = v;
    69     while (n != v+5) {
    70         printf("%d
    ", *n++);
    71     }
    72     return 0;
    73 }
    74 
    75 
    76 1
    77 2
    78 3
    79 4
    80 5
    81 
    82 Process returned 0 (0x0)   execution time : 11.702 s
    83 Press any key to continue.

    23.4 指标与下标运算子([ ])

    1 int v[5];
    2 int *n = v;
    3 n[0] == *n;          n[0] == *v;
    4 n[1] == *(n+1);      n[1] == *(v+1);
    5 n[2] == *(n+2);      n[2] == *(v+2);

    23.5 在函式间传递阵列(使用指标)

     1 #include <stdio.h>
     2 int maxv(int *, int N);
     3 
     4 int main()
     5 {
     6     int a[3] = {3, 8, 7};
     7     printf("Max: %d
    ", maxv(a, 3)); // 阵列当引数传
     8     int b[5] = {3, 9, 7, 1, 2};
     9     printf("Max: %d
    ", maxv(b, 5));
    10     return 0;
    11 }
    12 
    13 int maxv(int *v, int N) {  // 起始位置
    14     int max = v[0], i;     // 参数是个指标
    15     for (i = 1; i < 3; i++) {
    16         if (v[i] > max) {
    17             max = v[i];
    18         }
    19     }
    20     return max;  // 在函式内部指标当作一个阵列在用
    21 }
    22 
    23 Max: 8
    24 Max: 9
    25 
    26 Process returned 0 (0x0)   execution time : 0.817 s
    27 Press any key to continue.

    23.6 指标与阵列的关系

    指标存储某阵列元素位址时的特殊性

    • 可以透过加减整数算出同阵列其他元素的记忆体位址
    1. 加 N 等同于向后移动 N 个元素后的记忆体位址
    2. 减 N 等同于往回移动 N 个元素后的记忆体位址
    • a[b]运算等同于*(a+b),反之亦同
    1. 在该阵列中从 a 开始往后移动 b 所在的阵列元素
    2. 当指标储存某阵列第一个元素的记忆体位址后, 用起来就跟该阵列没什么两样了

    阵列可以隐性转型成该阵列第一个元素的记忆体位址

  • 相关阅读:
    thinkinginjava学习笔记07_多态
    thinkinginjava学习笔记06_复用类
    thinkinginjava学习笔记05_访问权限
    thinkinginjava学习笔记04_初始化与清理
    thinkinginjava学习笔记03_基本语法
    thinkinginjava学习笔记02_对象
    关于药物刺激引起的突变
    通路共出现
    关于reference-free去卷积
    一些研究生期间的反思
  • 原文地址:https://www.cnblogs.com/pxxfxxxx/p/10875250.html
Copyright © 2020-2023  润新知