• C++ 学习(七)指针、const修饰指针、指针与数组、指针与函数


    1、指针

    一个指针变量指向了一个值的内存地址,因此可以通过指针可以间接访问内存。

    在32位操作系统中,指针占用内存空间为4字节,64位操作系统中,指针占用内存空间为8字节,所有数据类型指针占用的内存空间都相同。

    C++指针

    语法: 数据类型 * 指针变量名;

    空指针:定义指针变量并没有赋值,指针变量指向内存编号为0的空间;空指针指向的内存是不可以访问的。

    野指针:指针变量指向非法的内存空间,在程序中避免出现野指针。

    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. //指针定义变量
    6. int * p;
    7. int a = 5;
    8. //空指针:定义指针变量并没有赋值,指针变量指向内存编号为0的空间
    9. //空指针指向的内存是不可以访问的
    10. // cout << "访问空指针 p = " << p << endl; // 此处会报错:使用了未初始化的局部变量p
    11. //野指针:指针变量指向非法的内存空间,在程序中避免出现野指针
    12. int* p1 = (int*)0x0022;
    13. // cout << "访问野指针 *p1" << *p1 << endl; //此处会报异常
    14. //指针变量赋值
    15. p = &a;
    16. cout << "指针地址:p = " << p << ", &a = " << &a << endl;
    17. //访问指针变量指向地址中的值
    18. cout << "指针变量地址中存储的值 *p = " << *p << ", a = " << a << endl;
    19. //修改指针值的内容
    20. *p = 12;
    21. cout << "指针值修改后:*p = " << *p << ", a = " << a << endl;
    22. //指针占用内存空间
    23. //在32位操作系统中,指针占用内存空间为4字节,64位操作系统中,指针占用内存空间为8字节
    24. //所有数据类型指针占用的内存空间都相同
    25. cout << "指针占用内存空间:" << sizeof(p) << endl;
    26. cout << "指针占用内存空间:" << sizeof(int *) << endl;
    27. cout << "指针占用内存空间:" << sizeof(float *) << endl;
    28. cout << "指针占用内存空间:" << sizeof(string *) << endl;
    29. system("pause");
    30. return 0;
    31. }

    输出结果

    指针地址:p = 000000029DAFF664, &a = 000000029DAFF664
    指针变量地址中存储的值 *p = 5, a = 5
    指针值修改后:*p = 12, a = 12
    指针占用内存空间:8
    指针占用内存空间:8
    指针占用内存空间:8
    指针占用内存空间:8

    Go语言指针

    语法:

    1. var  指针变量名 *数据类型 
    2. 指针变量名 := new(数据类型)  //new返回数据类型的指针,即 *数据类型 ,值为数据类型的零值
    1. package main
    2. import (
    3. "fmt"
    4. "unsafe"
    5. )
    6. func main() {
    7. //指针定义变量
    8. var p *int
    9. var a int = 5
    10. //空指针:定义指针变量并没有赋值,指针变量指向内存编号为0的空间
    11. //空指针指向的内存是不可以访问的
    12. fmt.Println("空指针 p = ", p)
    13. // fmt.Println("访问空指针 *p = ", *p) //访问空指针指向的内存时报错
    14. //指针变量赋值
    15. p = &a
    16. fmt.Printf("p = %x, &a = %x\n", p, &a)
    17. //访问指针变量指向地址中的值
    18. fmt.Println("指针变量地址中存储的值 *p = ", *p, ", a = ", a)
    19. //修改指针值的内容
    20. *p = 12
    21. fmt.Println("指针值修改后:*p = ", *p, ", a = ", a)
    22. //指针占用内存空间
    23. //在32位操作系统中,指针占用内存空间为4字节,64位操作系统中,指针占用内存空间为8字节
    24. //所有数据类型指针占用的内存空间都相同
    25. fmt.Println("指针占用内存空间:", unsafe.Sizeof(p))
    26. //使用new关键字创建指针
    27. fmt.Println("---- 使用new关键字创建指针 ----")
    28. p1 := new(int)
    29. var b int = 3
    30. fmt.Println("new指针结果 p1 = ", p1, ", *p1 = ", *p1)
    31. fmt.Printf("p1数据类型:%T\n", p1)
    32. p1 = &b
    33. fmt.Printf("p1 = %x, &b = %x\n", p1, &b)
    34. //访问指针变量指向地址中的值
    35. fmt.Println("p1指针变量地址中存储的值 *p = ", *p1, ", b = ", b)
    36. //修改指针值的内容
    37. *p1 = 27
    38. fmt.Println("p1指针值修改后:*p = ", *p1, ", b = ", b)
    39. }

     输出结果

    空指针 p =  
    p = c00000a0a0, &a = c00000a0a0
    指针变量地址中存储的值 *p =  5 , a =  5
    指针值修改后:*p =  12 , a =  12
    指针占用内存空间: 8
    ---- 使用new关键字创建指针 ----
    new指针结果 p1 =  0xc00000a0a8 , *p1 =  0
    p1数据类型:*int
    p1 = c00000a0d0, &b = c00000a0d0
    p1指针变量地址中存储的值 *p =  3 , b =  3
    p1指针值修改后:*p =  27 , b =  27

    2、const修饰指针

    C++ const 修饰指针

    const修饰指针 - 常量指针:指针的指向可以修改,但指针指向的值不可以修改。

    语法:const 数据类型 * 变量名 = 初始化值;

    const修饰常量 - 指针常量:指针的指向不可以修改,但指针指向的值可以修改。

    语法:数据类型 * const 变量名 = 初始化值;

    const既修改指针又修饰常量:指针的指向不可以修改,指针指向的值也不可以修改。

    语法:const 数据类型  * const 变量名 = 初始化值;

    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. int a = 5;
    6. int b = 3;
    7. //1、const修饰指针 - 常量指针:指针的指向可以修改,但指针指向的值不可以修改。
    8. const int* p = &a;
    9. p = &b; //指针的指向可以修改
    10. // *p = 8; //指针指向的值不可以修改
    11. //可以通过修改b的值修改指针指向的值
    12. cout << "修改变量b值前:*p = " << *p << ", b = " << b << endl;
    13. b = 8;
    14. cout << "修改变量b值后:*p = " << *p << ", b = " << b << endl;
    15. //2、const修饰常量 - 指针常量:指针的指向不可以修改,但指针指向的值可以修改。
    16. int* const p2 = &a;
    17. // p2 = &b; //指针的指向不可以修改
    18. *p2 = 8; //指针指向的值可以修改
    19. //3、const既修改指针又修饰常量:指针的指向不可以修改,指针指向的值也不可以修改。
    20. const int* const p3 = &a;
    21. // p3 = &b; //指针的指向不可以修改
    22. // *p3 = 8; //指针指向的值也不可以修改
    23. //可以通过修改a的值修改指针指向的值
    24. cout << "修改变量a值前:*p3 = " << *p3 << ", a = " << a << endl;
    25. a = 9;
    26. cout << "修改变量a值后:*p3 = " << *p3 << ", a = " << a << endl;
    27. system("pause");
    28. return 0;
    29. }

    输出结果

    修改变量b值前:*p = 3, b = 3
    修改变量b值后:*p = 8, b = 8
    修改变量a值前:*p3 = 8, a = 8
    修改变量a值后:*p3 = 9, a = 9

    Go语言中没有const修饰指针

    3、指针和数组

    C++ 指针和数组

    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. int arr[] = { 1,2,3,4,5 };
    6. //定义一个指针指向数组首地址
    7. int* p = arr;
    8. cout << "指针访问第一个元素:*p = " << *p << endl;
    9. //利用指针遍历数组
    10. for (int i = 0;i < 5; i++)
    11. {
    12. cout << "第 " << i << " 个元素值:" << *p << endl;
    13. p++;
    14. }
    15. system("pause");
    16. return 0;
    17. }

    输出结果

    指针访问第一个元素:*p = 1
    第 0 个元素值:1
    第 1 个元素值:2
    第 2 个元素值:3
    第 3 个元素值:4
    第 4 个元素值:5

    Go语言指针数组

    Go语言中把数组地址赋值给一个变量,这个变量类型其实是对应数据类型的指针数组;而不能像C++一样把数组地址赋值给一个*int(整型指针)变量。

    1. package main
    2. import "fmt"
    3. func main() {
    4. arr := []int{1, 2, 3, 4, 5}
    5. //定义一个指针指向数组地址, 可以看到结果是一个指针数组,而不是数组的首地址
    6. p := &arr
    7. fmt.Printf("p数据类型是:%T\n", p)
    8. fmt.Printf("数组arr地址:%p,变量p地址:%p\n", &arr, p)
    9. //修改数组元素值
    10. arr[2] = 7
    11. //遍历指针数组
    12. for i := range *p {
    13. fmt.Printf("(*p)[%d] = %d\n", i, (*p)[i])
    14. }
    15. }

    在Go语言中, 运算符[]优先级高于*, 因此获取指针数组元素时需要加上小括号(*p),先取指针数组的首地址,(*p)[i] 再取对应元素的值

    输出结果

    p数据类型是:*[]int
    数组arr地址:0xc000004480,变量p地址:0xc000004480
    (*p)[0] = 1
    (*p)[1] = 2
    (*p)[2] = 7
    (*p)[3] = 4
    (*p)[4] = 5

    4、指针和函数(地址传参)

    C++ 函数(地址传参)

    以下使用了头文件函数声明,基于上章节中 "C++函数分文件编写部分" ,可参考:C++ 学习(六)函数 及 函数的分文件编写_瘦身小蚂蚁的博客-CSDN博客

    (可将以下代码内容合并一个文件中进行测试)  

    在函数章节定义的头文件swap.h中增加函数声明:

    1. //两数交换函数声明 - 地址传递
    2. void swap(int *p1, int *p2);

    在swap.cpp中添加函数定义: 

    1. //定义函数(地址传递):交换两个数值(参数地址传递,形参发生改变影响实参改变)
    2. void swap(int* p1, int* p2)
    3. {
    4. int temp = *p1;
    5. *p1 = *p2;
    6. *p2 = temp;
    7. }

    增加测试.cpp文件,调用swap地址传参函数: 

    1. #include
    2. #include "swap.h"
    3. using namespace std;
    4. int main()
    5. {
    6. int a = 5;
    7. int b = 3;
    8. cout << "交换前 a = " << a << ", b = " << b << endl;
    9. //调用交换两数函数(地址传参)
    10. swap(&a, &b);
    11. cout << "交换后 a = " << a << ", b = " << b << endl;
    12. system("pause");
    13. return 0;
    14. }

    输出结果

    交换前 a = 5, b = 3
    交换后 a = 3, b = 5

    Go语言函数(地址传参)

    以下基于上章节中 "Go语言调用其它包中的函数",可参考:C++ 学习(六)函数 及 函数的分文件编写_瘦身小蚂蚁的博客-CSDN博客

    (可将以下2部分代码内容合并一个文件中进行测试) 

    在公共函数common.go文件中新增交换函数(地址传参):

    1. //定义函数(地址传递):交换两个数值(参数地址传递,形参发生改变影响实参改变)
    2. func Swap2(a *int, b *int) {
    3. *a, *b = *b, *a
    4. }

     新增测试文件

    1. package main
    2. import (
    3. "fmt"
    4. common "testProject/CPlus/19-函数-公用函数"
    5. )
    6. func main() {
    7. a, b := 5, 3
    8. fmt.Printf("交换前 a = %d, b = %d\n", a, b)
    9. common.Swap2(&a, &b)
    10. fmt.Printf("交换后 a = %d, b = %d\n", a, b)
    11. }

    输出结果

    交换前 a = 5, b = 3
    交换后 a = 3, b = 5 

  • 相关阅读:
    C++碎片化知识点记录(2)
    ARM硬件断点
    Nginx使用vite部署vue3项目 页面刷新404
    基于canvas实现的多功能画板
    ResNet-RS:谷歌领衔调优ResNet,性能全面超越EfficientNet系列 | 2021 arxiv
    css --- 让人上头的flex
    [Vue]之Jwt的入门和Jwt工具类的使用及Jwt集成spa项目
    探讨 volatile 关键字
    HTML5期末大作业:基于html企业官网项目的设计与实现【艺术官网】
    电销行业精准客户资源如何获取 ,看完这篇文章你就知道了!!
  • 原文地址:https://blog.csdn.net/ling1998/article/details/125901508