user5921954
user5921954

Reputation:

Why use "*(*(array + i) + j)" over "array[i][j]" in C++?

In what example situation I should use the trickier form of it? Does it generate a faster binary code? If I can do the job with the [][] notation, should I still make an extra effort for some reason and implement it with pointers? For single data I understand the need of pointers. I am asking specifically for two dimensional arrays and not about the general sense of using pointers.

Upvotes: 1

Views: 353

Answers (4)

Swift - Friday Pie
Swift - Friday Pie

Reputation: 14589

When you explain how 2d array works. That expression is counter-example of possible mistake using array's name if array is multi-dimensional. Let's say, it's not a rare mistake. What many inexperienced programmers do:

int arr[10][20];

for (int I =0; I<200; I+=1) *(arr + I) = <some value based on I>;

In best-case scenario, they encounter " cannot convert from 'int' to 'int [20]" error and go to their better (or to SO website ). In worst-case, they become "clever" and use type-cast to trick the compiler.

They know of how single dimensional array work, because it is in every book since K&R, and they don't realize that they go out of array bound as soon as I becomes larger than 9. The expression

*(*(arr + i) + j)

shows that indexes of array are multiplicative in relation to the pointer to which array's name degraded. More "proper " way to do what they want is to use pointer to int:

int  *p = (int *)&arr[0][0];

for (int I = 0; I < 200; I++) 
    *(p+I) = <some value based on I;

But strictly speaking, C++ standard doesn't guarantee this either, unless you use pointer to unsigned char.

unsigned char *p = (unsigned char*)&arr[0][0];

for (int I = 0; I < 200; I++) 
    *(int*)(p + I*sizeof(int)) = I;

Still must be careful here, in case of platform supposes padding and elements of array are actually of type that requires padding at end (e.g. a struct).

If we output resulting array:

cout << "arr[][] = { ";
 for (int i = 0; i < 10; i++) 
 {
     cout << "\n{";
     for (int j = 0; j < 20; j++) 
         cout << arr[i][j]<< " ";
     cout << "}";
 }
 cout << "}\n";

We'll get:

arr[][] = { 
{0 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 53 54 55 56 57 58 59 }
{60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 }
{80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 }
{100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 }
{120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 }
{140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 }
{160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 }
{180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 }}

Upvotes: 0

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

In what example situation I should use the trickier form of it?

Never, unless you're attending a code obfuscation contest as mentioned in @M.M' comment

Does it generate a faster binary code?

No, the emitted assembly instructions should be the same with any decent compiler.

should I still make an extra effort for some reason and implement it with pointers?

No. Use standard containers or smart pointers instead. Refrain to use raw c-style arrays and pointers.

For known sizes the simplest way is

std::array<std::array<T,5>,10> array2D;

For 2D arrays variying in size you can use

size_t rows, columns;
std::cout << "Enter row and column size please > " << std::flush;
if(std::cin >> rows >> columns) {
    std::vector<std::vector<T>> array2D(rows,std::vector<T>(columns));
}

To optimize 2D arrays to use contiguous dynamic memory blocks, consider to write a small wrapper class, that translates row and column offsets into the internal value positions (e.g. maintained in a simple std::vector).

Upvotes: 5

Peter
Peter

Reputation: 36597

There is generally no reason to prefer the *(array + i) form over the array[i] form. Similarly, there is no reason to prefer the *(*(array + i) + j) form over array[i][j].

Semantically they are equivalent, and the compiler is able to emit the more effective "binary code" - which will normally be the same in both cases.

Practically, human beings usually find it easier to understand the array[i][j] form, so that is preferable unless you are seeking to confuse people (e.g. obfuscation).

It is vital, when using other forms of pointer arithmetic, to understand the equivalence. That allows more effective choices of technique. It is not an accident that C++ standard library supports iterators (a generalised form of pointer) to work with ranges, and that using these are often encouraged over simple array[i] syntax,.

Upvotes: 0

Potatoswatter
Potatoswatter

Reputation: 137800

In addition to @πάντα ῥεῖ's advice, it's preferable to write C++ expressions so similar types may be substituted for the variables — the principle of generic code.

std::array is a preferred alternative to C arrays. (The two-dimensional case looks like std::array< std::array< int, M >, N >.) It doesn't support the + operator, so the obfuscated expression would obstruct migration to it, or any other container-like type such as std::vector.

Upvotes: 1

Related Questions