C++指针/数组(我现在完全困惑,真的需要帮助)

C++ Pointers/Arrays(I'm completely and utterly confused at this moment and really need help)

本文关键字:真的 帮助 数组 指针 C++      更新时间:2023-10-16

这就是我最终想出的。我目前无法弄清楚如何将我的反向数组函数放入主...如果我只是将代码复制并粘贴到其中,则每次运行时都会崩溃......我不知道为什么它会导致它崩溃或任何东西。感谢到目前为止帮助我的所有人。

using namespace std;
// Prototype for printArray goes here
void reverseArray(int*, int);
void printArray(int*, int);
int main()
{
int size;   // size of the dynamically allocated array
// Declare as needed for a dynamically allocated array of 
ints named "data".
// Declare other variables as needed

// Edit to display your own name
cout << "" << endl << endl;
// Prompt the user for the array size
cout << "Array size: ";
cin >> size;
// Add code to validate array size, so it is greater than one
while (size < 2)
{
cout << "Array size must be greater than 1: ";
cin >> size;
}
// Add code to dynamically allocate "data". Don't forget to release the memory before
// the program ends
int *data = new int[size],
*p = data;
// Write a loop to fill the "data" array with random numbers from 1 - 100 (inclusive)
// This code must use POINTER NOTATION (no subscripting) to work with the array.
// Reminder: neither of these notations is acceptable here:
// data[n]  or *(data + n)
// Instead this code will use pointer incrementing/decrementing and dereferencing
for (int i = 0; i < size; i++, p++)
{
*p = rand() % 100 + 1;
}
// Call function to print the original "data" array
cout << "nOriginal array:n" << endl;
printArray(data, size);
// Reset "data" to point to the beginning of the array 
// Add code to reverse the array. Use 2 pointers: one starts at the beginning of the array and
// moves forward, the other starts at its last element and works backward. Swap the values they
// point to.
// Reminder: neither of these notations is acceptable here:
// data[n]  or *(data + n)
// Instead this code will use pointer incrementing/decrementing and dereferencing
// For this, I made the function reverseArray instead of coding it in main.
reverseArray(data, size);
cout << endl;
cout << "nReversed array:n" << endl;
printArray(data, size);
cout << endl << endl;
// Finish up
delete[] data;
system("pause");
return 0;
}
// Function printArray() goes here. Print the array, 5 numbers per line, 
right-aligned
void printArray(int*p, int size)
{
for (int i = 0; i < size; i++, p++)
{
cout << setw(5) << right << *p;
if ((i + 1) % 5 == 0)
{
cout << endl;
}
}
}
// Function reverseArray() Reverses the array.
void reverseArray(int *data, int size)
{
int *e = data + size - 1;      // Pointer at the end
for (; e > data; data++, e--) // while end pointer (e)> start pointer, swap start w/ end
{
int arrayFlip = *data;
*data = *e;
*e = arrayFlip;
}
}

你可能无缘无故地折磨自己,或者把头撞到砖墙上(别担心 - 我们都去过那里......并有瘀伤来证明这一点。

首先,让我们从任何分配的内存块开始,比如:

int *a = new int[NELEM], ...

什么是a?(一个指针 - 是的,但指向什么?它是指向内存块中起始地址的指针,大小为NELEM * sizeof *a字节。它是什么类型的指针?(int)。每个整数多少字节?(一般4)。

那么,为什么让指针成为类型int很重要呢?(好吧,它设置了控制指针算术在通过该指针引用内存块时如何操作的类型大小)这意味着由于您的指针类型是int,编译器知道a + 1a + 4-bytes,这允许您引用内存块中的下一个值。

好的,但是我为a分配了内存,我对a的责任是什么?在您编写的任何动态分配内存的代码中,对于分配的任何内存块,您有 2个责任:(1)始终保留指向内存块起始地址的指针,以便 (2) 当不再需要内存块时可以释放它。

这对我意味着什么?这意味着,如果您不能简单地递增a(例如a++)在声明a的范围内。如果这样做,您将丢失对块的起始地址的引用,并且无法再释放该块(这是内存泄漏)。

因此,如果我无法使用任何索引(例如a[i]*(a + i)),并且我不能增加指针a- 那么我有什么选择?使用另一个指针...,例如

int *a = new int[NELEM],
*p = a;
...
std::cout << "array  : ";
for (int i = 0; i < NELEM; i++, p++) {
*p = rand() % 100 + 1;
std::cout << std::setw(5) << *p;
}
std::cout << 'n';

您是否履行了分配给a的内存块的责任?当然,a仍然指向块的起始地址,因此可以释放它。您所做的只是使用第二个指针p并使用p进行迭代,a保持不变。

嗯。。使用第二个指针..我想知道我是否可以使用相同的方案反转我的数组。是的。在最简单的形式中,您可以执行以下操作:

void rev (int *a, size_t size)
{
int *e = a + size - 1;      /* end pointer */
for (; e > a; a++, e--) {   /* while end > start, swap start, end */
int tmp = *a;
*a = *e;
*e = tmp;
}
}

但是等等!!您说您无法在不丢失我分配的块的起始地址的情况下增加a- 我现在如何free它?(amain()永远不会改变,函数rev接收a的副本,并且在rev内存块的范围内,您可以自由地增加/减少或做任何您喜欢a的事情,在内存块的范围内,因为rev中的amain()中的原始指针有自己的(并且非常不同)的地址。

(题外话...你可以在rev内声明第三个指针,例如

int *s = a,                 /* start pointer */
*e = a + size - 1;      /* end pointer */

然后在迭代和交换中使用s而不是a,但没有必要。如果您更清楚正在使用哪个指针,则可以自由地这样做。它只是另一个 8 字节(或 x86 上的 4 个字节),因此额外的存储空间不是问题。

把它放在一个简短的例子中,你可以做类似于下面的事情:

#include <iostream>
#include <iomanip>
#include <cstdlib>
#define NELEM 10
void rev (int *a, size_t size)
{
int *e = a + size - 1;      /* end pointer */
for (; e > a; a++, e--) {   /* while end > start, swap start, end */
int tmp = *a;
*a = *e;
*e = tmp;
}
}
int main (void) {
int *a = new int[NELEM],
*p = a;
srand (20180502);
std::cout << "array  : ";
for (int i = 0; i < NELEM; i++, p++) {
*p = rand() % 100 + 1;
std::cout << std::setw(5) << *p;
}
std::cout << 'n';
rev (a, NELEM);
p = a;
std::cout << "reverse: ";
for (int i = 0; i < NELEM; i++, p++)
std::cout << std::setw(5) << *p;
std::cout << 'n';
delete[] a;
}

示例使用/输出

$ ./bin/array_reverse
array  :    11    6   78   93   25   71   82   58   97   68
reverse:    68   97   58   82   71   25   93   78    6   11

所有这些都需要一点时间才能沉浸其中。我们的额头上都有同一堵墙上的瘀伤。只要接受这样一个事实,即指针只是一个变量,它保存其他东西的地址作为它的值(例如,它指向其他东西的存储位置)。

了解指针的type如何影响指针算术(和索引),例如,有多少字节以p++for (i = 0; i < size; i++) p[i]前进,并确保你确切地知道指针指向的位置,事情应该开始到位。

如果你在弄清楚指针发生了什么时遇到任何问题,拿出一张8.5 x 11的纸和一支2号铅笔,把它画出来——在每次迭代时填写你的指针指向的块,等等——它真的很有帮助。一旦你画了足够的图表,完成了足够的链表、堆栈等......你不会像现在这样需要纸张(你仍然需要它 - 所以请随身携带)


使用函数反转main()

作为对您的评论的回应,当您查看main()时,您已经声明了一个额外的指针p。因此,您可以简单地将其用作开始指针,并从rev()函数中添加e作为结束指针。一个简单的实现是:

int main (void) {
int *a = new int[NELEM],
*p = a,
*e = a + NELEM - 1;;
srand (20180502);
std::cout << "array  : ";
for (int i = 0; i < NELEM; i++, p++) {
*p = rand() % 100 + 1;
std::cout << std::setw(5) << *p;
}
std::cout << 'n';
p = a;                      /* reset pointer */
for (; e > p; p++, e--) {   /* reverse array */
int tmp = *p;
*p = *e;
*e = tmp;    
}
p = a;                      /* reset pointer -- again */
std::cout << "reverse: ";
for (int i = 0; i < NELEM; i++, p++)
std::cout << std::setw(5) << *p;
std::cout << 'n';
delete[] a;
}

(相同的输出)

仔细查看,如果您有其他问题,请告诉我。

main中的以下行不正确。

*data = rand() % 100 + 1;
cout << setw(5) << right << *data;

他们只是设置数组第一个元素的值并打印相同的元素。

请改用data[i]

data[i] = rand() % 100 + 1;
cout << setw(5) << right << data[i];

如果必须使用指针表示法,请使用*(data+i)

*(data+i) = rand() % 100 + 1;
cout << setw(5) << right << *(data+i);

可以使用的另一种方法是使用临时指针变量来迭代数组。

int* iter = data;
for (int i = 0; i < size; i++. ++iter)
{
*iter = rand() % 100 + 1;
cout << setw(5) << right << *iter;
...
}

这可确保不会丢失原始指针,这是能够释放内存所必需的。

PS可能有其他错误,也可能没有,但我在快速浏览您的代码后注意到了上述问题。

移动指针

data++;

data--;

主要。您还可以执行其他操作,但您的教师要求递增和递减。

所以

for (int i = 0; i < size; i++)
{
*data = rand() % 100 + 1;
cout << setw(5) << right << *data;
if ((i + 1) % 5 == 0)
{
cout << endl;
}
}

成为

for (int i = 0; i < size; i++)
{
*data = rand() % 100 + 1;
cout << setw(5) << right << *data++; // change made here
if ((i + 1) % 5 == 0)
{
cout << endl;
}
}

请注意,只有一个data++,它是第二次使用data。你应该能够弄清楚为什么。

重置指针

最简单和最明显的是

int*reset = data; 

然后你可以随心所欲地data,当你想重置时,

data = reset;

所以上面的循环最终看起来像

int*reset = data; 
for (int i = 0; i < size; i++)
{
*data = rand() % 100 + 1;
cout << setw(5) << right << *data++; // change made here
if ((i + 1) % 5 == 0)
{
cout << endl;
}
}
data = reset;

但。。。您还可以将逻辑分离到函数中,并利用按值传递

void fill(int * data,
int size)
{
for (int i = 0; i < size; i++)
{
*data = rand() % 100 + 1;
cout << setw(5) << right << *data++; // change made here
if ((i + 1) % 5 == 0)
{
cout << endl;
}
}
}

main的相关部分现在看起来像

data = new int[size];
// Write a loop to fill the "data" array with random numbers from 1 - 100 (inclusive)
// This code must use POINTER NOTATION (no subscripting) to work with the array.
// Reminder: neither of these notations is acceptable here:
// data[n]  or *(data + n)
// Instead this code will use pointer incrementing/decrementing and dereferencing
cout << "This is just the test to see if the pointer is successfully creating the array" << endl;
fill(data, size);
// Reset "data" to point to the beginning of the array

"等一下!"你在想。 "克罗姆的名义int * data如何按价值传递?这是一个<咒骂删除>指针!指向的数据通过引用传递,但指针本身按值传递。data填写是maindata的副本。fill中的所有data++都发生在副本上,因此main中的data仍然指向您离开它的位置。

无需重置,并且通过将部分职责剥离到自己的简单且可独立测试的功能中简化了main的职责。在我看来,让一切尽可能简单、小巧和愚蠢是值得的。