Can someone explain why I get the same output?

Is my professor's code wrong?
Is my code right?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//PROFESSORS CODE
    const int SIZE = 5;
char* counts[SIZE];
// Allocate arrays in dynamic memory
for (int i = 0; i < SIZE; i++) {
 counts[i] = new char[SIZE - (i + 1)];             //suppose to print*****
 for (int j = 0; j <= SIZE - (i + 1); j++) {       //                ****
     cout << i << endl;                            //                ***
 counts[i][j] = 'X';                               //                **
 }                                                 //                *
}
// Print all counts
for (int i = 0; i < SIZE; i++) {
 for (int j = 0; j <= SIZE - (i + 1); j++) {
 cout << counts[i][j];
 }
 cout << endl;
}
// Deallocate the rows
for (int i = 0; i < SIZE; i++) {
 delete[] counts[i];
}

//VERSUSES
//MY CODE

const int SIZE = 5;
    char* counts[SIZE];

    // Allocate arrays in dynamic memory
    for (int i = 0; i < SIZE; i++) {
        counts[i] = new char[SIZE - i];              //pointer at i = 0: size is 5-0 = 5, Each cell has X
        for (int j = 0; j < SIZE - i; j++) {         //pointer at i = 1: size is 5-1 = 4
        cout << i << endl;counts[i][j] = 'X';                          //pointer at i = 2: size is 5-2 = 3
        }                                            //pointer at i = 3: size is 5-3 = 2
    }                                                //pointer at i = 3: size is 5-3 = 1
//  WHY 2D ARRAY? i is the position of the pointer in the char array and 
//  j is the position of the cells in the allocated array
    
    // Print all counts
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE-i; j++) {
            cout << counts[i][j];
        }
        cout << endl;
    }
    
    // Deallocate each pointer
    for (int i = 0; i < SIZE; i++) {
        delete[] counts[i]; // pointers are allocated arrays so we need  (delete[])
    }//then after delete we want to delete every pointer in the array
Last edited on
I ran both codes and they printed out the same thing.
What is different about yours?
In my professor's code it stores a size 4 dynamically allocated array

In my code it stores a size 5

1
2
counts[i] = new char[SIZE - (i + 1)];
counts[i] = new char[SIZE - i]; 
< 5 is the same as <=4.
0, 1, 2, 3, 4. Less than 5

0, 1, 2, 3, 4. Less than or equal to.

But this may not be what you're asking about. It looks like you're both practicing for an obfuscation competition.
Last edited on
I think your professor's code is writing beyond the end of an array.
I’ve checked if the teacher’s code remains inside boundaries and it seems it does (if I’m not missing anything!):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>


int main()
{
    constexpr int Size { 5 };
    char* counts[Size];

    // Allocate arrays in dynamic memory
    for (int i = 0; i < Size; ++i) {
        std::cout << "Allocating new row of size "
                  << Size << " - (" << i << " + 1) --> "
                  << Size - (i + 1) << '\n';
        counts[i] = new char[Size - (i + 1)];

        for (int j = 0; j <= Size - (i + 1); ++j) {
            counts[i][j] = 'X';
            std::cout << "counts["<< i << "][" << j << "] = X\n";
        }
    }

    // Print all counts
    std::cout << '\n';
    for (int i = 0; i < Size; ++i) {
        for (int j = 0; j <= Size - (i + 1); ++j) {
            std::cout << "counts["<< i << "][" << j << "] = "
                      << counts[i][j] << "; ";
        }
        std::cout << '\n';
    }

    // Deallocate the rows
    for (int i = 0; i < Size; ++i) {
        delete[] counts[i];
    }
}


Output:
Allocating new row of size 5 - (0 + 1) --> 4
counts[0][0] = X
counts[0][1] = X
counts[0][2] = X
counts[0][3] = X
counts[0][4] = X
Allocating new row of size 5 - (1 + 1) --> 3
counts[1][0] = X
counts[1][1] = X
counts[1][2] = X
counts[1][3] = X
Allocating new row of size 5 - (2 + 1) --> 2
counts[2][0] = X
counts[2][1] = X
counts[2][2] = X
Allocating new row of size 5 - (3 + 1) --> 1
counts[3][0] = X
counts[3][1] = X
Allocating new row of size 5 - (4 + 1) --> 0
counts[4][0] = X

counts[0][0] = X; counts[0][1] = X; counts[0][2] = X; counts[0][3] = X; counts[0][4] = X;
counts[1][0] = X; counts[1][1] = X; counts[1][2] = X; counts[1][3] = X;
counts[2][0] = X; counts[2][1] = X; counts[2][2] = X;
counts[3][0] = X; counts[3][1] = X;
counts[4][0] = X;

Allocating new row of size 5 - (0 + 1) --> 4
counts[0][0] = X
counts[0][1] = X
counts[0][2] = X
counts[0][3] = X
counts[0][4] = X


If the array has size 4 ... and counts from 0 ... are you sure that counts[0][4] is legitimate?

I'm not really sure what happens if you declare an array of size 0, either.
Last edited on
lastchance wrote:
are you sure that counts[0][4] is legitimate?

No, I’m not :-) It seems I was missing something, indeed.
(I would have started by a C-style array of 5, so the indexes from 0 to 4 looked fine to me, but I didn’t notice the code actually allocated an array of 4.)
My bad, my bad...

lastchance wrote:
what happens if you declare an array of size 0

Reading here:
https://stackoverflow.com/questions/1087042/c-new-int0-will-it-allocate-memory
it seems you are allowed to ask for that, but
- the request can fail
- dereferencing a pointer returned as a request for zero size is undefined
That post is 10 years old.
I think it would at least raise a warning.
The prof's code, run through valgrind.

$ valgrind ./a.out
==8410== Memcheck, a memory error detector
==8410== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==8410== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==8410== Command: ./a.out
==8410==
0
0
0
0
0
==8410== Invalid write of size 1
==8410== at 0x400A22: main (bar.cpp:15)
==8410== Address 0x5ab6c84 is 0 bytes after a block of size 4 alloc'd
==8410== at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8410== by 0x4009D2: main (bar.cpp:12)
==8410==
1
1
1
1
2
2
2
3
3
4
==8410== Invalid read of size 1
==8410== at 0x400A64: main (bar.cpp:22)
==8410== Address 0x5ab6c84 is 0 bytes after a block of size 4 alloc'd
==8410== at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8410== by 0x4009D2: main (bar.cpp:12)
==8410==
XXXXX
XXXX
XXX
XX
X
==8410==
==8410== HEAP SUMMARY:
==8410== in use at exit: 72,704 bytes in 1 blocks
==8410== total heap usage: 7 allocs, 6 frees, 73,738 bytes allocated
==8410==
==8410== LEAK SUMMARY:
==8410== definitely lost: 0 bytes in 0 blocks
==8410== indirectly lost: 0 bytes in 0 blocks
==8410== possibly lost: 0 bytes in 0 blocks
==8410== still reachable: 72,704 bytes in 1 blocks
==8410== suppressed: 0 bytes in 0 blocks
==8410== Rerun with --leak-check=full to see details of leaked memory
==8410==
==8410== For counts of detected and suppressed errors, rerun with: -v
==8410== ERROR SUMMARY: 10 errors from 2 contexts (suppressed: 0 from 0)
Topic archived. No new replies allowed.