当前位置:   article > 正文

c语言 --- 指针_指针指向地址

指针指向地址

什么是指针?

指针就是一个地址,在c语言中任何东西都是有地址的,如何获取地址? 用的是&:取地址符

  • 指针就是一个整数

  • 获取指针:&

  • 定义变量时,可以通过取地址符 &,得到当前变量的地址-> 一个房间对应一个房间号,地址类比于房间号

  • 所有的指针类型都是 4个字节,就是一个整数,不需要考虑溢出

指针变量

存放地址(指针)的,也就是存放一个特定的整数(这个整数是可以表示地址的)

例:整型变量存放整数,指针变量存放指针,指针变量就是一个变量,和整型变量没有区别

  • 如何产生一个指针变量

  • * 用于标识 变量是 指针变量,必须有,没有就不是指针变量,* 写前面 和 写后面没有区别

类型* 变量名;

类型 *变量名;

  • 指针变量的两个重要概念

    • 指针的类型:去掉变量名

    • 指针所指向的类型:去掉变量名和 * 号

用指针的时候需要保持上述两个类型的一致

  1. int* p;
  2. //类型: int*
  3. //所指向的类型: int --->本质就是指针所操作的数据类型
  4. int(*pp)[3]; //--->指针
  5. //类型: int(*)[3];
  6. //所指向的类型: int[3] --->pp操作的就是一个数组 数组长度是3
  7. int* pArray[3];//--->数组

第一个位置存储了一个0,0是4个字节,对应这四个字节的首地址就是这个地址:0x0000000EAA93F574-> 指针

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int num = 0; //定义的变量会占用一段内存-> 操作系统对于这段内存会给予一个编号-> 地址
  5. //printf("%d\n", &num); //得到变量的地址:-1433143948-> 指针是一个整数
  6. printf("%p\n", &num); //指针有特定的打印方式:0000000EAA93F574-> %p的方式打印 16进制的整数
  7. int* p; //指针变量
  8. char* pc;
  9. double* pd;
  10. printf("%d\n", sizeof(int*));
  11. printf("%d\n", sizeof(char*));
  12. void* pvoid;
  13. pc = NULL;
  14. pd = NULL;
  15. pvoid = NULL;
  16. pc = (void*)0; //强制转换语法-> 把0强制转换成一个地址赋值给一个指针变量
  17. //新手误区
  18. int* pNum = &num; //在创建指针变量赋值的时候,不能这样理解*pNum=&num *起说明作用 int* 是一个类型
  19. //实质还是:pNum=&num;
  20. int aa = 1001;
  21. pNum = &aa;
  22. printf("%d\n", *pNum); //得到当前地址中的值
  23. //当指针变量指向了普通变量的时候 *指针变量等效普通变量
  24. *pNum = 10111101; //等效于普通变量做赋值运算 打印变量时变量改变了aa=10111101
  25. printf("%d\n", aa);
  26. return 0;
  27. }
  28. /*输出*/
  29. 8 //x64
  30. 8
  31. 1001
  32. 10111101
  33. 8 //x86
  34. 8
  35. 1001
  36. 10111101
  37. #ifndef NULL
  38. #ifdef __cplusplus
  39. #define NULL 0
  40. #else
  41. #define NULL ((void *)0)
  42. #endif
  43. #endif

不同类型的指针变量

  • 所有类型的指针占用的字节数都是相同的-> 指针变量就是用来存放地址的,地址就是一个整数,所有整数占用内存都是一样的  

  • 所有类型的指针变量占用的内存 在32位系统(x86)下都是4个字节   在64位系统(x64)下是8个字节

  • 特殊的指针类型:void*

  • 所有类型的指针变量的初始化都可以让它指向空

  • 专门用来初始化指针变量的东西:NULL

    • 防止悬浮指针:没有指向任何地方,放在那里,不知道它指向哪里

    • 防止野指针:指向一个莫名其妙的地方

    • 在写程序的时候一定要避免这两种情况的存在

  • 指针变量如何获取当前地址中的值:*指针变量

指针的运算

  • *指针变量:获取当前指针变量所指向的内存中存储的值

  • p+n操作或者p-n操作:算术运算,n是一个整数,实质上是内存的字节偏移,和指向内存存储的数据类型有关

    • p+sizeof(指针所指向的类型)*n

    • 对于一个指针变量来说,不会单独的去偏移,一般要指向一段内存去做偏移(数组就是一段连续的内存,可以通过指针的偏移去操作数组)

    • 对于不同类型的指针之间是没有什么算术运算

    • p++ 和 p-- 也算 p + n 操作

  • 指针的偏移和存储的数据类型有关,int 类型占用 4 个字节,+ 3 总共移动 12 个字节,char 类型占用 1 个字节,+ 3 总共移动 3 个字节

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int* p = NULL; //所指向的数据类型:int 大人一步走4米
  5. char* pc = NULL; //所指向的数据类型:char 小孩子一步走1米
  6. printf("%p\n", NULL); //0000000000000000
  7. //做字节上的偏移-> 和它操作的数据类型有关
  8. p = p + 3; //大人走了3步 总共走了: 12米 0xC
  9. //p+sizeof(int)*3 : 0 + 4*3
  10. pc = pc + 3; //小孩子走3步 总共走了: 3米 0x3
  11. //p + sizeof(char) * 3 : 0 + 1*3
  12. //类似加法的运算-> 不需要带int类型
  13. //int a = 1;
  14. //a = a + 3;
  15. printf("%p\n", p);
  16. printf("%p\n", pc);
  17. //指针的偏移字节数 == p + sizeof(指针所指向的类型)*n
  18. //两个指针相加没有实际含义
  19. //int a = 0;
  20. //int b = 1;
  21. //int* pa = &a;
  22. //int* pb = &b;
  23. //int* pd= pa + pb; //表达式必须包含整型
  24. return 0;
  25. }
  26. /*输出*/
  27. 0000000000000000
  28. 000000000000000C
  29. 0000000000000003

内存四区

在c语言中会把内存分成4个区域

学习内存四区可以帮助理解指针运算中的错误代码

 静态变量的特性

  1. void print()
  2. {
  3. static int num = 1; //定义一个静态变量 这个代码运行的时候只执行一次
  4. //静态变量不做初始化默认为0
  5. num++;
  6. printf("%d\n", num);
  7. }
  8. int main()
  9. {
  10. print(); //第一次调用 num=2
  11. print(); //第一次调用 num=3 会记录程序上一次运行的结果
  12. //num = 3; //静态变量有作用域-> 和全局变量的区别 只能在子函数中使用 报错
  13. }

 一些运用指针的经典错误

指针处理字符串的特例

操作常量区的字符串,不能修改

* pchar 指向第一个内存,等效于常量区的 I,常量区的内存不能做修改

指针变量可以指向一段内存(字符串),指向这段内存的首地址

返回一个指针

可以返回一个值,但是不能返回一个值的地址,但是字符串只能返回首地址

  1. char* returnPoint()
  2. {
  3. //返回局部变量地址,不允许 static修饰没有问题-> 静态区会保存数据
  4. //函数调用完,栈区内存会被系统自动回收(清除所有的数据)
  5. char array[10] = "ILoveyou";
  6. //%s 打印方式,从首地址开始,打印到'\0'结束
  7. char* p = &array[0]; // 1.指针变量指向第一个变量的地址 2.返回字符串的首地址
  8. //处理方案:把数据存到堆区,返回堆区这段内存的首地址 堆区内存不会被系统自动回收
  9. return p;
  10. }
  11. int main()
  12. {
  13. int* p = NULL; //0 存放在常量区-> 指针没有指向一个变量导致修改了常量中的东西
  14. //*p = 1234; //不能修改0所在的内存 引发了未经处理的异常:写入访问权限冲突-> 由于访问了常量区的内容导致的
  15. //printf("%d", *p);
  16. //------------------------------------------------------
  17. char* str = "ILoveyou"; //解析:把这段字符串的首地址赋值给指针变量-> 并没有把"ILoveyou"存到指针变量中去
  18. char* pchar;
  19. puts(str);
  20. pchar = "ILoveyou";
  21. //*pchar = 'M'; //写入访问权限冲突-> *pchar等效于'I' 'I'存在常量区不能修改
  22. puts(pchar);
  23. //-------------------------------------------------------
  24. char array[10] = "ILoveyou";
  25. pchar = &array[0]; //取第一个位置的地址-> 把I的地址赋值给 *pchar
  26. *pchar = 'M'; //把"ILoveyou"从常量区拷贝到栈区-> 修改栈区变量的内存
  27. puts(array);
  28. int* result = returnPoint();
  29. puts(result);
  30. puts(result);
  31. puts(result);
  32. puts(result);
  33. return 0;
  34. }
  35. /*输出*/
  36. ILoveyou
  37. ILoveyou
  38. MLoveyou
  39. 頊槷?
  40. 頊槷?
  41. 頊槷?
  42. 頊槷?

万能指针

  • 万能指针就是void* 类型的指针变量

  • 能够操作任何类型的地址

  • 万能指针在访问数据的时候必须要做强制类型转换

  1. #include <stdio.h>
  2. int main()
  3. {
  4. int num = 10;
  5. void* pVoid = &num; //解析: pVoid=&num; 不是*pVoid=&num-> 定义变量时 *起说明作用 表示类型
  6. //printf("%d\n",*pVoid); 不能直接这样使用 必须要做强制类型转换为int*类型才能访问数据
  7. printf("%d\n", *(int*)pVoid);
  8. double dNum = 1.11;
  9. pVoid = &dNum;
  10. printf("%.3lf\n", *(double*)pVoid);
  11. //万能指针使用的时候要强制转换为目标类型(指向数据类型的指针)
  12. int number = 0x00410042; //字节的高低 左边:高位(高字节) 右边:低位(低字节)
  13. printf("%d\n", number);
  14. void* p = &number;
  15. char* pp = (char*)p;
  16. //一个十六进制位是4个二进制位
  17. //两位十六进制位是一个字节 4个字节用8个十六进制位表示
  18. //8个二进制位是一个字节 8个二进制位是2个十六进制数
  19. printf("%c\n", *pp); //42 -->B //0000005B9FDBF5E4 低地址
  20. char* pc = (char*)p; //转换为char类型做偏移
  21. printf("%c\n", *(pc + 2));//41 -->A //000005B9FDBF5E6 高地址
  22. //小端模式 高字节存放到内存地址高的地址上
  23. //大端模式 高字节存放到内存地址低的地址上
  24. //十进制:1235
  25. //1高位 5低位
  26. printf("%p\n", pp); //0000005B9FDBF5E4 低地址
  27. printf("%p\n", pc + 2); //0000005B9FDBF5E6 高地址
  28. //万能指针应用: 统一接口(统一函数传参-> 以万能指针充当函数参数 可以传任何类型的指针)
  29. //malloc(void* p,int arrayNum);
  30. return 0;
  31. }
  32. /*输出*/
  33. 10
  34. 1.110
  35. 4259906
  36. B
  37. A
  38. 0000005B9FDBF5E4
  39. 0000005B9FDBF5E6
  40. E4<E6 E4是低地址 E6是高地址
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/逻辑思维/article/detail/63177
推荐阅读
相关标签
  

闽ICP备14008679号