1指针、数组 内存


1.指针

int main(
int a = 10;
const int *p = &a ;//常量指针 
int const *p = &a ;//常量指针
int *const p = &a ;//指针常量
system("pause") ;return 0;}
const在的*左边:常量指针,const*的右边:指针常量
const int *const p2 = &b

p2指针的指向不能修改,指针指向空间中的内容也不能修改
常量指针:指针所指空间的值不能发生改变,不能通过指针解引用修改指针所指空间的值,但是指针的指向可以发生改变.
指针常量:指针本身是一个常量,指针的指向不能发生改变,但是指针所指空间的值是可以发生改变的,可以通过指针解引用改变指针所指空间的值.
连续初始化:

int a[][3]={{0,2},{},{3,4,5}};//错误

a不能自己改自己,可以用指针修改,a里的内容确实被修改为20,但是,C++中被const修饰的变量:该变量已经是一个常量,具有替换的作用,编译器在编译代码,在程序中看到对常量中内容读取时,会直接使用常量中的内容替换该常量
在这里插入图片描述

const int a = 10;
int * p = (int* )(&a);&a的类型:const int*,取地址a,即取a的地址
*p = 20;
结果:a=10,*p=20
**a[][]:是数组,数组里放**类型的指针

void:不能定义变量,void*可以定义变量,因为指针大小是确定的,

3.动态内存管理

在这里插入图片描述
堆上频繁的调用newdelete含易产生内存碎片,栈没有这个问题,调用结束之后,系统自动回收。静态分配(程序在编译时就已经知道要多少的空间),栈上既可以动态分配也可以静态分配。

mem系列的函数,操作的基本单位是【字节】,内存操作相关的函数。
柔性数组目的:在结构内,提供一个变长数组,内存标识的一种占位符。
任何空间起始地址=众多地址中最小的那个

为什么要动态内存管理

1.可以申请大块内存,进行较为大型的应用
2.可以在程序运行期间进行申请,可以更灵活的使用内存空间(malloc()
函数free ()函数,既然是函数,必须是程序运行起来才能调用。编译的过程早就已经过了。
堆区:1.整体申请,整体释放,堆空间必须是连续的内存空间,
2.如果申请内存,不归还,会造成内存泄漏(一种严重的问题,可用内存越来越少,进程退出了,内存泄漏问题就不在)
3.申请有大小,free(P)没有告诉我们应该释放多少个字节,free参数只能知道从哪里开始释放,free:取消p内部的地址和堆空间的关系
4.申请的时候,实际申请的空间,会比你要的空间大一些,大出来的部分,用来保存本次申请的”元信息”=属性信息=对应堆空间的大小=内存cookie信息)
5.堆空间适合大块内存申请

缓冲

缓冲区:内存,刷新策略:1行刷新(\n:换行时刷新)2程序结束3强制刷新4文件关闭,c程序,在默认情况下,会打开3个文件(3个设备:键盘,显示器,显示器)对应的:FILE*,stdin,stdout,stderr 对应的:标准输入,标准输出,标准错误。

请添加图片描述

添加链接描述
C(任何语言)程序在运行的时候,默认永远是从上到下依次执行,除非for,函数,if等。
缓存:快的设备给慢的设备做缓存内存。
free只是将p指向的空间释放掉了,并不会p指针置为NULL

malloc、calloc、realloc三个区别

相同点: 都是从堆上申请空间的,最后都需要用free释放,返回值类型都是void*,在使用时都需要强转以及判空

malloc:参数含义表示所需要申请空间的字节数
calloc:
1.参数: 参数1–>元素个数; 参数2–>类型所占的字节数
2.功能:从堆上申请空间&会将空间中的每个字节初始化为0
realloc(void* p, int newsize):
p是空:相当于是malloc
p不是空: realloc会将p指向的空间调整到newsize个字节
假设:p指向的旧空间的大小为oldsize
newsize <= oldsize:将原空间缩小到newsize个字节---->返回原空间的首地址
newsize > oldsize:需要newsize个字节大小的空间
大一点点:直接将原空间扩展到newsize字节,返回的也是原空间的首地址
大许多︰不足以直接扩展,则
1.申请新空间⒉将旧空间中的元素拷贝到新空间3.释放旧空间4.返回新空间
面试题:malloc是怎么实现的?

char x[1024*1024];//在栈上申请1兆空间,崩溃
//方法1:
char *GetMemory (){
char *p = (char *)malloc(100) ;
return p;}
int main(){
char *str = NULL;str = GetMemory () ;//str拿到堆空间
strcpy(str,"hello world") ;
printf(str) ;//结果=hello world
system("pause") ;return 0;}
//方法2:
void *GetMemory (char**p){//p里放str的地址
 *p = (char *)malloc(100) ;//*p是str,把堆给了str,不是把堆给了P,释放p
}
int main(){
char *str = NULL;GetMemory (&str) ;//str拿到堆空间
strcpy(str,"hello world") ;
printf(str) ;}

char*GetMemory (void){
char p[]=''worl''
return p;//p=临时变量,函数调用=消失p
}
int main(){
char *str =NULL;
str = GetMemory ();//worl的空间无效,数据依然存在,str依然指向worl,printf=函数=形成栈帧,对内存重新设置,worl被清空
printf(str) ;//乱码=结果
}
//真正的mymemcpy(memmove)(解决了内存区间重叠):
void *mymemcpy (void *dst,const void *src,int len)
{assert(dst) ;assert(src) ;
char *_dst = (char*)dst;
char *_src = (char*)src;
if (_dst > _src && _dst < _src + len){
//从右(高地址)向左(低地址)拷贝
_dst = _dst + len - 1;//都指向各自的结尾
_src = _src + len - 1;
while (len--) {
*_dst = *_src;
_dst—-;
_src—-;
}}
else {//从左向右
while (len--){
*_dst = *_src;
_dst++;
_src++;
}}
return dst ;}
int main(){
char msg[64] = "hello bit ! \n";
char buffer[64];
mymemcpy (msg+1,msg,strlen (msg)) ;
printf("%s\n", msg) ;//结果=hhello bit!
}

4.指针、数组

指针

clss Sample{
public:
	Sample(int x){
		? ;补充完整,使得程序的运行结果是5
	}
	~Sample(){
		if (p) delete p;
	}
		int show(){
		return *p;
}
		private:int *p;
};
int main(){
	Sample s(5);
	cout << S.show() << endl;
	return 0;
}
p=new int(x);正确
*p=new int(x); *p==x;都错误:p都没有指向,不能解引用
p=&X;错误:p指向的值=5,构造函数结束,x销毁,p指向非法内存空间A,A不—定是new出来的,delete时,会出问题

指针数组

#define INT_PTR int*
typedef int*int_ptr;
INT_PTR a,b;
int_ptr c,d;//只有b不是指针
 int aa[2][5] = {10,9,8,7,6,5,4,3,2,1};&aa的类型是int (*)[2][5]
  int *ptr1 = (int *)(&aa + 1);
  int *ptr2 = (int *)(*(aa + 1));
  printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));//1   6
  aa[0]等价*(aa+0)等价*aa:第0*(aa[0]+1)是9*aa)[2]8,*(a+1)+23

&aa加一操作会导致跳转一个int [2][5]的长度,直接跑到刚好越界的位置。 减一以后回到最后一个位置1处。*(aa + 1)相当于aa[1],即实际是1个1维数组,也就是第二行的首地址,自然是5的位置。减一以后由于多维数组空间的连续性,会回到上一行末尾的6处;
在这里插入图片描述

int a[5] = {5, 4, 3, 2, 1};
  int *ptr = (int *)(&a + 1);
  printf( "%d,%d", *(a + 1), *(ptr - 1));// 4    1int x=10,&x的类型:int*,同理类比,&a的类型就是int(*)[5]即数组指针,a的类型是int [5],a+1&a[1]*(a+1 )即a[1]=4
int*b=a;
*b+=2;数组第1个元素5改为7
*(b+2)=2;3个元素3改为2
b++;指针指向第2个元素4
printf("%d , %d \n”",*b,*(b+2));//*b=4 , *(b+2)=2:指针指向第4个元素

*(a + 1)等同于a[1],第一个是4;&a是个数组指针,加一,指针直接跳过了a全部的元素,直接指在了刚好越界的位置上,然后转换成了int *后再减一,相当于从那个位置向前走了一个int,从刚好越觉得位置回到了1的地址处。数组名只有在&和sizeof之后,才表明整个数组本身,其余都表示首元素的地址。
在这里插入图片描述

void func(char **m){
++m;
cout<<*m<<endl;}
int main(){
static char*a[]={"morning" ,"afternoan", "evening" };
char **p;
p=a;//morning是char*类型,a[]里面放的指针
func(p);}

在这里插入图片描述

只要是传参,一定临时拷贝。回调函数:逆置调用关系,提高效率,

函数指针

int (*a[10])//指针数组,[]优先级高,先和[]结合
char *(*a)[10]//数组指针,先和*结合

A.int* fun(int a, int b);//函数声明
c.int (*fun)(int a, int b);//函数指针
D.(int *)fun(int a, int n);//返回值为int *的函数,D=A

A.int (*(*F)(int, int))(int)//函数指针,指向的函数有两个int形参并且返回一个函数指针,返回的指针指向一个有一个int形参且返回int的函数
B.int (*F)(int, int)//返回值是int
C.int (*(*F)(int, int))//返回值是int *
B.int (*fun)(int *)//参数为int *,返回值为int的函数指针

int(*)(int)//函数指针的数组

int *p = NULL;
int(*p2)[10]=NULL;
 int arr[10] = {0};
 A.p = arr;//左右两边都是int *
B.int (*p2)[10] = &arr;//左右两边都是 int (*)[10],指向整形数组的指针。数组下标也是数组类型的一部分
C.p = &arr[0];//左右两边都是int *
D.p = &arr;//左边是 int *,右边是 int (*)[10],
p2[0][1]*(*(p2+0)+1)

int (*(*p)[10])(int *)//指向含有10个元素的数组的指针,其中每个元素是一个函数指针,该函数的返回值是int,参数是int*

(*(void (*)())0)();//函数调用,函数入口是0号地址,
void (*signal(int , void(*)(int)))(int);//函数声明,第2个参数:函数指针,返回值=函数指针,
 void (*COM)(int, int);//定义没有返回值类型,2个int类型参数的函数指针变量:
typedef int(*COM)(int, int);//函数指针类型,
//
typedef int(*COM)(int, int);
typedef struct Heap
{
	HDataType* array;
	int capacity;
	int size;
	COM Compare;//函数指针变量=Compare
}Heap;

交换 a b

void swap (int* left,int* right){
int temp = *left;
*left = *right;
*right = temp;
}//left, right是pa pb的拷贝。改变指针的指向:交换不成功;改变指针里内容:交换成功

int main(){
int a = 10;
int b = 20;
// Swap(a,b) ;
 //Swap( &a, &b) ;
int* pa = &a;
int* pb = &b;
Swap(pa, pb) ;//交换a b,传a b的地址,如下图
}

图1

void swap (int** left,int** right){
int* temp = *left;
*left = *right;
*right = temp;
}

int main(){
int a = 10;
int b = 20;
int* pa = &a;
int* pb = &b;
Swap(&pa,& pb) ;//交换pa pb,传pa pb的地址,执行完之后如下图
}

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值