Ok, here I come with a some sort of big analysis with pointer, array, vector, iterator and address of memory...

using this input:

// hakim makan nasi
// ayam yang basi
// sambil tengok
// hafiz yang tergelak
// gelak

with this code:

#include <cstring>
#include <iostream>
#include <string>
#include <vector>

using std::cin;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main()
{
    const int maxStrings = 5;
    vector<string> svec(maxStrings);
    char* parr[maxStrings] = { NULL, NULL, NULL, NULL, NULL };

    for (int i = 0; i != maxStrings; ++i)
    {
        getline(cin, svec[i]);
        parr[i] = new char[svec[i].size() + 1];
        strcpy(parr[i], svec[i].c_str());
    }

    vector<string>::iterator str_iteration = svec.begin();
    for (char **curr_point = parr, **bound_point =  parr + maxStrings;
         curr_point < bound_point; ++curr_point)
    {
        cout << parr << endl;               // 1
        cout << curr_point << endl;         // 2
        cout << *str_iteration << endl;     // 3
        cout << *parr << endl;              // 4
        cout << **parr << endl;             // 5
        cout << *curr_point << endl;        // 6
        cout << **curr_point << endl;       // 7
        cout << &str_iteration << endl;     // 8
        cout << &parr << endl;              // 9
        cout << &curr_point << endl;        // 10
    }
    delete [] parr;

    cout << endl << endl << endl;
    return 0;
}

see the attachment(Microsoft Excel) for the details that I listed. It contain each output result from the above code and another detail/description that explained and grouped...

before going to conclusion for the analysis, take a note for this, based on the details in the attachment :

reference : see in the attachment...

A. parr(parr's value) == &parr(parr's address)
(reference = 1 & 9)

B. parr == curr_point, except that parr only point to the first element ONLY, but curr_point iterate through each element in the array
(reference = 1 & 2 / 4 & 6/ 5 & 7)

note: which what I mean by "const" of in the attachments...

C. str_iteration is similar to the parr, except it doesn't have its own value(str_iteration can't be compiled) and the value of pointer's pointer's value.(**str_iteration can't be compiled too)

D. the curr_points value, which holds address of each element in the array, and some of the address actually come's from:-
- &str_iteration
- &curr_points
- &parr
note, see the "highlighted" part =)
(reference = 2)

E. curr_points has it own address which is seperated from its element's address, thought so based on D., some element use the curr_points's address as its own...
(reference = 10)

conclusion and wonders:

1- element of a vector/array can "share" same address with the vector/array it was put into. (Well, I always thought each value has its own address)
(reference = 2)

2- from what I learned, when we setup an array, the pointer will referred to the first element automatically, and that's what (I thought) happen on the pointer's pointer's value(**parr and **curr_points)
(reference = 5 / 7)

3- based on '2-', can anyone give an idea on how to iterate through the pointer's pointer's value? (does ++**curr_point works?) :P

4- from this analysis and other experience with programming C++, I found out that array's format similar to pointer's format, especially when dealing with dynamic allocated array, and I find it confusing at time... 

(parr is array, but why it work like curr_point in this test, except that its value == its address(reference A.) while, curr_point's value is referring to the element's address?!

I'm a self-taught programmer, so all of this analysis just come from myself, I need confirmation and solving some of confusion related with these case before I move to multidimensional array topic, hop anyone can help =)

1- element of a vector/array can "share" same address with the vector/array it was put into. (Well, I always thought each value has its own address)
(reference = 2)

The address of the array and the address of the first element are the same. This makes sense when you think of array indexing as an offset from a base address. (base + 0) == base , right? So &base[0] == base as well.

2- from what I learned, when we setup an array, the pointer will referred to the first element automatically, and that's what (I thought) happen on the pointer's pointer's value(**parr and **curr_points)
(reference = 5 / 7)

That's what happens. Though **parr will always produce the same result because you're always accessing parr[0][0] .

3- based on '2-', can anyone give an idea on how to iterate through the pointer's pointer's value? (does ++**curr_point works?) :P

If I understand your question, you still need some form of iterator. You can't use *curr_point directly, but you can use it as a base:

for (char *p = *curr_point; *p != '\0'; p++)
    std::cout << *p << '\n';

The reason you can't use *curr_point directly is somewhat subtle. If it's an actual array then the pointer isn't modifiable and your code will fail. If it's a simulated array as in your example then modifying it will break subsequent code that relies on parr . This is because curr_point is referencing parr , so changes to *curr_point or **curr_point will also change the referenced part of parr . This is one of the aliasing errors that you need to watch out for when using pointers to non-const.

4- from this analysis and other experience with programming C++, I found out that array's format similar to pointer's format, especially when dealing with dynamic allocated array, and I find it confusing at time...

Pointers can be used to simulate arrays. As long as you understand that an array is not a pointer and a pointer is not an array, all is well. :)

(parr is array, but why it work like curr_point in this test, except that its value == its address(reference A.) while, curr_point's value is referring to the element's address?!

When used in value context, an array name is converted to a pointer to the first element. So in value context, parr and curr_point are equivalent. In fact, that's why you can assign parr to curr_point in the loop. You're not assigning an array to a pointer, you're assigning one pointer to another pointer due to the value context conversion.

One final thing to note is that delete [] parr is wrong. You cannot delete anything that was not initially allocated with new. Presumably you were trying to release the allocated memory to each element, in which case you need a loop:

for (int i = 0; i < maxStrings; i++)
    delete[] parr[i];
for (char **curr_point = parr, **bound_point = parr + maxStrings;
curr_point < bound_point; ++curr_point)

Please someone correct me, if I am wrong but, I don't think it is safe to use such addition :
**bound_point = parr + maxStrings; maxStrings = 5 it's an int.

The size of a char is 1 byte, but if it was an int (4 byte), this method wouldn't work. In the end of the loop, it would be pointing to the memory area of the second element of the ""array""(int) instead of the desired bound point. (The bound point would be pointing in the second int.)

Edited 4 Years Ago by LRRR: n/a

for (char **curr_point = parr, **bound_point = parr + maxStrings;
curr_point < bound_point; ++curr_point)

Please someone correct me, if I am wrong but, I don't think it is safe to use such addition :
**bound_point = parr + maxStrings; maxStrings = 5 it's an int.

The size of a char is 1 byte, but if it was an int (4 byte), this method wouldn't work. In the end of the loop, it would be pointing to the memory area of the second element of the ""array""(int) instead of the desired bound point. (The bound point would be pointing in the second int.)

I believe you're trying to say that addition on a pointer will always use a step size of one byte, which isn't how pointers work. The step size of a pointer depends on the size of the pointed to type, so p + n under the hood is more along the lines of (char*)p + (n * sizeof *p) .

Sorry, I made mistake earlier. What I meant is, the pointer would be incremented in the right way. However the problem in the condition because, **bound_point = parr + maxStrings would point to address + 5. After the first incrementation, the address would be incremented by the size of int (4). The condition would be still true, because curr_point + 4 < curr_point + 5 ( bound_point ). On the second run, this would be no longer true.

Yes, thank you Narue. :)

Edited 4 Years Ago by LRRR: n/a

The address of the array and the address of the first element are the same. This makes sense when you think of array indexing as an offset from a base address. (base + 0) == base , right? So &base[0] == base as well.

that part make sense, but (referring to the new attachment(MSPaint1)). I found out that the element pick random address and two of the address is str_iteration's address and curr_point's address. well, the one you refer to(I thought) is the first element where base's address == parr's address.

That's what happens. Though **parr will always produce the same result because you're always accessing parr[0][0] .

Wait a minute, so it is actually a multi-dimension array? that's why it's some sort of weird, I thought it work like element in a vector but at the same time confuse because always thought that array just hold a character for each element...

If I understand your question, you still need some form of iterator. You can't use *curr_point directly, but you can use it as a base:

for (char *p = *curr_point; *p != '\0'; p++)
    std::cout << *p << '\n';

yeah, this is what I mean... thought so not really understand why not this one?
for (char **p = **curr_point) not really understand how does the (base thingy) works, but I will study on it from now on..


The reason you can't use *curr_point directly is somewhat subtle. If it's an actual array then the pointer isn't modifiable and your code will fail. If it's a simulated array as in your example then modifying it will break subsequent code that relies on parr . This is because curr_point is referencing parr , so changes to *curr_point or **curr_point will also change the referenced part of parr . This is one of the aliasing errors that you need to watch out for when using pointers to non-const.

thnx, I will keep that in mind...

Pointers can be used to simulate arrays. As long as you understand that an array is not a pointer and a pointer is not an array, all is well. :)

relative to me,
array == an object that can store element(like vector), except that vector can't iterate like array iterate, vector need iterator but array doesn't really need pointer, its variable work like pointer itself(can be incremented and point on the first element automatically)

pointer == another way to iterate and access through object's address and value indirectly. At the same time, become another object that has its own address(which is one of the reason the nested pointer exist)

When used in value context, an array name is converted to a pointer to the first element. So in value context, parr and curr_point are equivalent.

ahah, lol I forgot myself that 'const' part in the code, where parr always refer to the first element, thnx for remind me of that...

In fact, that's why you can assign parr to curr_point in the loop. You're not assigning an array to a pointer, you're assigning one pointer to another pointer due to the value context conversion.

I assigning one pointer to another pointer? so, parr is a pointer? hmm now my mind will come out with (array == pointer) =S I missing something here???

One final thing to note is that delete [] parr is wrong. You cannot delete anything that was not initially allocated with new. Presumably you were trying to release the allocated memory to each element, in which case you need a loop:

for (int i = 0; i < maxStrings; i++)
    delete[] parr[i];

thnx for the reminder and solution, always wonder the warning that compiler giving out while processing the code... but if I delete either through curr_point(pointer) or the parr(array) itself, that parr that always refer to the first element will refer to garbage value because the value's there being 'deleted' already... well, if I do the delete process in the same loop :P

but, anyway, talking about memory leak, does the memory that was used by dynamic allocated array(which doesn't being deleted) will be restored after reboooting computer? or not? will my computer become exhausted of this problem?

anyway, thnk you so much for replying ^_^

Attachments MSPaint1.png 113.36 KB

I found out that the element pick random address and two of the address is str_iteration's address and curr_point's address.

str_iteration and curr_point are both separate entities. They each have a unique address that has nothing to do with the addresses of the pointed to objects.

Wait a minute, so it is actually a multi-dimension array?

Not technically, but you can use it as one because it's an array of simulated arrays. Only the first dimension is an actual array, but the second dimension is a pointer with memory allocated by new, which is functionally very similar to an array.

thought so not really understand why not this one?
for (char **p = **curr_point)

Because there's a type mismatch. **curr_point is type char , not char** . You could certainly say char **p = curr_point , but that's no different from your current loop.

I assigning one pointer to another pointer? so, parr is a pointer? hmm now my mind will come out with (array == pointer) =S I missing something here???

I went to great lengths to stress that arrays are not pointers, yet you still gravitated toward that misconception. No, array != pointer, ever. When you say curr_point = parr , it really means curr_point = &parr[0] .

but if I delete either through curr_point(pointer) or the parr(array) itself, that parr that always refer to the first element will refer to garbage value because the value's there being 'deleted' already... well, if I do the delete process in the same loop

I'm not entirely sure what you're trying to say, but it sounds like you think delete[] parr[0] will break the rest of the array, which is incorrect. While parr and parr[0] share the same address, you can still use parr as the base for indexing after deleting the pointer at parr[0] . The loop I gave you for releasing all pointers in the array is correct and safe.

does the memory that was used by dynamic allocated array(which doesn't being deleted) will be restored after reboooting computer?

Yes, a reboot fixes all memory leaks.

str_iteration and curr_point are both separate entities. They each have a unique address that has nothing to do with the addresses of the pointed to objects.


Not technically, but you can use it as one because it's an array of simulated arrays. Only the first dimension is an actual array, but the second dimension is a pointer with memory allocated by new, which is functionally very similar to an array.


Because there's a type mismatch. **curr_point is type char , not char** . You could certainly say char **p = curr_point , but that's no different from your current loop.


I went to great lengths to stress that arrays are not pointers, yet you still gravitated toward that misconception. No, array != pointer, ever. When you say curr_point = parr , it really means curr_point = &parr[0] .

Yes, a reboot fixes all memory leaks.

thnx for shed some light on my confusion, seems to be clear now...

I'm not entirely sure what you're trying to say, but it sounds like you think delete[] parr[0] will break the rest of the array, which is incorrect. While parr and parr[0] share the same address, you can still use parr as the base for indexing after deleting the pointer at parr[0] . The loop I gave you for releasing all pointers in the array is correct and safe.

ignore this one, just fooling around ;)

thought so, I still have some confusion about the pointer and array, I will try to study more to find the difference of these... (at some point, this remind me of mass and energy, different manifestation of the same thing);)

for (int i = 0; i != maxStrings; ++i)
{
getline(cin, svec);
parr = new char[svec.size() + 1];
strcpy(parr, svec.c_str());
}

i is supposed to be < maxStrings

Why are you nulling char* parr[maxStrings] = { NULL, NULL, NULL, NULL, NULL }; ?????????

Why not?

i is supposed to be < maxStrings

This is a pointless change as it doesn't make the code better. In fact, C++ convention leans toward using != for such purposes rather than < because it's consistent with the iterator model.

This question has already been answered. Start a new discussion instead.