1.指针的创建与定义
如图,创建一个指针p指向变量a,则p、&p、*p、&*p分别表示指针地址存放的值、指针的地址、指针指向单元的值、指针指向单元的地址。
上代码:
#include<iostream>
using namespace std;
//指针地址、指针地址所存放的值、指针指向的地址、指针指向的值
int main(){
int a = 10;
int b = 20;
cout << a << ' '; //变量a的值
cout << &a << endl; //变量a的地址
//创建一个指针指向 a
int *p = &a;
cout << *p << ' '; //指针指向地址所存放的值
cout << &*p << ' '; //指针指向的地址
cout << p << ' '; //指针地址所存放的值(指向地址)
cout << &p << ' '; //指针的地址
p = &b; //修改指针指向
*p = 50;//修改指针指向的值
return 0;
}
结果:
输出第一行为变量a的值和地址,第二行为指针指向(此时指向a)的值和地址,第三行为指针地址存放的值和指针地址。
2.const修饰指针的三种情况
(1)const修饰指针,即常量指针。
可以修改指针指向,但不能修改指针指向地址的值。
int a = 10;
int b = 20;
const int *p = &a;
p = &b;//可以
*p = 50;//不可以
但是需要注意的是,p = &b已经修改指针的指向地址,则解引用*p也发生了改变。
代码测试:
#include<iostream>
using namespace std;
int main(){
int a = 10;
int b = 20;
//1.const修饰指针 常量指针
//可以修改指针指向,但不能修改指针指向地址的值
const int *p = &a;
cout << a << ' ';
cout << &a << ' ';
cout << *p << ' ' ;
cout << p << endl;
p = &b;//可以
// *p = 50;//不可以
cout << b << ' ';
cout << &b << ' ';
cout << *p << ' ' ;
cout << p << ' ';
return 0;
}
结果:
第一行为指针p指向a的情况,第二行为指针p指向b。当常量指针p对所指向地址的值进行修改时,会编译报错。
(2)const修饰变量,即指针常量。
可以修改指针指向地址的值,但不能修改指针指向地址。
int a = 10;
int b = 20;
int * const p = &a;
//p = &b;//不可以
*p = 50;//可以
如图,指针常量意味着可以任意修改指针指向地址的值,但是指针指向从定义时就不能再改。
代码测试:
#include<iostream>
using namespace std;
int main(){
int a = 10;
int b = 20;
//2.const修饰常量 指针常量
int * const p = &a;
cout << a << ' ';
cout << &a << ' ';
cout << *p << ' ' ;
cout << p << endl;
// p = &b;//不可以
*p = 50;//可以
cout << b << ' ';
cout << &b << ' ';
cout << *p << ' ' ;
cout << p << ' ';
return 0;
}
结果:
第一行为p指向a,第二行为修改指针p指向地址的值。
(3)const既修饰变量,又修饰指针。
既不可以修改指针指向地址的值,也不能修改指针指向地址。
int a =10;
int b =20;
const int * const p = &a;
//p = &b;//不可以
//*p = 50;//不可以
3.指针、数组、函数的简单运用示例
定义一个整型乱序数组,利用数组首地址、数组长度对数组排序和打印。
#include<iostream>
using namespace std;
//创建一个指针接收数组首地址 、int变量接收长度
void bubbleSort(int *arr,int len){
for(int i = 0;i<len-1;i++){
for(int j=0;j<len-i-1;j++){
if(arr[j] > arr[j+1])
{
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
//打印函数
void printArray(int *arr,int len){
for(int i = 0;i < len;i++)
cout << arr[i] <<' ';
}
int main(){
int arr[10] = {4,5,3,2,1,6,4,8,4,9};
int len = sizeof(arr)/sizeof(arr[0]);
//传入数组首地址和长度
bubbleSort(arr,len);
//首地址的另一种表示方式:引用首个元素的地址&arr[0]
printArray(&arr[0],len);
return 0;
}
需要注意以下内容:
(1)函数创建指针接收首地址,可以实现对实参的修改。这与形参接收参数不同,函数内部形参的改变不会影响实参。
(2)由于数组不可以复制,导致了数组同样不支持传参,因此我们只能采用“首地址+长度”的方式来传递数组,这存在两个重点:
a.提供一种隐式类型转换,支持将数组类型转换为首元素指针类型(比如说这里 arr 是int[10]
类型,传参时自动转换为int *
类型);
b.函数参数的语法糖,如果在函数参数写数组类型,那么会自动转换成元素指针类型,比如说下面这几种写法都完全等价:
void f(int *arr);
void f(int arr[]);
void f(int arr[5]);
void f(int arr[100]);
所以这里非常容易误导人的就在这个语法糖中,无论中括号里写多少,或者不写,这个值都是会被忽略的,要想知道数组的边界,你就必须要通过额外的参数来传递。
但通过参数传递这是一种软约束,你无法保证调用者传的就是数组元素个数。可能导致边界溢出。
之所以 C/C++ 的数组会这样,可能是数组的实际使用场景中经常会进行切段截取的,也就是说,一个数组类型并不总是完全整体使用,我们可能更多时候用的是其中的一段。
干脆这里就不支持复制,强迫程序员使用指针+长度这种方式来操作数组,反而更加符合数组的实际使用场景。
当然了,在 C++中有了引用语法,我们还是可以把数组类型进行传递的,比如:
void f1(int (&arr)[5]); // 必须传int[5]类型
void demo() {
int arr1[5];
int arr2[8];
f1(arr1); // OK
f1(arr2); // ERR
}
同时C++能通过 STL 提供容器的方式来区分定长与变长数组,std::array就是定长数组,而std::vector就是变长数组。
(3)C++中数组名可以作为数组的首地址使用。数组名是一个右值,不能对其进行自增或者自减处理,但是可以对其作为右值进行运算处理。对数组名进行运算处理可以看成是对数组地址进行了偏移,而偏移的单位可能是数组中元素的大小,也有可能是整个数组的大小。
#include<iostream>
using namespace std;
int main(){
int arr[10] = {4,5,3,2,1,6,4,8,4,9};
int *pp = arr; //首地址
int *p = &arr[0]; //首地址
int *p0 = (int *)(&arr);//首地址
int *p1 = arr+1; //偏移单位是数组中元素的大小(int 4字节)
int *p2 = (int*)(&arr+1); //偏移单位是整个数组大小(int 40字节)
//出错
// int *p3 = (int*)((int)arr+1);//偏移单位是一个字节(将arr转换为整型数)
cout << p << ' '<< pp << ' '<< p0 << ' '<< p1 << ' ' << p2 << ' ' << endl;
// cout << int(*p2)- int(*pp)<<endl;
return 0;
}
结果:
前三个均为数组首地址,第四个为偏移单位是数组中元素的大小(int 4字节),第五个为偏移单位是整个数组大小(int 40字节) 。
(4)sizeof求数组空间大小
当利用sizeof 求数组长度时:
int len = sizeof(arr)/sizeof(arr[0]
arr这里表示整个数组空间的大小,除以首地址元素的大小即得到数组长度。其余情况arr表示首地址。
但是不能用sizeof去求字符串数组的长度,因为字符串数组有两种初始化情况结尾是‘\0’,sizeof会把这个也计算在内,求字符串数组长度需要用到strlen函数。
#include<iostream>
#include<string.h>
using namespace std;
int main(){
char ch0[] = "abcde"; //默认结尾带有'\0'
char ch1[] = {'a','b','c','d','e','\0'};//结尾带有'\0'
char ch2[] = {'a','b','c','d','e'};
cout << strlen(ch0)<<' '; //5
cout << strlen(ch1)<<' '; //5
cout << strlen(ch2)<<endl; //5
cout << sizeof(ch0)<<' '; //6
cout << sizeof(ch1)<<' '; //6
cout << sizeof(ch2)<<endl; //5
return 0;
}
结果:
部分内容参考:https://blog.csdn.net/weixin_44216359/article/details/127647121