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

时间:2018-05-02 22:34:31

标签: c++ arrays pointers

所以这就是我为此提出的建议。我目前无法弄清楚如何将reverseArray功能放入主...如果我只是将代码复制并粘贴到其中,它每次运行时都会崩溃...我不会...我知道它为什么会导致它崩溃或任何事情。感谢迄今为止帮助我的每一个人。

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;
    }
}

3 个答案:

答案 0 :(得分:3)

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

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

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

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

那么为什么指针的类型int很重要? (好吧,它设置了控制指针算术在引用内存块时如何操作的类型大小),因为指针类型为int,编译器知道a + 1是{{} 1}}允许您引用内存块中的下一个值。

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

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

因此,如果我无法使用任何索引(例如aa[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中的a永远不会更改,函数main()会收到rev的副本,并且在a内,您可以自由增加/减少或做任何您喜欢的事情到rev,在内存块的范围内,因为a中的arev中的原始指针有自己的(并且非常不同)地址。 / p>

(旁边......)你可以在main()内声明第三个指针,例如

rev

然后在您的迭代和交换中使用 int *s = a, /* start pointer */ *e = a + size - 1; /* end pointer */ 而不是s,但没有任何需要。如果你更清楚你正在使用哪个指针,你可以自由地这样做。它只是另一个8字节(或x86上的4个字节),因此附加存储不是问题。

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

a

示例使用/输出

#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 如何影响指针算术(和索引),例如使用typep++提前了多少字节,并确保您确切知道指针指向的位置,并且事情应该开始落实到位。

如果您在弄清楚指针发生了什么问题时,请拉出一张8.5 x 11的纸和一支2号铅笔然后将其画出来 - 在每次迭代时填写你的指针指针指向等等。 - 它确实有帮助。一旦你绘制了足够的图表,做了足够的链接列表,堆栈等等......你现在就不需要这篇论文了(你仍然需要它 - 所以保持方便)

在[{1}}中使用函数

进行反转

在回复您的评论时,当您查看for (i = 0; i < size; i++) p[i]时,您已经宣布了另一个指针main()。因此,您只需将其用作开始指针,并从main()函数添加p作为结束指针。一个简单的实现是:

e

(相同的输出)

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

答案 1 :(得分:1)

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 可能还有其他错误,但在快速浏览一下代码后,我注意到了上述问题。

答案 2 :(得分:1)

移动指针

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

&#34;等一下!&#34;你在想什么。 &#34; Crom的名字int * data如何通过值传递?这是&lt; expletive deleted&gt;指针!&#34;指向的数据通过引用传递,但指针本身按值传递。填充中的datadatamain的副本。 data++ fill中的所有data都发生在副本中,因此main中的main仍指向您离开的位置。

不需要重置您已将部分内容分解为自己简单且可独立测试的功能,从而简化了{{1}}的职责。在我看来,保持一切尽可能简单,小巧和愚蠢是比得上比特币的重量。