我一直在阅读一些书籍,当谈到使用指针/动态内存(或堆或他们称之为的 w/e)的类/函数时,我开始感到困惑。
有没有人有一个简单的....像他们可以展示的简单示例,因为我使用的书籍使用了过于复杂的示例(大型类或多个函数),并且很难理解。无论如何,指针一直是我的弱点,但我理解 BASIC 指针,只是使用它们的类/函数有点令人困惑。
另外......你什么时候使用它们是另一个问题。
我一直在阅读一些书籍,当谈到使用指针/动态内存(或堆或他们称之为的 w/e)的类/函数时,我开始感到困惑。
有没有人有一个简单的....像他们可以展示的简单示例,因为我使用的书籍使用了过于复杂的示例(大型类或多个函数),并且很难理解。无论如何,指针一直是我的弱点,但我理解 BASIC 指针,只是使用它们的类/函数有点令人困惑。
另外......你什么时候使用它们是另一个问题。
堆栈分配:
char buffer[1000];
这里 1000 必须是一个常数。buffer
超出范围时会自动释放内存。
堆分配:
int bufsz = 1000;
char* buffer = new char[bufsz];
//...
delete [] buffer;
这里 bufsz 可以是一个变量。必须显式释放内存。
何时使用堆:
您计算机的 RAM 是一大堆依次排列的字节,每个字节都可以通过其地址独立访问:从零开始向上的整数。指针只是一个变量,用于保存内存中单个位置的地址。
由于 RAM 是一大块字节,CPU 通常将一大堆字节分成几个块。最重要的是:
代码块是汇编代码所在的位置。堆是一个用于分配的大字节池:
new
C++ 或 C 上的操作malloc()
。堆栈是用于存储的内存块:
Stack 和 Heap 的主要区别在于它的使用方式。虽然堆是一个大字节池,但堆栈“增长”就像一堆盘子:你不能删除底部的盘子,除非它的顶部没有更多盘子。
这就是递归的实现方式:每次递归调用函数时,内存都会在堆栈上增长,分配参数、局部变量并存储返回函数的返回值,一个在另一个之上,就像一堆盘子一样。
堆栈上的数据与堆上的数据具有不同的“寿命”。一旦函数退出,局部变量的数据就会丢失。
但是,如果您在堆上分配数据,则该数据不会丢失,除非您使用delete
orfree()
操作明确释放该数据。
指针基本上是一个变量,它包含另一个变量的内存地址(或者在其他情况下指向一个函数,但让我们关注第一个)。
这意味着,如果我声明int[] x = {5,32,82,45,-7,0,123,8};
该变量将分配给某个地址的内存,可以说它是0x00000100
通过地址分配的,0x0000011F
但是我们可以有一个变量来指示某个内存地址,我们可以使用它来访问它。
所以,我们的数组看起来像这样
Address Contents
0x00000100 1
0x00000104 32
0x00000108 82
0x0000010B 45
0x00000110 -7
0x00000114 0
0x00000118 123
0x0000011B 8
例如,如果我们要创建一个指向数组开头的指针,我们可以这样做:int* p = &x;
想象这个指针变量被创建了一个内存地址0x00000120
,该地址处的内存将包含数组开头的内存位置x
。
Address Contents
0x00000120 0x00000100
然后,您可以通过取消引用指针来通过指针访问该地址处的内容,这样int y = *p
会导致y = 1
. 我们也可以移动指针,如果我们这样做p += 3;
,指针将向前移动 3 个地址(但是请注意,它移动的大小是它所指向的对象类型的 3 倍,这里我用 32位系统,其中一个 int 为 32 位或 4 个字节长,因此地址将移动 4 个字节,每次递增或总共 12 个字节,因此指针最终指向0x0000010B
),如果我们要p
再次取消引用,y = *p;
那么我们最终会有y = 45
。这只是一个开始,你可以用指针做很多事情。
其他主要用途之一是将指针作为参数传递给函数,以便它可以对内存中的某些值执行操作,而不必复制所有这些值或进行将在函数范围之外持续存在的更改。
警告:不要这样做。这就是我们有向量的原因。
如果你想创建一个数据数组,并从函数返回 if,你会怎么做?
显然,这不起作用:
int [10] makeArray(int val)
{
int arr[10];
for(int i=0; i<10; ++i)
arr[i] = val;
return arr;
}
您不能从函数返回数组。我们可以使用指针来引用数组的第一个元素,如下所示:
int * makeArray(int val)
{
int arr[10];
for(int i=0; i<10; ++i)
arr[i] = val;
return &(arr[0]); // Return the address of the first element.
// Not strictly necessary, but I don't want to confuse.
}
然而,这也失败了。arr 是一个局部变量,它进入堆栈。当函数返回时,数据不再有效,现在你有一个指向无效数据的指针。
我们需要做的是声明一个即使在函数退出后仍然存在的数组。为此,我们使用关键字 new 创建该数组,并将地址返回给我们,该地址需要存储在指针中。
int * makeArray(int val)
{
int * arr = new int[10];
for(int i=0; i<10; ++i)
arr[i] = val;
return arr;
}
然后您可以调用该函数并像这样使用该数组:
int * a = makeArray(7);
for(int i=0; i<10; ++i)
std::cout << a[i] << std::endl;
delete [] a; // never forget this. Obviously you wouldn't do it right
// away like this, but you need to do it sometime.
将指针与 new 一起使用还为您提供了一个优势,即您可以在运行时确定数组的大小,这是本地静态数组无法做到的(尽管在 C 中可以):
int * makeArray(int size, int val)
{
int * arr = new int[size];
for(int i=0; i<size; ++i)
arr[i] = val;
return arr;
}
这曾经是指针的主要用途之一。但就像我在顶部所说的那样,我们不再这样做了。我们使用vector
.
指针的最后遗迹之一不适用于动态数组。我唯一一次使用它们是在我希望一个对象可以访问另一个对象的类中,而不给它该对象的所有权。因此,对象 A 需要了解对象 B,但即使对象 A 消失了,这也不会影响对象 B。您也可以为此使用引用,但如果您需要为对象 A 提供更改哪个对象的选项,则不需要它可以访问。
(未经测试,只是写下来。并按要求故意保持原始状态。)
int* oneInt = new int; // allocate
*oneInt = 10; // use: assign a value
cout << *oneInt << endl; // use: retrieve (and print) the value
delete oneInt; // free the memory
现在是一个整数数组:
int* tenInts = new int[10]; // allocate (consecutive) memory for 10 ints
tenInts[0] = 4353; // use: assign a value to the first entry in the array.
tenInts[1] = 5756; // ditto for second entry
//... do more stuff with the ints
delete [] tenInts; // free the memory
现在有了类/对象:
MyClass* object = new MyClass(); // allocate memory and call class constructor
object->memberFunction("test"); // call a member function of the object
delete object; // free the object, calling the destructor
那是你想要的吗?我希望它有所帮助。
我想这就是你要问的:
基本上 C++ 不允许可变大小的数组。C++ 中的任何数组都必须指定一个非常具体的大小。但是您可以使用指针来解决这个问题。考虑以下代码:
int *arry = new int[10];
这只是创建了一个包含 10 个元素的整数数组,并且与此几乎完全相同:
int arry[] = int[10];
唯一的区别是每个人都将使用一组不同的语法。但是想象一下尝试这样做:
Class class:
{
public:
void initArry(int size);
private:
int arry[];
};
void class::initArry(int size)
{
arry = int[size]; // bad code
}
无论出于何种原因,C++ 都被设计为不允许为常规数组分配在运行时确定的大小。相反,它们必须在编码时分配大小。然而,在 C++ 中创建数组的另一种方法 - 使用指针 - 没有这个问题:
Class class:
{
public:
~class();
void initArry(int size);
private:
int *arry;
};
class::~class()
{
delete []arry;
}
void class::initArry(int size)
{
arry = new int[size]; // good code
}
您必须在第二个示例中进行一些内存清理,这就是为什么我包含析构函数的原因,但是通过使用指针,您可以在运行时调整数组的大小(具有可变大小)。这叫做动态数组,据说这里的内存是动态分配的。另一种是静态数组。
就二维数组而言,您可以像这样处理它:
Class class:
{
public:
~class();
void initArrays(int size1, int size2);
private:
int **arry;
};
class::~class()
{
delete [] arry[0];
delete [] arry[1];
delete [] arry;
}
void class::initArrays(int size1, int size2)
{
arry = new int*[2];
arry[0] = new int[size1];
arry[1] = new int[size2];
}
免责声明:我已经有一段时间没有使用这种语言做了很多事情了,所以我在某些语法上可能有点不正确。