c++指针的一切问题

1】、变量地址: 
&+变量名  获取变量在内存中的起始地址

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a;
	char b;
	bool c;
	string d;
	//16进制表示 
	cout<<"变量a的地址:"<<(void*)&a<<endl;
	cout<<"变量b的地址:"<<(void*)&b<<endl;
	cout<<"变量c的地址:"<<(void*)&c<<endl;
	cout<<"变量d的地址:"<<(void*)&d<<endl;
	//10进制表示 
	cout<<"变量a的地址:"<<(long long)&a<<endl;
	cout<<"变量b的地址:"<<(long long)&b<<endl;
	cout<<"变量c的地址:"<<(long long)&c<<endl;
	cout<<"变量d的地址:"<<(long long)&d<<endl;
    return 0;
}

2】、指针变量
用于存放变量在内存中的起始地址
表示: 数据类型 *变量名 
3】、对指针赋值(指向某变量,被指向的变量的数据类型称为"基类型")
表示: 指针=&变量名 

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int *pa = &a;//(让指针pa指向变量a,pa的基类型是int)
	char *pb = &b;
	bool *pc = &c;
	string *pd = &d;
	cout<<"变量a的地址:"<<(long long)pa<<endl;
	cout<<"变量b的地址:"<<(long long)pb<<endl;
	cout<<"变量c的地址:"<<(long long)pc<<endl;
	cout<<"变量d的地址:"<<(long long)pd<<endl;
//如果指针的数据类型与基类型不符,会报错,但是可以强制转换类型
	int*pe = (int*)&b;
    return 0;
}

4】、指针占用的内存

在64位操作系统中,不管什么类型的指针,占用的内存都是8字节

#include<bits/stdc++.h>
using namespace std;
int main()
{
	cout<<sizeof(pa)<<endl;
	cout<<sizeof(pb)<<endl;
	cout<<sizeof(pc)<<endl;
	cout<<sizeof(pd)<<endl;
	cout<<sizeof(int*)<<endl; 
	cout<<sizeof(char*)<<endl; 
	cout<<sizeof(bool*)<<endl; 
	cout<<sizeof(string*)<<endl; 
	return 0;
}

5】、指针的使用
(指针存放变量的地址,因此,指针名表示的是地址,就像变量名可以表示变量的值一样)
*运算符被称为间接值或解引用值,将它用于指针,可以得到该地址的内存中存储的值

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a = 3;
	int *p = &a;
	//p是a的地址
	//*p相当于a 
	cout<<"a="<<a<<endl;
	cout<<"*p="<<*p<<endl;
	*p = 8;//更改值 
	cout<<"a="<<a<<endl;
	cout<<"*p="<<*p<<endl;
	return 0;
}

变量和zhizhend
如图:

(!但是也不是很贴切,因为变量和指向变量的指针并不是一对一的关系,多个指针可以指向同一个变量) 

6】、const修饰指针 

1、常量指针 (最常用)
    const 数据类型*变量名 
    1)不能通过解引用的方法修改内存地址中的值(用原始的变量名可以修改)
    2)指向的变量可以改变(之前指向变量a的,后来可以改为指向变量b)
    3)一般用于修饰函数的形参,表示不希望在函数里修改内存地址中的值
    4)如果用于形参,虽然指向的对象可以改变,但没有意义
    5)如果形参的值不需要改变,建议加上const修饰,程序可读性更好 
    

#include<bits/stdc++.h>
using namespace std;
int main()
{ 
	int a = 3, b = 8;
	const int*p = &a;
	*p = 13;//会报错
	return 0;
}

2、指针常量(引用)
数据类型*const变量名
指向的变量(对象)不可改变
但是可以通过解引用的方法修改内存地址中的值 

#include<bits/stdc++.h>
using namespace std;
int main()
{ 
	int a = 3, b = 8;
	int*const p = &a;
	*p = 13;
	p = &b;//会报错 
	return 0;
}

3、常指针常量 (常引用)
const数据类型*const变量名
指向的变量(对象)不可改变,不能通过解引用的方法修改内存地址中的值 

//三种总结对比

7】、void关键字的用途 
void表示无类型 
1、函数的参数用void*,表示接受任意数据类型的指针(重要) 
2、函数返回值用void,表示函数没有返回值
3、函数参数用void,表示函数不需要参数(或者让参数列表空着)

#include<bits/stdc++.h>
using namespace std;
//显示变量的十六进制地址的函数,varname-变量名,addr-变量的地址
void fun(string varname, int&p)
{
	cout<<varname<<"的地址是:"<<p<<endl; 
}
int main()
{ 
	int a;
	char b;
	cout<<"a的地址是:"<<&a<<endl;
	cout<<"b的地址是:"<<&b<<endl;
	fun("a", &a); 
	fun("b", &b);//会报错,需要强制转换 
	return 0;
}

调用b会报错

但是如果改为void,则不会有问题

#include<bits/stdc++.h>
using namespace std;
//显示变量的十六进制地址的函数,varname-变量名,addr-变量的地址
void fun(string varname, void&p)
{
	cout<<varname<<"的地址是:"<<p<<endl; 
}
int main()
{ 
	int a;
	char b;
	cout<<"a的地址是:"<<&a<<endl;
	cout<<"b的地址是:"<<&b<<endl;
	fun("a", &a); 
	fun("b", &b);
	return 0;
}

 1)不能用void声明变量,它不能代表一个真实的变量
 2)不能对void*指针直接解引用(需要转换成其他类型指针)
 3)把其它类型的指针赋值给void*指针不需要转换
 4)把void*指针赋值给其他类型的指针则需要转换 

8】、c++内存模型

 

9】、 动态分配内存 new和delete
(在程序中,局部变量存放在栈区,栈区的效率很高,但是空间很小,如果需要处理大量的数据,就必须使用堆区的内存)

#include<bits/stdc++.h>
using namespace std;
int main()
{ 
	int *p = new int(5);
	cout<<"*p="<<*p<<endl;//5
	*p = 8;
	cout<<"*p="<<*p<<endl;//8
	delete p;//释放这块内存 

	return 0;
}

 10】、二级指针
(指针是指针变量的简称,也是变量,是变量就有地址) 
(指针是用来存放普通变量的地址,二级指针是用来存放指针变量的地址)
(在函数中,如果传递普通变量的地址,形参用指针;传递指针地址,形参用二级指针) 
声明二级指针的语法:数据类型**指针名
使用指针的目的:1)传递地址;2)存放动态分配的内存的地址

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int ii = 8;
	cout<<"ii="<<ii<<",ii的地址是:"<<&ii<<endl;
	int *pii = &ii;
	cout<<"pii="<<pii<<",pii的地址是:"<<&pii<<endl;
	int **ppii = &pii;
	cout<<"ppii="<<ppii<<",ppii的地址是:"<<&ppii<<endl;
	return 0;
}



将分配内存的代码放在另一个函数中:

#include<bits/stdc++.h>
using namespace std;
void func(int **pp)
{
	*pp = new int(3);//为形参pp分配内存
	cout<<"pp="<<pp<<",*pp="<<*pp<<endl;
}
int main()
{
	int*p=new int(3);//将内存中的值设置为3
	cout<<"p="<<p<<"*p="<<*p<<endl; //*p=3
	//如果业务要求把分配内存的代码放在另一个函数中
	//需要把指针p的地址传给另一个函数
	func(&p);
	return 0;
}

11】、空指针

1)对空指针解引用,程序会崩溃;

int*p=0;

在函数中,应该有判断形参是否为空指针的代码,目的是保证代码的健壮性 
c++11中用nullptr代替0 ,在linux平台下,编译需要加-std=c++11参数 
12]、野指针

 

 13】、一维数组和指针

1、指针的算数
将一个整型变量加1后,其值将增加1
但是,将指针变量(地址的值)加1后,增加的量等于它指向的数据类型的字节数 

#include<bits/stdc++.h>
using namespace std;
int main()
{
	char a;   //sizeof 1字节 
	short b;  //sizeof 2字节 
	int c;    //sizeof 4字节 
	double d; //sizeof 8字节 
	cout<<"a的地址是:"<<(void*)&a<<endl;
	cout<<"a+1的地址是:"<<(void*)(&a+1)<<endl;
	
	cout<<"b的地址是:"<<(void*)&b<<endl;
	cout<<"b+1的地址是:"<<(void*)(&b+1)<<endl;
	
	cout<<"c的地址是:"<<(void*)&c<<endl;
	cout<<"c+1的地址是:"<<(void*)(&c+1)<<endl;
	
	cout<<"d的地址是:"<<(void*)&d<<endl;
	cout<<"d+1的地址是:"<<(void*)(&d+1)<<endl;
	return 0;
}

 2、数组的地址
1)数组在内存中占用的空间是连续的
2)c++将数组名解释为数组第0个元素的地址(和数组首地址的取值是相同的) 
3) 数组第n个元素的地址是:数组首地址+n
4)c++编译器将数组名[下标]解释为*(数组首地址+下标) 

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[5];
	cout<<"a的值是:"<<(long long)a<<endl;
	cout<<"&a的值是:"<<(long long)&a<<endl;
	cout<<"a[0]的地址是:"<<(long long)&a[0]<<endl;
	cout<<"a[1]的地址是:"<<(long long)&a[1]<<endl; 
	cout<<"a[2]的地址是:"<<(long long)&a[2]<<endl; 
	cout<<"a[3]的地址是:"<<(long long)&a[3]<<endl; 
	cout<<"a[4]的地址是:"<<(long long)&a[4]<<endl;  
	return 0;
}

3、数组的本质
数组是占用连续空间的一块内存,数组名被解释为数组第0个元素的地址。
c++操作这块内存的方法:数组解释法和指针表示法,它们是等价的。

4、数组名不一定被解释为地址
将sizeof运算符用于数组时,将返回整个数组占用内存空间的字节数
可以修改指针的值,但是数组名是常量,不可修改

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[5];
	int *p = a;
	cout<<a<<endl;//0x6ffe00
	cout<<p<<endl;//0x6ffe00
	p++;
	cout<<p<<endl;//0x6ffe04
	a++;//报错 
	return 0;
} 

14】、一维数组用于函数的参数
1、指针的数组表示
1)c++编译器把 数组名[下标] 解释为 *(数组首地址+下标)
2)c++编译器把 地址[下标] 解释为 *(地址+下标)

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[5] = {3, 6, 5, 8, 9};
	//用数组表示法操作数组
	for(int ii = 0; ii < 5; ii++)
	{
		cout<<"a["<<ii<<"]的值是:"<<a[ii]<<endl;
	}
	cout<<(&a[2])[0]<<endl;//第二个元素的地址+[0] 解释为*(第二个元素的地址+0) 
	//用指针表示法操作数组 
	int *p = a;//p是整型指针 
	for(int ii = 0; ii < 5; ii++)
	{
		cout<<"*(p+"<<ii<<")的值是:"<<*(p+ii)<<endl;//数组名[下标] 解释为 *(数组首地址+下标) 
		cout<<"p["<<ii<<"]的值是:"<<p[ii]<<endl;//地址[下标] 解释为 *(地址+下标)//把整型指针p当成了数组名来使用 
	}
	return 0;
} 

 2、一维数组用于函数的参数

#include<bits/stdc++.h>
using namespace std;
int main()
{
	char a[20];
	int *p = (int*)a;
	for(int i = 0; i < 5; i++)
	{
		p[i] = i+300;
	}
	for(int i = 0; i < 5; i++)
	{
		cout<<p[i]<<endl;
		cout<<*(p+i)<<endl;
	}
	return 0;
} 

 
 

15】、用new动态创建一维数组
普通数组在栈上分配内存,栈很小;如果需要存放更多的元素,必须在堆上分配内存
动态创建一维数组的语法:数据类型*指针=new 数据类型[数组长度]
释放一维数组的语法:delete[]指针
注:
1)普通数组有数组名,可以用sizeof运算得到整个数组占用内存空间的大小;但是,动态创建的数组没有数组名,只有一个指针,不能用sizeof运算符
2)可以用数组表示法和指针表示法两种方式使用动态创建的数组
 

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int *arr=new int[8];//创建八个元素的整型数组
	for(int i = 0; i < 8; i++)
	{
		arr[i] = 100+i;
		cout<<"arr["<<i<<"]="<<*(arr+i)<<endl;//指针表示法 
	}
	delete[]arr;
	
	//用栈 空间很小 
	int a[1000001];
	//用堆放内存 数组加到100亿程序才会崩溃 
	int *a = new int[10000000001];
	//但是如果用(std::nothrow),不会崩溃,会返回nullptr
	int *a=new(std::nothrow) int[10000000001];
	//再判断指针是否为空 
	if(a == nullptr)
	{
		cout<<"分配内存失败。\n"; 
	}
	else
	{
		a[10000000001]=8;
		delete[]a;
	}
	return 0;
} 
 
 
 

16】、二维数组用于函数的参数
回顾:
int*p;//整型指针
int*p[3];//一维整型指针数组,元素是3个整型指针(p[0]p[1]p[2])
int*p();//函数p的返回值类型是整型的地址,调用函数时可以用一个整型指针接收它的返回值
int(*p)(int,int);//p是函数指针,函数的返回值是整型
 (指针是容器,用于存放地址,不要把两者混淆)

1、行指针(数组指针)
声明:数据类型(*行指针名)[行的大小];//行的大小即数组长度
int(*p)[3]//p是行指针,用于指向数组长度为3的int型数组
double(*p2)[5]//p2是行指针,用于指向数组长度为5的double型数组
一维数组名取地址得到的是数组的地址,是行地址

int main()
{
	int a[10];
	//存放数组a的地址,不能用普通指针
	int*p1=a;
	cout<<p1<<endl;//首地址 
	int*p2=&a;//报错
	cout<<p2<<endl;
	int(*p3)[10]=&a; 
	cout<<p3<<endl;//首地址 
	return 0;
} 

2、二维数组名是行地址

int main()
{
	int bh[2][3]={{11,12,13},{21,22,23}};
	//bh是二维数组名,该数组有两元素,每一个元素本身又是一个数组长度为3的整型数组
	//bh被解释为数组长度为3的整型数组类型的行地址
	//如果存放bh的值,要用数组长度为3的整型数组类型的行指针
	int(*p)[3]=bh;//可以 
	return 0;
}

3、把二维数组传递给函数
如果把bh传给函数,函数的声明如下:
void func(int(*p)[3], int len);
void func(int p[][3], int len); 

#include<bits/stdc++.h>
using namespace std;
void func(int p[][3], int len)
{
	for(int i = 0; i < len; i++)
		for(int j = 0; j < 3; j++)
			cout<<"p["<<i<<"]["<<j<<"]="<<p[i][j]<<endl;
}
int main()
{
	int bh[2][3]={{11,12,13},{21,22,23}};
	func(bh, 2);//二维数组名,二维数组的行数
	return 0;
}

17】、函数指针
如果把函数的地址作为参数传递给函数,就可以在函数中灵活的调用其它函数
使用函数指针的三个步骤:
1)声明函数指针
int(*pfa)(int,string); 
2)让函数指针指向函数的地址
3)通过函数指针调用函数

#include<bits/stdc++.h>
using namespace std;
void func(int no, string str)
{
	cout<<no<<' '<<str<<endl;
}
int main()
{
	int bh = 3;
	string message = "hello";
	func(bh,message);
	void(*pfunc)(int,string);//声明函数的函数指针
	pfunc=func;//对函数指针赋值,语法是函数指针名=函数名 
	pfunc(bh,message);//对函数指针名调用函数 
	return 0;
}

以上是我对于学习指针相关内容做的笔记

如有不正确 欢迎指正
希望留个赞~ 

谢谢阅读

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值