C++ 指针认识(一)

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

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值