一个指针变量指向了一个值的内存地址,因此可以通过指针可以间接访问内存。
在32位操作系统中,指针占用内存空间为4字节,64位操作系统中,指针占用内存空间为8字节,所有数据类型指针占用的内存空间都相同。
语法: 数据类型 * 指针变量名;
空指针:定义指针变量并没有赋值,指针变量指向内存编号为0的空间;空指针指向的内存是不可以访问的。
野指针:指针变量指向非法的内存空间,在程序中避免出现野指针。
- #include
- using namespace std;
-
- int main()
- {
- //指针定义变量
- int * p;
- int a = 5;
-
- //空指针:定义指针变量并没有赋值,指针变量指向内存编号为0的空间
- //空指针指向的内存是不可以访问的
- // cout << "访问空指针 p = " << p << endl; // 此处会报错:使用了未初始化的局部变量p
-
- //野指针:指针变量指向非法的内存空间,在程序中避免出现野指针
- int* p1 = (int*)0x0022;
- // cout << "访问野指针 *p1" << *p1 << endl; //此处会报异常
-
- //指针变量赋值
- p = &a;
- cout << "指针地址:p = " << p << ", &a = " << &a << endl;
-
- //访问指针变量指向地址中的值
- cout << "指针变量地址中存储的值 *p = " << *p << ", a = " << a << endl;
-
- //修改指针值的内容
- *p = 12;
- cout << "指针值修改后:*p = " << *p << ", a = " << a << endl;
-
- //指针占用内存空间
- //在32位操作系统中,指针占用内存空间为4字节,64位操作系统中,指针占用内存空间为8字节
- //所有数据类型指针占用的内存空间都相同
- cout << "指针占用内存空间:" << sizeof(p) << endl;
- cout << "指针占用内存空间:" << sizeof(int *) << endl;
- cout << "指针占用内存空间:" << sizeof(float *) << endl;
- cout << "指针占用内存空间:" << sizeof(string *) << endl;
-
- system("pause");
-
- return 0;
- }
输出结果
指针地址:p = 000000029DAFF664, &a = 000000029DAFF664
指针变量地址中存储的值 *p = 5, a = 5
指针值修改后:*p = 12, a = 12
指针占用内存空间:8
指针占用内存空间:8
指针占用内存空间:8
指针占用内存空间:8
语法:
- var 指针变量名 *数据类型
- 指针变量名 := new(数据类型) //new返回数据类型的指针,即 *数据类型 ,值为数据类型的零值
- package main
-
- import (
- "fmt"
- "unsafe"
- )
-
- func main() {
- //指针定义变量
- var p *int
- var a int = 5
-
- //空指针:定义指针变量并没有赋值,指针变量指向内存编号为0的空间
- //空指针指向的内存是不可以访问的
- fmt.Println("空指针 p = ", p)
- // fmt.Println("访问空指针 *p = ", *p) //访问空指针指向的内存时报错
-
- //指针变量赋值
- p = &a
- fmt.Printf("p = %x, &a = %x\n", p, &a)
-
- //访问指针变量指向地址中的值
- fmt.Println("指针变量地址中存储的值 *p = ", *p, ", a = ", a)
-
- //修改指针值的内容
- *p = 12
- fmt.Println("指针值修改后:*p = ", *p, ", a = ", a)
-
- //指针占用内存空间
- //在32位操作系统中,指针占用内存空间为4字节,64位操作系统中,指针占用内存空间为8字节
- //所有数据类型指针占用的内存空间都相同
- fmt.Println("指针占用内存空间:", unsafe.Sizeof(p))
-
-
- //使用new关键字创建指针
- fmt.Println("---- 使用new关键字创建指针 ----")
- p1 := new(int)
- var b int = 3
-
- fmt.Println("new指针结果 p1 = ", p1, ", *p1 = ", *p1)
-
- fmt.Printf("p1数据类型:%T\n", p1)
-
- p1 = &b
- fmt.Printf("p1 = %x, &b = %x\n", p1, &b)
-
- //访问指针变量指向地址中的值
- fmt.Println("p1指针变量地址中存储的值 *p = ", *p1, ", b = ", b)
-
- //修改指针值的内容
- *p1 = 27
- fmt.Println("p1指针值修改后:*p = ", *p1, ", b = ", b)
- }
输出结果
空指针 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
const修饰指针 - 常量指针:指针的指向可以修改,但指针指向的值不可以修改。
语法:const 数据类型 * 变量名 = 初始化值;
const修饰常量 - 指针常量:指针的指向不可以修改,但指针指向的值可以修改。
语法:数据类型 * const 变量名 = 初始化值;
const既修改指针又修饰常量:指针的指向不可以修改,指针指向的值也不可以修改。
语法:const 数据类型 * const 变量名 = 初始化值;
- #include
- using namespace std;
-
- int main()
- {
- int a = 5;
- int b = 3;
-
- //1、const修饰指针 - 常量指针:指针的指向可以修改,但指针指向的值不可以修改。
- const int* p = &a;
- p = &b; //指针的指向可以修改
- // *p = 8; //指针指向的值不可以修改
- //可以通过修改b的值修改指针指向的值
- cout << "修改变量b值前:*p = " << *p << ", b = " << b << endl;
- b = 8;
- cout << "修改变量b值后:*p = " << *p << ", b = " << b << endl;
-
- //2、const修饰常量 - 指针常量:指针的指向不可以修改,但指针指向的值可以修改。
- int* const p2 = &a;
- // p2 = &b; //指针的指向不可以修改
- *p2 = 8; //指针指向的值可以修改
-
- //3、const既修改指针又修饰常量:指针的指向不可以修改,指针指向的值也不可以修改。
- const int* const p3 = &a;
- // p3 = &b; //指针的指向不可以修改
- // *p3 = 8; //指针指向的值也不可以修改
- //可以通过修改a的值修改指针指向的值
- cout << "修改变量a值前:*p3 = " << *p3 << ", a = " << a << endl;
- a = 9;
- cout << "修改变量a值后:*p3 = " << *p3 << ", a = " << a << endl;
-
- system("pause");
-
- return 0;
- }
输出结果
修改变量b值前:*p = 3, b = 3
修改变量b值后:*p = 8, b = 8
修改变量a值前:*p3 = 8, a = 8
修改变量a值后:*p3 = 9, a = 9
- #include
- using namespace std;
-
- int main()
- {
- int arr[] = { 1,2,3,4,5 };
-
- //定义一个指针指向数组首地址
- int* p = arr;
-
- cout << "指针访问第一个元素:*p = " << *p << endl;
-
- //利用指针遍历数组
- for (int i = 0;i < 5; i++)
- {
- cout << "第 " << i << " 个元素值:" << *p << endl;
- p++;
- }
-
- system("pause");
-
- return 0;
- }
输出结果
指针访问第一个元素:*p = 1
第 0 个元素值:1
第 1 个元素值:2
第 2 个元素值:3
第 3 个元素值:4
第 4 个元素值:5
Go语言中把数组地址赋值给一个变量,这个变量类型其实是对应数据类型的指针数组;而不能像C++一样把数组地址赋值给一个*int(整型指针)变量。
- package main
-
- import "fmt"
-
- func main() {
- arr := []int{1, 2, 3, 4, 5}
-
- //定义一个指针指向数组地址, 可以看到结果是一个指针数组,而不是数组的首地址
- p := &arr
- fmt.Printf("p数据类型是:%T\n", p)
- fmt.Printf("数组arr地址:%p,变量p地址:%p\n", &arr, p)
-
- //修改数组元素值
- arr[2] = 7
-
- //遍历指针数组
- for i := range *p {
- fmt.Printf("(*p)[%d] = %d\n", i, (*p)[i])
- }
- }
在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
以下使用了头文件函数声明,基于上章节中 "C++函数分文件编写部分" ,可参考:C++ 学习(六)函数 及 函数的分文件编写_瘦身小蚂蚁的博客-CSDN博客
(可将以下代码内容合并一个文件中进行测试)
在函数章节定义的头文件swap.h中增加函数声明:
- //两数交换函数声明 - 地址传递
- void swap(int *p1, int *p2);
在swap.cpp中添加函数定义:
- //定义函数(地址传递):交换两个数值(参数地址传递,形参发生改变影响实参改变)
- void swap(int* p1, int* p2)
- {
- int temp = *p1;
- *p1 = *p2;
- *p2 = temp;
- }
增加测试.cpp文件,调用swap地址传参函数:
- #include
- #include "swap.h"
- using namespace std;
-
- int main()
- {
- int a = 5;
- int b = 3;
-
- cout << "交换前 a = " << a << ", b = " << b << endl;
-
- //调用交换两数函数(地址传参)
- swap(&a, &b);
- cout << "交换后 a = " << a << ", b = " << b << endl;
-
- system("pause");
-
- return 0;
- }
输出结果
交换前 a = 5, b = 3
交换后 a = 3, b = 5
以下基于上章节中 "Go语言调用其它包中的函数",可参考:C++ 学习(六)函数 及 函数的分文件编写_瘦身小蚂蚁的博客-CSDN博客
(可将以下2部分代码内容合并一个文件中进行测试)
在公共函数common.go文件中新增交换函数(地址传参):
- //定义函数(地址传递):交换两个数值(参数地址传递,形参发生改变影响实参改变)
- func Swap2(a *int, b *int) {
- *a, *b = *b, *a
- }
新增测试文件
- package main
-
- import (
- "fmt"
- common "testProject/CPlus/19-函数-公用函数"
- )
-
- func main() {
- a, b := 5, 3
- fmt.Printf("交换前 a = %d, b = %d\n", a, b)
- common.Swap2(&a, &b)
- fmt.Printf("交换后 a = %d, b = %d\n", a, b)
- }
输出结果
交换前 a = 5, b = 3
交换后 a = 3, b = 5