1

In my Computer Science course, we have been taught a method of storing a value in the 0th element of a malloced array, then incrementing the array so that things such as the size of the array can be stored in that element and retrieved later. I have tried using a modified version of this method to store various datatypes in these incremented elements.

Here is an example of how such an array is created:

int *array; 
array = malloc(sizeof(int) + sizeof(double) + (n * sizeof(int)))
*(array) = n;
array++;
(double*)array++;
return array;

In this example, the sizeof(int) and sizeof(double) in the malloc statement are the elements that will store things, such as the size of the array in the int element, and in the double element we can store something like the average of all the numbers in the array (excluding these two elements of course)

(n * sizeof(int)) is for creating the rest of the elements in the array, where n is the number of elements, and sizeof(int) is the desired data type for these elements, and in theory this should work for an array of any data type.

Now, here is the trouble I am having:

I have created another function to retrieve the size of the array, but I am having trouble decrementing and incrementing the array. Here is my code:

getArraySize(void* array){
(double*)array--;//Decrement past the double element
(int*)array--;//Decrement past the int element

int size = *((int*)array);//Acquire size of the array

(int*)array++;//Increment past int element
(double*)array++;//Increment past the double element

return size;}

This function fails to get the size of the array, and I have realized it is because the compiler first increments the array then type casts it. However, when i try to fix such increment/decrement statements as follows:

((int*)array)++;

I get an error that says lvalue required as increment operand. I do not know how to fix this notation in such a way that it will increment and decrement correctly. Any suggestions would be much appreciated.

Nerdy
  • 1,016
  • 2
  • 11
  • 27
Makrovich
  • 11
  • 2
  • 1
    An additional line of questions/answers [Array of size 0 at the end of struct](https://stackoverflow.com/questions/36577094/array-of-size-0-at-the-end-of-struct) – David C. Rankin Feb 19 '20 at 07:45

2 Answers2

6

In my Computer Science course, we have been taught a method of storing a value in the 0th element of a malloced array, then incrementing the array so that things such as the size of the array can be stored in that element and retrieved later.

Sorry to hear that, since this is utter nonsense. Use struct instead.

What's worse than the task being nonsense however, is that it also invokes undefined behavior (see the C standard 6.5.6). You cannot do pointer arithmetic with that are not pointing to an array with the same type as the pointer itself.

In addition, this may lead to misaligned access. Depending on CPU, misalignment could cause needlessly slow code or instruction traps leading to a program crash. Misaligned access is also undefined behavior.

Also, storing the result of various operations on a data type, such as average, inside the data type itself doesn't make any sense at all. They would have to be updated as soon as a value changes, which causes needless bloat and ineffective code.

Forget about all this nonsense immediately. Your program cannot get fixed or repaired, since the very idea behind it is fundamentally wrong. Do like this instead:

typedef struct
{
  int i;
  double d;
  int array[];
} something;

something* s = malloc(sizeof(something) + sizeof(int[n]));
s->i = ...;
s->d = ...;
for(int i=0; i<n; i++)
  s->array[i] = ...;

...
free(s);

Specifically, your code invokes undefined behavior per C17 6.5.6 §7 and §8:

For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

When an expression that has integer type is added to or subtracted from a pointer, the result has the type of the pointer operand. /--/ If both the pointer operand and the result point to elements of the same array object... /--/ ...otherwise, the behavior is undefined.

There is also the issue of pointer aliasing, but (by luck?) it doesn't apply in this case, since data allocated on the heap doesn't have an "effective type" until written to. Long as you write to a specific address with the same pointer type, it is not undefined behavior.

Relevant parts regarding misalignment is C17 6.3.2.3/7:

A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.

Lundin
  • 195,001
  • 40
  • 254
  • 396
-2

What you can do to reach your goal and is (to my opinion) more readable anyway:

array -= sizeof(double);  // get to position where double starts
array -= sizeof(int);     // get to position where int starts

NOTE This only works on some compilers and within getArraySize since you casted the array pointer to void*. So this is also not advisable at all

But I really think that this is NOT the way to go and I also recommend to use a struct instead as @Lundin points out.

If you call your getArraySize function with any other pointer or with a pointer of the expected array but not at the right position, it will most likely end up in segmentation faults

Odysseus
  • 1,213
  • 4
  • 12
  • This doesn't work at all since `sizeof(double)` would make the `int*` pointer move for example 8 times the size of an int, which can never be correct. Your method would only work with character type pointers, which doesn't apply here. – Lundin Feb 19 '20 at 07:43
  • @Lundin I refer to his `getArraySize` function where he passes his array pointer as `(void*)` so that should work I guess. Outside if it is still `int*` you are absolutely right, I will clarify this – Odysseus Feb 19 '20 at 07:49
  • No, you cannot do pointer arithmetic on void pointers. That's a non-standard compiler extension only supported by a few compilers, when their non-compliant mode is enabled. – Lundin Feb 19 '20 at 07:51
  • Good point - you are right but at least GNU C seems to allows this. But that makes it not advisable – Odysseus Feb 19 '20 at 07:58
  • GNU C isn't standard C and shouldn't be taught to beginners. Beginners using gcc should always compile with `-std=c11 -pedantic-errors -Wall -Wextra` for strict standard compliance and maximum warnings. – Lundin Feb 19 '20 at 08:00
  • Well as said I would not recommend that at all – Odysseus Feb 19 '20 at 08:05