1、指针
一个指针变量指向了一个值的内存地址,因此可以通过指针可以间接访问内存。
在32位操作系统中,指针占用内存空间为4字节,64位操作系统中,指针占用内存空间为8字节,所有数据类型指针占用的内存空间都相同。
C++指针
语法: 数据类型 * 指针变量名;
空指针:定义指针变量并没有赋值,指针变量指向内存编号为0的空间;空指针指向的内存是不可以访问的。
野指针:指针变量指向非法的内存空间,在程序中避免出现野指针。
#include<iostream>
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
Go语言指针
语法:
- 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 = <nil>
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 修饰指针
(1)const修饰指针 - 常量指针
指针的指向可以修改,但指针指向的值不可以修改。
语法:const 数据类型* 变量名 = 初始化值;
(2)const修饰常量 - 指针常量
指针的指向不可以修改,但指针指向的值可以修改。
语法:数据类型 * const 变量名 = 初始化值;
(3)const既修饰指针又修饰常量
指针的指向不可以修改,指针指向的值也不可以修改。
语法:const 数据类型* const 变量名 = 初始化值;
#include<iostream>
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
Go语言中没有const修饰指针
3、指针和数组
C++ 指针和数组
#include<iostream>
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语言指针数组
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
4、指针和函数(地址传参)
C++ 函数(地址传参)
以下使用了头文件函数声明,基于上章节中 "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<iostream>
#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语言函数(地址传参)
以下基于上章节中 "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