• C++基础_指针


    内存四区

    a) 代码区。代码
    b) 全局区。全局的常量字符串常量“ABC” 变量
    c) 栈区。系统自动开辟,系统自动释放,并不是很大
    d) 堆区。动态开辟的内存,手动开辟,手动释放。大

    地址

    把内存以单个字节为单位,分开,每一个编号,这个编号就是地址。
    a) 编号是连续的
    b) 唯一的
    c) 取地址运算符:&单目运算符 优先级() [],结合性右往左
    int a;
    &a;

    首地址

    一段内存空间的第一个存储单元的地址

    指针变量

    1.用来存放地址的变量,2.内存大小4个字节

    地址是编号,一种数据
    整数 int a;
    字符 char c;
    小数 float b;
    地址 指针变量

    a) 指针变量的定义

    int main(){
    	int a;
    	float b;
    	char c;
    	int *p;		// 定义一个指针变量p,存的是地址。int指名指针指向的数据类型,*指名p这个变量是一个指针变量
    }
    

    b) 指针变量的赋值

    int a = 5;
    int *p;
    p = &a;		//p指向a
    

    c) 指针的引用

    int a = 5;
    int *p;
    p = &a;
    printf("a=%d", a);
    printf("a=%d", *p);
    

    访问a这个变量:

    1. 使用变量名,
    2. 指针访问:指针变量。取值运算符,返回某一个地址中的值,单目运算符 右到左
      注意了:再定义指针变量的时候int *p; 只是表明p是一个指针变量。而非定义的时候p则是取值p指向的内存值。
      image

    补充

    野指针:不能明确指向的指针变量。危险。
    int *p; // p里面保存的地址不确定,p的指向不明确的,重要数据有可能被操作到
    解决方法:int *p = NULL;

    空指针:int* float* char* double*
    void* 转换成其它的数据类型
    指针变量的运算:+ - ++ -- 只是作指针的偏移,去访问地址旁边的一些内存
    指针变量的加减,是以指针所指向的类型空间为单位进行偏移

    int main(){
    	char *p;	// 一个char占1个字节,则p + 1表示p指向的地址偏移一个字节
    	int *p1;	// 一个int占4个字节,则p1 + 1表示p1指向的地址偏移4个字节
    	double *p2;	// 一个double占8个字节,则p2 + 1表示p2指向的地址偏移8个字节
    }
    

    一维数组与指针

    1. 定义一个一维数组,数组名是这个数组的“首地址”
    int main()
    {
    	int a[5];
    	printf("a = %d
    ", a);
    	printf("&a = %d
    ", &a);
    	printf("a+1 = %d
    ", a + 1);
    	printf("&a+1 = %d
    ", &a + 1);
    	return 0;
    }
    

    结果:
    a = 13630344
    &a = 13630344
    a+1 = 13630348
    &a+1 = 13630364
    解析:
    a 指向a[0], a[0]是int元素, a的类型就是 int*
    a 指向a[0] int元素, int* 4字节
    &a 这个地址指向整个数组, &a的类型就是 int(*)[5];(数组的指针)

    1. 访问数组的元素:
      a) 下标法:a[0] a[1]
      b) 指针法:
    int main()
    {
    	int a[5] = { 1, 2, 3, 4, 5 };
    	int* p = a;	// p指向a[0]
    	/*
    	p指向a[0]的地址
    	p+1指向a[1]的地址
    	...
    	p+4指向a[4]的地址
    	则取地址的值,使用取值运算符* *p、*(p+1)...*(p+4)
    
    	*/
    	for (int i = 0; i < 5;++i) {
    		printf("%d ", *(p + i));  // 或者这样:printf("%d ", *p++);
    	}
    	// 结果为:1 2 3 4 5 
    	return 0;
    }
    

    一些细节解析:

    • 和 ++ 是单目运算符,优先级相同,从右往左结合
    • 是双目运算符,优先级<和++
      所以
      (p+i)要加括号,而p++不需要(p++,先p++结合,然后再与*结合)
      当然也可以这么写:
    for (int i = 0; i < 5;++i) {
    	printf("%d ", *(a+i));  // 这里不能用*a++,因为a是数组名,也是第一个元素的首地址,但它永远都是数组名,这个主要意义不能忽略,所以就不能去a++
    	}
    

    再次强调:
    1. 数组名是这个数组的首地址
    2. 首地址就是一段内存中第一个存储单元的地址

    二/多维数组与指针

    需要知道:三维数组的存储单元是一个二维数组,二维数组的存储单元是一个一维数组,一维数组的存储单元是一个元素
    image
    int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

    1. 数组名a是这个二维数组的首地址,这个数组在内存中的第一个存储单元是a[0],即这个一维数组
      a的类型是:(数组的指针)int(*)[4]; 所以a+1 偏移16字节
    2. a[0]是一维数组的数组名,a[0]指向a[0][0];
      a[0]的类型是:(int类型指针)int*; 所有a[0]+1 偏移4个字节
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
    	int a[2][3] = { {1, 2, 3}, {4, 5, 6} };
    	printf("a[1][2] = %d
    ", a[1][2]);  // 结果为:6
    	printf("a[1][2] = *(a[1] + 2) = %d
    ", *(a[1] + 2));  // 结果为:6
    	printf("a[1][2] = *(a[1] + 2) = *(*(a+1) + 2) = %d
    ", *(*(a + 1) + 2));  // 结果为:6
    	cout << *(a + 1) << endl;  // 随机内存地址 00CFFEB0
    	cout << a << endl;	//  随机内存地址 00CFFEA4
    	cout << *a << endl;	//  随机内存地址 00CFFEA4
    	
    	
    	return 0;
    }
    

    int a[3][4]
    a 类型是:int()[4],a指向首地址,也指向a[0]
    &a 类型是:int(
    )[3][4], &a指向整个数组
    a[0] 类型是:int*,a[0]指向一维数组的元素
    a[0][0] 类型是:int,a[0][0]是一个元素,他的类型就是int类型

    image

    指针数组: int*[]
    数组指针: int(*)[]


    请相信自己

    当我们迷茫,懒惰,退缩的时候 我们会格外的相信命运 相信一切都是命中注定

    而当我们努力拼搏,积极向上时 我们会格外的相信自己

    所以命运是什么呢? 它是如果你习惯它 那它就会一直左右你

    如果你想挣脱它 那它就成为你的阻碍 可如果你打破了它 那它就是你人生的垫脚石!


    如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!

  • 相关阅读:
    [CF997E] Good SubSegment
    CF916E
    BZOJ2006 超级钢琴
    BZOJ4571
    凸包总结
    树形DP入门
    bzoj4300 绝世好题(位运算+DP)
    bzoj4552 [Tjoi2016&Heoi2016]排序 (线段树+二分)
    SP1716 GSS3
    Noip2009 Hankson 的趣味题 (简单数学)
  • 原文地址:https://www.cnblogs.com/suguangti/p/15413323.html
Copyright © 2020-2023  润新知