指针

一 为什么使用指针

  • 函数的值传递,无法通过调用函数,来修改函数的实参
  • 被调用函数需要提供更多的“返回值”给调用函数
  • 减少值传递时带来的额外开销,提高代码执行效率

二 指针的定义与读写

//------------ 指针的定义  -------------
int age = 0;
int *p = NULL;  // p是一个指针,p的值就是一个整数的地址
P = &age;
*p = 4;  // age = 4

//-------------- 指针的读  --------------
// 读指针变量本身
float w_daige = 80.5;
float* p = &w_daige;
printf("%p", p);  // 0019FED8

// 读指针变量指向的内容
float w_daige = 80.5;
float* p = &w_daige;
printf("%.2f", *p);  // 80.50

//-------------- 指针的写  --------------
// 写指针变量本身
float w_daige = 80.5;
float *p = &w_daige;

float w_xiaoyu = 50;
p = &w_xiaoyu;

// 写指针变量指向的内容
float w_daige = 80.5;
float *p = &w_daige;

*p  =  60;  // w_daige = 60;

32 位系统中,int 整数占 4 个字节,指针占 4 个字节
64 位系统中,int 整数占 4 个字节,指针占 8 个字节

三 空指针和野指针

空指针,就是值为0的指针,任何程序数据都不会存储在地址为0的内存块中,它是被操作系统预留的内存块

int *p = 0;
int *p = NULL;
  • 空指针的使用
    • 指针初始化为空指针,避免访问非法数据
    • 指针不再使用时,可以设置为空指针
    • 表示这个指针还没有具体的指向,使用前进行合法性判断
// 1 指针初始化为空指针,避免访问非法数据
int *p = NULL;

// 2 指针不再使用时,可以设置为空指针
int a = 3;
int *p = &a;
*p = 5;
p = NULL;

// 3 表示这个指针还没有具体的指向,使用前进行合法性判断
int *p = NULL;
if(p)  // 等价于 p != NULL
{
  
}
  • 野指针:没有进行初始化
int *p; // 野指针

四 指针与整数加减运算

指针加减数字表示的意义是指针在数组中位置的移动; 对于整数部分而言,它代表的是一个元素,对于不同的数据类型,其数组的元素占 用的字节是不一样的, 比如指针 + 1,并不是在指针地址的基础之上加 1 个地址,而是在这个指针地址的 基础上加 1 个元素占用的字节数

  • p++:在 p 当前地址的基础上 ,自增 p 对应类型的大小,p = p + 1*(sizeof(类型))
  • p--:在 p 当前地址的基础上 ,自减 p 对应类型的大小,p = p - 1*(sizeof(类型))
#include <iostream>
using namespace std;

int main()
{
    int arry[] = { 0,1,2,3,4,5,6,7,8,9 };

    int* p = arry;
    cout << p << endl;  // array[0] 0019FEB4

    /* 1.p++; 在 p 当前地址的基础上, 自增 p 对应类型的大小,p = p + 1 * (sizeof(类型)) */
    p++;
    cout << p << endl;  // array[1] 0019FEB8

    /* 2.p--; */
    p--;
    cout << p << endl;  // array[0] 0019FEB4

    /* 3.*p; */
    cout << *p << endl; // array[0] 0

    /* 4.*p++; 
    *和++的优先级相同,运算顺序从右向左,所以*p++就相当于*(p++);
    这里存在一个知识误区:本应该先执行括号内的p++,使p从指向a[0]变成指向a[1];
    但其实事实并非如此;事实是,对于直接对*p++的输出来说,第一步是运算*p,输出后,第二步再运算p++
    */
    cout << *p++ << endl; // array[0] 0
    cout << p << endl;    // array[1] 0019FEB8

    /* 5.*p--; */
    cout << *p-- << endl; // array[1] 1
    cout << p << endl;    // array[0] 0019FEB4

    /* 6.*p+5; */
    cout << *p + 5 << endl; // array[0] 0+5
    cout << p << endl;      // array[0] 0019FEB4

    /* 7.*(p+5); */
    cout << *(p + 5) << endl;  // array[5] 5
    cout << p << endl;         // array[0] 0019FEB4
    return 0;
}

image

五 指针与指针的比较运算

// 指针与指针的比较
#include <iostream>
#include <string>
using namespace std;

int main()
{
  string gong_lue[5] = { "秀逼格", "吃夜宵", "看电影", "订房", "谈理想" };
  string* p;

  string* p1 = gong_lue;
  string* p2 = p1 + sizeof(gong_lue) / sizeof(gong_lue[0]);

  while (p1 < p2)
  {
    cout << *p1 << endl;
    p1++;
  }

  return 0;
}

image

六 指针与指针的减法运算

指针和指针做减法适用的场合:两个指针都指向同一个数组,相减结果为两个指针之间的元素数目,而不是两个指针之间相差的字节数

// 指针的减法运算
#include<iostream>
#include <string>
using namespace std;

int main()
{
  string gong_lue[5] = { "秀逼格", "吃夜宵", "看电影", "订房", "谈理想" };
  string* p;

  string* p1 = gong_lue;
  string* p2 = &gong_lue[4];

  printf("p1: %p\n", p1);          // p1: 0019FE44
  printf("p2: %p\n", p2);          // p2: 0019FEB4

  printf("%d\n", sizeof(string));  // 28
  printf("p2-p1=%d\n", p2 - p1);   // p2-p1=4
  printf("p2-p1=%d\n", (unsigned int)p2 - (unsigned int)p1); // p2-p1=112 (28*4=112)

  return 0;
}

image
VC++ 关于Sizeof(string) 为何是28(x86)40(x64)个字节

七 指针的强制类型转换

#include <stdio.h>
#include <string>

int main()
{
  int x = 0x31323334;  // 31 32 33 34  --->  1 2 3 4
  char* p = (char*)&x;

  for (int i = 0; i < 4; i++) 
  {
    printf("%c ", *p);
    p++;
  }

  printf("\n");
  return 0;
}

image

八 指针与const

看 const 离类型(int)近,还是离指针变量名近,离谁近,就修饰谁,谁就 不能变

#include <iostream>

int main()
{
  int value = 0;
  int value1 = 0;

  const int* p = NULL;
  p = &value;
  //*p = 5;  // const离类型近,value的值不能被改变

  int* const p1 = &value;
  *p1 = 5;
  //p1 = &value1;  // const离指针变量名近,p1的指向不能改变

  return 0;
}

九 二级指针

二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址

int guizi1 = 888; 
int *guizi2 = &guizi1; //1 级指针,保存 guizi1 的地址 
int **liujian = &guizi2; //2 级指针,保存 guizi2 的地址,guizi2 本身是一个一级指针变量

二级指针的用途

  • 普通指针可以将变量通过参数“带入”函数内部,但没办法将内部变量“带出”函数
#include <iostream>

void swap(int* a, int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main()
{
    int num1 = 2;
    int num2 = 3;
    swap(&num1, &num2);
    std::cout << "num1:" << num1 << "  num2:" << num2 << std::endl;

    return 0;
}

  • 二级指针可以不但可以将变量通过参数函数内部,也可以将函数内部变量 “带出”到函数外部。
#include <iostream>

void boyHome(int** matchmaker)
{
    static int boy = 23;
    *matchmaker = &boy;
}

int main()
{
    int* meipo = NULL;

    boyHome(&meipo);
    *meipo = 32;
    std::cout << "boy:" << *meipo << std::endl;

    return 0;
}

image

十 指针和数组

10.1 指针表示法和数组表示法

  • 数组完全可以使用指针来访问, days[3] 和 *(days+3) 等同
#include <iostream>
#include <iomanip>

using namespace std;

void printMonths1(int days[], int months)
{
    cout << "--------- 数组表示法 ----------" << endl;
    for (int i = 0; i < months; i++)
    {
        cout << setw(2) << i + 1 << "月有" << days[i] << "天" << endl;
    }
}
void printfMonths2(int* days, int months)
{
    cout << "--------- 指针表示法 ----------" << endl;
    for (int i = 0; i < months; i++)
    {
        cout << setw(2) << i + 1 << "月有" << *(days + i) << "天" << endl;
    }

}
int main()
{
    int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    printMonths1(days, sizeof(days) / sizeof(days[0]));
    printfMonths2(days, sizeof(days) / sizeof(days[0]));

    return 0;
}

image

10.2 指针数组和数组指针

#include <iostream>

using namespace std;

int main()
{
    //----------------  指针数组 ----------------
    int* p1[2]; // 定义了一个有两个元素的指针数组,每个元素都是一个指针变量
    int num1 = 172;
    int num2 = 173;
    p1[0] = &num1;
    p1[1] = &num2;

    //----------------  数组指针 ----------------
    int (*p2)[3];  // 定义一个指向三个成员的数组的指针
    int arry[4][3] = {
        {0,1,2},
        {3,4,5},
        {6,7,8},
        {9,11,12}
    };
    p2 = &arry[0];

    // 数组法访问元素  (*p)[j]
    cout << "---------- 数组法访问 ----------" << endl;
    for (int i = 0; i < sizeof(arry) / sizeof(arry[0]); i++)
    {
        for (int j = 0; j < sizeof(arry[0]) / sizeof(arry[0][0]); j++)
        {
            cout << (*(p2 + i))[j] << " ";
        }
        cout << endl;
    }
    // 指针法访问元素  *((*p)+j)
    cout << "---------- 指针法访问 ----------" << endl;
    for (int i = 0; i < sizeof(arry) / sizeof(arry[0]); i++)
    {
        for (int j = 0; j < sizeof(arry[0]) / sizeof(arry[0][0]); j++)
        {
            cout << *(*(p2 + i) + j) << " ";
        }
        cout << endl;
    }
    return 0;
}

image

int main()
{
    int a[3][4] = { 0 };
    int (*p)[4] = a;    //数组指针:定义一个指向四个成员的数组的指针

    // 给二维数组赋值
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            static int count = 1; // static只初始化一次
            a[i][j] = count++;
        }
    }

    // 用指针法访问二维数组
    cout << *(*(p + 1) + 2) << endl;
    return 0;
}

十一 函数指针

#include <iostream>

using namespace std;

void swap(int* a, int* b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}


int main()
{
    int num1 = 1;
    int num2 = 2;

    // 1.函数指针的定义 把函数声明移过来,把函数名改成 (* 函数指针名):
    void (*fp)(int* a, int* b);
    fp = &swap;
 
    // 2.调用
    /*贝尔实验室的C和UNIX的开发者采用第1种形式,而伯克利的UNIX推广者却采用第2种形式, ANSI C 兼容了两种方式*/
    (*fp)(&num1, &num2);    // 第2种,按普通指针解引的方式进行调用  // 交换完:num1:2  num2:1
    fp(&num1, &num2);       // 第2种,直接调用                    // 交换完:num1:1  num2:2
    
    // 3.给函数指针起别名
    typedef void(*swap_t)(int* a, int* b);
    // 用别名进行调用
    ((swap_t)&swap)(&num1, &num2);                               // 交换完:num1:2  num2:1

    cout << num1 << num2 << endl;  // 2 1

    return 0;
}

image

posted @ 2022-04-17 01:31  荒年、  阅读(54)  评论(0编辑  收藏  举报