c++指针从简单到复杂的一些理解和例子

目录

1.指针01例 指针的使用和意义

指针02例 野指针

指针03例 使用*p时(解引用指针)的注意事项

2.指针和函数01例  传入参数为指针类时

 指针和函数02例  返回参数为指针类时

3.指针和一维数组01例  指针的前后移动

指针和一维数组02例  定义指针类型的一维数组 (指针数组)

指针和一维数组03例  数组名的类型和数组名的地址的类型

指针和一维数组04例  关于数组、数组地址和数组项的地址的返回值

指针和一维数组05例  指向指定长度数组的指针(数组指针)

指针和一维数组06例  数组指针作为函数的传入参数

4.常量指针和指针常量例01  基本功能和区别

5.指针指向一个类时 例01


1.指针01例 指针的使用和意义

#include <iostream>
using namespace std;
int main( )
{
   int *p;  //定义了一个变量名为p的int* (读作int类型的指针)
            //指针p指向一块int类型的内存地址,类似空快递盒上面单号。
            //所以输出p是一个地址值  如:0x1
            //那么*p输出什么?这个*叫做解引用运算符,与上文的int *里的*是两个不同的东西。
            //*p是这个空的内存里面存着的int类型的具体值,类似单号为p的快递盒里面的具体物品。
            //而我们没有让这个指针指向一块有具体值的内存地址,所以*p什么也无法输出。
            //值得注意的是,这样的写法并不规范,这是一个野指针,调用*p会导致程序运行时异常。
            //即使我们通过int* p=0或NULL 这也只是表示p是一个空指针,同样不建议调用*p。
   
   int a=100;
   p=&a;    //指针p指向变量a的地址
            //此时,p返回值就是a的地址值,如:0x6ffe44。
            //而*p,读作解引用p,它的返回的值是p指向的内存地址里存放的int值,故*p==100。
   a=1000;  //p依然返回a的地址值,但*p顺着地址找过去的a变成了1000,所以*p==1000
   *p=1;    //直接修改指针p指向的内存地址里存储的int a,所以a==1,*p==1,p还是a的地址值。
   
   cout<<"a:"<<a<<endl;//1
   cout<<"*p:"<<*p<<endl;//1
   return 0;
}

指针02例 野指针

#include <iostream>
using namespace std;
int main()
{
	int a;
	int* p;  //野指针 即在初始化时未指向一块合法内存空间的指针
    cout<<p<<endl;
	cout<<*p<<endl; //错误 解引用p必须保证指针合法 
	p=&a;    //初始化一块合法内存 
	cout<<p<<endl;
	cout<<*p<<endl; //正确 
   return 0;
}

指针03例 使用*p时(解引用指针)的注意事项

#include <iostream>
using namespace std;

int main( )//在使用*p(解引用p)之前 一定要确保指针p指向一块有效的内存地址
{
	int a;
	int* p;
    int* q=new int;  //指针q指向动态分配的一块int类型的地址
                     //new int返回的数据类型是地址值
    p=&a;            //指针p指向a的地址值
    *q=a;            //解引用q 为q指向的int内存地址的内存赋值为a 
	cout<<p<<endl;   //a的地址值
	cout<<*p<<endl;  //a的值
    cout<<q<<endl;   //q指向的地址值
	cout<<*q<<endl;  //解引用q,也就是a的值
   return 0;
}

2.指针和函数01例  传入参数为指针类时

#include <iostream>
using namespace std;
int oldAdd(int x){ //int函数oldAdd 传入参数为一个int类型的形参x
	return ++x;    //返回一个int类型的数字,值为x+1
}                  //我们如果在主函数中使用这个函数,需要写a=oldAdd(a);
                   //有没有更加简洁的写法呢?

void add(int* x){  //void函数add,传入参数为一个int类型的指针,形参为x
	cout<<"x:"<<*x<<endl;
	(*x)++;         //这里*x需要加上括号 因为++运算符的优先级比解引用运算符*高 
                    //我们在《指针和数组》中再讲*p++的意思和用法
                    //解引用x,返回结果自增1
	cout<<"x:"<<*x<<endl;
}
int main( )//主函数
{
   int a=100;
   int *p=&a;
   add(p);
   cout<<"a:"<<a<<endl; //101
   return 0;
}

 指针和函数02例  返回参数为指针类时

#include<iostream>
using namespace std;
int* test(){
	int a = 10;  //局部变量存在栈区,由编译器在函数执行完后自动释放 
	return &a;   //注意事项:不要直接返回局部变量的地址 
    
    //解决方法:在堆区开辟一块内存空间
    //int *a=new int(10); //返回类型为地址型 即指针
	//return a;
} 
int main(){
	int *ref=test();
	cout<<*ref<<endl; //10  这里临时保存了一次 
					  //但第二次就会释放这块内存空间 *ref就成了野指针 
	cout<<*ref<<endl; //0   dev5.11版本的结果,vs可能会是别的值,但都是非法的内存空间
	
} 

3.指针和一维数组01例  指针的前后移动

#include <iostream>
using namespace std;
void add(int* x){
    cout<<"x:"<<*x<<endl;  // 输出解引用x的值
    cout<<*x++<<endl;      // 1.x++返回x,2.解引用x,语句结束后x+1,指针x以int为单位后移一位。
                           // 因为数组存储在一串连续的内存地址上
                           // 所以指针的加/减n就是向后/前移动n个单位地址
    cout<<"x:"<<*x<<endl;  // 指针x移动后,解引用看看
}
int main()
{
	int a[]={10, 20, 30};
	int* p=a;     //p指向a[0],也可以写成int* p=a+0,a默认返回a[0]的地址值
	int* q=&a[0]; //q指向a[0],和上面的p是一样的指向,这里是指向一维数组的两种写法
	add(p);
	add(q);
	return 0;
}

指针和一维数组02例  定义指针类型的一维数组 (指针数组)

#include <iostream>
using namespace std;
int main()
{
	int* p[3];            //定义了int* 类型的数组p
                          //数组p[3]是指针,指向int类型的内存空间
	int a[]={1,2,3};
	for(int i=0;i<3;i++){
		p[i]=&a[i];       //数组p的每一个p[i]都是一个int类型的指针 即int* 类型
		cout<<*p[i]<<endl;
	}  
	return 0;
}

指针和一维数组03例  数组名的类型和数组名的地址的类型

#include <iostream>
#include <typeinfo> 
using namespace std;
int main()
{
	int a[]={1,2,3};
	int b[3][3]={{1,2,3},{4,5,6},{7,8,9}};
	cout<<typeid(a).name()<<endl;    //A3_i     //a是长度为3的int数组类型,A是array(数组)
	cout<<typeid(&a).name()<<endl;   //PA3_i    //&a是指针类型,指向长度为3的int数组
                                                //P是ptr(指针)
	cout<<typeid(a[0]).name()<<endl; //i        //a[0]是int类型,i是int
	cout<<typeid(b).name()<<endl;    //A4_A3_i  //b是4个长度为3的int数组的二维数组类型
	cout<<typeid(&b).name()<<endl;   //PA4_A3_i //&b是指针类型,
                                                //指向4个长度为3的int数组的二维数组
	cout<<typeid(b[0]).name()<<endl; //A3_i     //b[0]是长度为3的int数组类型
	return 0;
}

指针和一维数组04例  关于数组、数组地址和数组项的地址的返回值

#include <iostream>
using namespace std;
int main()
{
	int a[]={1,2,3};
	cout<<a<<endl; 
	cout<<&a<<endl; 
	cout<<&a[0];
	//这三个输出结果都是一样的地址值,为什么?
	//上个例子我们知道了a,&a和a[0]的类型
    //a[0]的返回值就是1,这个我们很好理解
    //a的返回值呢?它是数组类型,我们该返回什么?C++在这里做了隐式转换
    //a的返回值转换为了int*  类型,也就是指向一个int类型的指针,它指向数组的第一个数a[0]
    //所以直接输出a,相当于输出了&a[0],也就是a[0]的地址值
    //并且它的运算是以int为单位左右移动,所以a+1指向的是下一个数组元素
    //&a的返回值呢?它是PA3_i类型,是指向长度为3的int数组的指针 类型
    //所以&a返回的是数组a的地址值,这里只返回a的起始地址,即a[0]的地址值

    //所以尽管它们三个输出结果是一样的,但它们的数据类型不一样
    //那么又来了一个问题
    //我们之前定义过 int* p[3],它的类型是A3_Pi,什么意思呢?可以先看A3_P,再看Pi
    //意思是数组p[3]为指针类型,指向一个int类型的内存空间
    //也就是数组中的三个元素都是指针类型,都是int*类型
    //还记得PA3_3i类型吗?
    //就是上文中&a的类型。是PA3_i类型,是指向长度为3的int数组的指针,P指向A3_i
    //它们两个之间区别就出来了。
	return 0;
}

指针和一维数组05例  指向指定长度数组的指针(数组指针)

#include <iostream>
#include <typeinfo> 
using namespace std;
int main()
{
	int a[]={1,2,3};
	int(* p)[3];  //p是指针,指向int[3]类型的内存空间
	p=&a;
	cout<<typeid(p).name()<<endl; //PA3_i 
	cout<<typeid(*p).name()<<endl; //A3_i 
	//这样,p指向了一块长度为3的int类型数组
    //解引用p 得到数组a,用访问数组中元素的方式即可获取元素的值
	
    cout << (*p)[0] << endl;  // 输出 1
	cout << (*p)[1] << endl;  // 输出 2
	cout << (*p)[2] << endl;  // 输出 3

	return 0;
}

指针和一维数组06例  数组指针作为函数的传入参数

#include <iostream>
#include <typeinfo> 
using namespace std;
void arr3Add1(int(* x)[3]){//x是指针,指向int[3]类型的内存空间
	(*x)[0]++;
	(*x)[1]++;
	(*x)[2]++;
}
int main()
{
	int a[]={1,2,3};
	int(* p)[3];
	p=&a;
	cout<<typeid(p).name()<<endl; //PA3_i 
	cout<<typeid(*p).name()<<endl; //A3_i 
	arr3Add1(p);
	cout << (*p)[0] << endl;  // 输出 2
	cout << (*p)[1] << endl;  // 输出 3
	cout << (*p)[2] << endl;  // 输出 4

	return 0;
}

4.常量指针和指针常量例01  基本功能和区别

#include<iostream>
using namespace std;
int main(){
	int a = 10;
	int b = 20;
	const int* ptr1 = &a;  // 常量指针指向的值不可修改,但指针本身可以修改。
	*ptr1 = 30;  // 错误:指向的值不可修改
	ptr1 = &b;   // 正确:指针本身可以修改
	
	int c = 10;
	int d = 20;
	int* const ptr2 = &c;  // 指针常量的值(即指针指向的地址)不可修改,但指向的值可以修改。
	*ptr2 = 30;  // 正确:指向的值可以修改
	ptr2 = &d;   // 错误:指针本身不可修改
	return 0;

    //关于区分的一些理解
    //const int * ptr1其实也能写成int const * ptr1
    //表示ptr指针指向一个const int类型的值 那么这个值被const 修饰,自然就不能变
    // 这个可以和int*ptr一起理解 指针指向一个int类型的值

    //int* const ptr2 表示一个ptr是一个常量且是指针,那么指针的值是地址,地址自然就不能变
} 

5.指针指向一个类时 例01

#include<iostream>
using namespace std;
//一个person类
class Person{
	public:
		string name;
		int age;
		Person(string name,int age){
			this->name=name;//this 是一个指向当前对象的指针,而不是对象本身。
			this->age=age;//如果一定要通过.访问的话可以使用(*this).name
                          //意思就是解引用指针,然后访问指向的对象的内部属性
		} 
};
int main(){
	Person p("张三",18);
	Person* ptr = &p;//定义一个指向person类的指针
	cout<<p.name<<endl; 
	cout<<p.age<<endl; 
	
	cout<<ptr->name<<endl;//通过->箭头去访问指向的对象内部属性
	cout<<ptr->age<<endl;
	return 0;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值