指针块

本文深入探讨C++中的指针类型、函数指针的使用、对象指针及this指针的概念,同时详细讲解C++动态内存分配机制,包括new与delete操作符的应用,以及如何避免深层复制和浅层复制带来的问题。

指针类型的函数

函数的返回值是指针(并不是说要传地址进去)
不能返回非静态的局部地址(此地址离开函数就失效了)
下面看一个合法的例子

int *search(int*a,int num)//返回的地址存放的是int型
{  for(int i=0;i<num;i++)
       if(a[i]==0)//传进来的是数组,用数组形式使用也行,但这个并不是数组,是个指针
         return &a[i];}

而动态分配的不会自动被释放,所以可以放心返回

指向函数的指针

指针容纳的是函数代码的起始地址
int(*func)(int, int)
作用:实现函数回调
在处理相似事情时可以灵活运用不同的方法
举例子:
编写一个compute函数,对整数进行各种运算,有一个形参时指向不同运算的函数,要进行不同运算,只需这个形参传的不一样即可

#include<iostream>
using namespace std;
int compute(int a, int b, int(*func)(int, int))//函数指针放到参数表中,具体用哪个函数就把哪个函数的首地址放进去
{
	return func(a, b);
}

int max(int a, int b)
{
	return (a > b ? a : b);
}
int min(int a, int b)
{
	return (a < b ? a : b);
}
int sum(int a, int b)
{
	return (a + b);
}
int main()
{
	int a = 1; int b = 2;
	int ans = compute(a, b, &max);
	cout << "max of a and b is:" << ans << endl;
	ans = compute(a, b, &min);
	cout << "min of a and b is:" << ans << endl;
	ans = compute(a, b, &sum);
	cout << "a and b is:" << ans << endl;
	return 0;

}

对象指针

指向对象的指针,指针前面的类型是类

其调用像结构体那样->

this指针

隐含于类的每一个非静态数据成员中;
指出成员函数所操作的对象
当通过一个对象成员调用函数时,系统先将该对象的地址值给this,然后调用成员函数,成员函数对对象的数据或成员进行操作时,就隐含地使用了this;
例如 point中调用getx时:
return x;
相当于return this->x;(所以我们在编写getx时觉得这个函数中并没有给x,为什么等返回x呢,就是这个原因)

在两个类的定义都用到令一个类时,把谁定义前面都不合适,我们用了前向引用声明

class A;//前向引用声明
class B
{ 
   A x;//在B中构造一个A类对象,虽然系统知道A是什么,但是不知道A具体是什么,所以不能制造A的对象,所以这样是错的
}
class A
{ 
B x;
}

我们可以用指针解决

class A;//前向引用声明
class B
{ 
   A *x;//这种情况很少,只是为了说明一下这样可行
}
class A
{ 
B x;
}

c++动态内存分配

操作符new
new+类型名T(初始化参数列表)
成功了就返回一个T类的指针,指向新分配的内存单元起始地址

delete 来删除用new申请的地址
模拟

point ptr1=new point;//申请内存并把首地址交给指针
delete ptr1;//通过指针直接释放
point prt 2=new point(1,2);//带参数申请并构造这个地址这个

申请和释放动态数组

new 类型名T[数组长度]
delete [ ]数组名T(T必须是由new构造的数组的首地址)(写方括号是释放整个数组)

point *ptr1 =new point[2];//申请一个有两个点对象的数组
int i=ptr[0].getx()//数组形式不用*
delete []ptr;//释放记得方括号

也可以分配多维数组,但是要表明长度,指针是指向数组的指针

下面是动态数组的一个例子,写的有点粗略

#include<iostream>
using namespace std;
class point//定义一个点类
{
public:

	point():x(0),y(0){}//默认构造函数
	point(int x, int y) :x(x), y(y) {}//构造函数
	point(const point& p); //赋值构造函数
	int  getX() { return x; }//对外接口
	int  getY() { return y; }
	~point() { cout << "析构了一个函数" << endl; }//析构函数
	void move(int newx,int newy)//点的移动
	{
		x=(newx);
		y=(newy);
	}
	void showpoint()//输出点
	{
		cout << "(" << x << "," << y << ")" << endl;
	}
private:
	int  x, y;
};

point::point(const point& p)//复制构造函数
{
	x = p.x;
	y = p.y;
	cout << "point复制构造函数被调用" << endl;

}

class array_of_point//动态数组类
{
public:
	array_of_point(int size) :size(size)//使points指向新分配的点内存 ,这是一个、构造函数
	{
		points = new point[size];

	}
	~array_of_point() //析构该类并把申请来的内存释放
	{ 
		cout << "析构了一个动态数组" << endl;
		delete []points;
	}
	point& element(int index)//这是一个访问数组元素的方法,为什么要引用呢,
		//因为我们想要返回的点是左值,就是可以去修改()移动它,返回类型是引用就可以做到这点,返回的是points[index]的引用,点类的引用
	{
		if(index > 0 && index < size)//防止下表越界
		return points[index];
	}
	void show(int index)//用这个指针去调用这个点的输出函数,这里无需引用
	{
		if (index > 0 && index < size)//防止下表越界
		{
			cout << "p[" << index << "]:";
			points[index].showpoint();
		}
	}
private:
	point *points;//类中的一个指针,通过它来对新申请的内存进行操作,这个成员并不需要给外部知道
	int size;//数组的大小

};
int main()
{
	int count;
	cout << "请输入点的个数:";
	cin >> count;
	array_of_point p (count);//在对象p中构造一个大小为count的数组,表示有count个点
	p.element(0).move(5,0);//调用点的移动函数实现对点的初始化,为什么要调用这个,因为在动态申请点的时候使用了默认构造函数,所以不能再用构造函数了
	p.element(1).move(10, 2);
	p.show(0);//输出前两个点
	p.show(1);

	return 0;

}

注意:这个数组是不能以下标的方式访问的,所以我们要在里面定义那么多东西来实现它的功能,而vector可以用下标方式访问

深层复制和浅层复制

若在上面主函数中改一下,构造一个与p一样的点数组p1,由于自己没有写复制构造函数,所以使用默认复制构造函数,p1中也有一个指针points,它指向的类 也是那么多,并且,它指向的地址是和p一样的地址,所以这时对p进行修改操作,也会对p1修改。
这显然不是我们想要的。
而且在释放空间时,p已经把动态内存释放,p1又去释放,就会出错。

int main()
{
	int count;
	cout << "请输入点的个数:";
	cin >> count;
	array_of_point p (count);//在对象p中构造一个大小为count的数组,表示有count个点,
	p.element(0).move(5,0);//调用点的移动函数实现对点的初始化,为什么要调用这个,因为在动态申请点的时候使用了默认构造函数,所以不能再用构造函数了
	p.element(1).move(10, 2);
	array_of_point p1(p);//复制构造一个数组
	p.show(0);//输出前两个点
	p.show(1);

	return 0;

}

针对这个问题,我们给动态数组写一个构造函数就行了:
这个就叫做深层复制。(在复制类的时候不会动用构造函数,只用复制构造函数,而这两个函数的功能不过就是把私有成员初始化等等,所以在编写构造函数时到底要干什么,就是要初始化)

array_of_point::array_of_point(const array_of_point&v)
{
size=v.size;//大小直接复制;
points=new point[size];//重新申请一个新的内存空间;
for(int i=0;i<size;i++)
    {
       point[i]=v.point[i];
    }
  
}

移动构造(c++11)

只是想把对象移动,,而不是真正想复制。即将原对象
的资源控制权全部交给目标对象
当一个对象将要消亡,且里面的内存是还要用的,我们就可以移动掉它
这样定义

array_of_point::array_of_point(const array_of_point &&v)://右值引用,直接把v的东西拿来用而不用再复制一遍,即v只放在右边
size(v.size),points(v.points)
{v.points=nullptr;}//把原指针指向空,这样以后这个原来对象消亡的话就不影响新对象那个地址,又不用构造一个新的
<think>嗯,用户问的是calloc在分配内存时,是否会将内存初始化为0,以及是否能将指针设为NULL,这两个是不是同一个情况。我需要先确认calloc的具体行为。 首先,calloc的作用是分配内存并初始化为0。用户提到的“指针变成NULL”可能是指当calloc用于分配指针数组时,每个指针都被初始化为NULL。这其实是正确的,因为在C语言中,NULL指针在内存中的表示可能全为0,所以calloc将内存初始化为0后,指针的值就变成了NULL。 但要注意,虽然全0的内存对应指针是NULL,但严格来说,这取决于系统实现,不过大多数现代系统都支持这一点。因此,calloc分配的指针数组确实会被初始化为NULL。 接下来需要解释calloc的基本用法,和malloc的区别,以及初始化为0的具体含义。还要区分分配普通数据类型和指针类型时的情况。比如,分配int数组时初始化为0,而分配指针数组时初始化为NULL,本质上都是内存清零,但表现形式不同。 然后用户可能混淆了“初始化为0”和“设置为NULL”,需要明确这两者在内存层面是一样的,但概念上不同。NULL是针对指针的,而0是针对数值的。同时要指出calloc的局限性,比如不能保证在所有平台上都正确初始化浮点数或结构体。 最后,根据用户之前的问题,可能需要进一步解释内存分配的不同方法,或者如何安全使用calloc,避免常见错误。相关问题可以包括calloc与malloc的区别、内存初始化的安全性、多维数组分配等。</think>`calloc` 的核心行为是 **分配内存并初始化为0**,而将指针数组初始化为 `NULL` 是其副作用(因为 `NULL` 在内存中通常表示为全0)。具体区别和联系如下: --- ### **1. `calloc` 的基本行为** ```c void* calloc(size_t num, size_t size); ``` - **功能**:分配 `num` 个大小为 `size` 的内存,并将所有字节初始化为 `0`。 - **返回值**:指向分配内存的指针,若失败返回 `NULL`。 - **关键点**: - 分配的内存内容全为 `0`(二进制全0)。 - 与 `malloc` 的区别:`malloc` 不初始化内存,内容是随机的“脏数据”。 --- ### **2. 指针数组初始化为 `NULL` 的原理** 当使用 `calloc` 分配指针数组时: ```c int **arr = (int**)calloc(10, sizeof(int*)); ``` - 每个指针(`int*` 类型)占用的内存会被初始化为 `0`。 - 在大多数系统中,`NULL` 的定义是 `(void*)0` 或 `0`,因此指针值会变为 `NULL`。 - **注意**:这是基于 `NULL` 的二进制表示为全0的隐含假设(现代系统均支持)。 --- ### **3. 初始化为0的通用性** - **数值类型**:如 `int`、`float` 等,初始化为 `0` 是安全的。 - **指针类型**:初始化为 `NULL` 是安全的(依赖系统实现)。 - **复杂类型**:如结构体,其所有成员会被初始化为 `0` 或 `NULL`(适合简单结构体)。 - **例外情况**:浮点数的 `0` 表示可能依赖平台(但 `calloc` 仍保证初始化为全0)。 --- ### **4. 示例对比** ```c // 分配并初始化整型数组 int *a = (int*)calloc(3, sizeof(int)); // a[0] = 0, a[1] = 0, a[2] = 0 // 分配并初始化指针数组 int **p = (int**)calloc(3, sizeof(int*)); // p[0] = NULL, p[1] = NULL, p[2] = NULL // 分配结构体数组 typedef struct { int x; int* ptr; } S; S *s = (S*)calloc(1, sizeof(S)); // s->x = 0, s->ptr = NULL ``` --- ### **5. 常见误区** - **误区**:`calloc` 保证所有类型的初始化值都符合逻辑(如浮点数 `0.0`)。 - **真相**:`calloc` 只保证内存全为 `0`,但某些平台可能需要 `memset` 或手动初始化。 - **误区**:`calloc` 分配的内存永远安全使用。 - **真相**:若分配失败(返回 `NULL`),直接解引用会导致崩溃,需检查返回值。 --- ### **总结** | 行为 | `calloc` 的表现 | |---------------------|-------------------------------------| | 内存初始化 | 所有字节为 `0`(二进制全0) | | 数值类型初始化 | `0`(如 `int`, `float`) | | 指针类型初始化 | `NULL`(依赖系统实现) | | 结构体成员初始化 | 所有成员为 `0` 或 `NULL` | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值