0
点赞
收藏
分享

微信扫一扫

C++指针详解1_malloc内存分配、指针常量结合以及指针加减带来的地址偏移

慕犹清 2022-04-30 阅读 31

系列前言

本系列参考书目:《深入理解C指针》《C和指针》《征服C指针》
旨在对书中内容进行提炼,将重要结论和底层原理配合丰富的代码实例来加深大家对C指针的理解;
下面是本节的主要内容

  • size_t等多种指针常用数据类型介绍
  • 指向指针的指针
  • 指针常量 常量指针 指向常量的常量指针
  • malloc calloc realloc
  • 迷途指针
  • 简单的返回值为指针的函数


指针常用四种预定义数据类型

  1. size_t 实际上是一个无符号整型
  2. ptrdiff_t 表示指针算术运算
  3. intptr_t uintptr_t 表示用来存储指针地址
// intptr_t可以存储长长整数型指针,而uintptr_t存储无符号整数指针
// 不可以直接将整数赋给uintptr_t,而是需要强转类型才可以 
long long num;
intptr_t *p1 = #

// 强转类型后我们可以使用uintptr_t指向整数变量num
uintptr_t *p2 = (uintptr_t *)num;

指针直接加减带来的地址问题

请注意以下定理

  1. 对某指针直接加减,实际上是对地址进行加减
    因为int占用4字节,所以如下代码进行自增后实际上是增加了一个int占用字节的地址,请注意注释写出的地址变化
  2. 指针相减相加返回两个指针之间的地址“单位差”,譬如对于int类型,一个单位就是4字节
int vector[] = {12, 43, 99};
int *p1 = vector;
printf("%d\n",*p1); // 假设初始地址100
p1++;
printf("%d\n",*p1); // 此时地址为104
p1++;
printf("%d\n",*p1); // 现在地址变成108

int *p2 = vector;
int *p3 = vector+2;
printf("p3-p2=%d\n",p3-p2);

指向指针的指针

所谓指向指针的指针很好理解,就是指针嵌套,依据以下代码做出解析

  1. 首先构造一数组b,然后用取地址运算符将a指向数组b的首地址!
  2. 之后新建一int类型的二重指针c,指向指针a(只有对使用&a才可以将a转换成int**的格式,这样才能正常赋值)
  3. 因为c是一双重指针,所以c[0]实际上就是a,而*c[0]才等于a[0]
  4. 最后结果显而易见,是1,即第一个元素
int *a, b[] = {1, 2, 3};
a = &b[0];              // 指针指向数组
int **c = &a;           // 将指针的指针指向一个指针
printf("%d\n", *c[0]);  // result = 1

经典常量与指针结合三板斧

  1. 指针常量(const在*前):它是一个指针,但无法通过解引用自己来改变指向对象的值
  2. 指向非常量的常量指针(const在*后):必须指向一非常量,指向对象不可变,但指向对象值可变
  3. 指向常量的常量指针(const分居*两侧):指向对象不可变,指向对象的值亦不可变
// 指针常量,const放在数据类型的前后无所谓,但必须在*前面
int const *a;
const int *b;

// 指向非常量的常量指针
int *const c;

// 指向常量的常量指针
const int *const d;

易错点

  • 当指针被任意多个不限位置的const修饰后,在Printf中需要用*解引用才可以显示其指向的内容
  • 而正常的没有被任何const修饰的指针都可以直接调用其指向的数值

malloc内存分配

  1. 原则:非静态的指针都需要执行内存分配来初始化,否则会出现内存错误
  2. malloc(sizeof(int))分配一个int大小的内存
  3. (int *)对分配的内存进行强制数据转换,之后就可以成功的为指针p1分配内存
  4. 静态的局部指针变量或者全局指针变量无法直接分配内存,需要分开两行写
  5. malloc calloc realloc都需要头文件stdlib.h
  6. 使用外指针务必记得free释放内存!不然怎么GC!
// malloc基本用法
int *p1 = (int *) malloc(sizeof(int));
*p1 = 8;
free(p1);

// 静态的局部指针变量或者全局指针变量分配步骤
static int *p2;
p2 = (int *) malloc(sizeof(int));

针对char类型的指针,因为我们要存储字符串,所以可以使用乘法来分配指定的内存,譬如下方代码分配了8字节的内存

char *str = (char *) malloc(8*sizeof(char));

calloc与memset

  1. calloc表示分配指定数量的内存并且将他们都执行默认初始化
  2. memset表示内存填充
// calloc第一个参数表示分配数量,第二个为分配大小,下图分配20字节空间
int *p1 = (int *) calloc(5, sizeof(int));

// 参一是欲填充指针,参二是填充数,参三是填充数量
memset(p1,1,5*sizeof(int));

realloc内存大小控制

realloc返回一个void*指针,所以必要情况下需强转类型;
参数一为指针变量,参数二为将该指针变量占据的内存缩小到多少

char *s1 = (char *) malloc(8*sizeof(char));
realloc(s1,3);

迷途指针

也即我们对一个以及free的指针再次执行赋值操作,那么该指针就会指向一不存在的内存(虽然编译器不会报错)

int *p1 = (int *) malloc(sizeof(int));
*p1 = 7;
free(p1);
*p1 = 100;

指针栈与堆

  1. 程序栈通常和堆共享一块内存,堆占用上部,栈占用下部
  2. 程序栈存储栈帧,栈帧存放函数参数和局部变量,而堆则负责管理动态内存
  3. 栈帧的组成部分:返回地址、局部数据存储、参数存储、栈指针与基指针
    栈指针:指向栈的顶部,不是C指针
    基指针:存在并指向栈帧内部地址,如返回地址,同样也不是C指针

基本返回指针的函数

下方展示了一个返回int指针的函数,我们在main方法里调用它并使用完后,务必记得free掉这个指针!

int* pointer(){
    int *nums = (int *) malloc(10*sizeof(int));
    for(int i = 0; i < 10; i++){
        nums[i] = i;
    }
    return nums
}

int main(){
    int* vector = pointer();
    free(vector);
}

举报

相关推荐

0 条评论