0

I am trying to create a generic function which when called allocated contiguous memory for a dimensional array. Goal is to achieve something like below

enter image description here

so to achieve it - equation I am using is

Type **pArray;
int total_elements = ((rows * cols) + rows); 
pArray = (Type **) malloc(total_elements * sizeof(Type));

I am also confused with respect to accessing elements part. I am finding it hard to visualize how below code will fill the elements of above array

for (row = 0; row < dim0; ++row)
   {
      for (col = 0; col < dim1; ++col)
      {
         /* For this to work Type must be a 1D array type. */
         for (ix = 0; ix < (int)(sizeof(item)/sizeof(item[0])); ++ix)
         {
            /*            printf("%4d\n", testValue); */
            ppObj[row][col][ix] = testValue;
            if (testValue == SCHAR_MAX)
               testValue = SCHAR_MIN;
            else
               ++testValue;
         }
      }
   } 

Goal is not to create below array format

enter image description here

oneday
  • 629
  • 1
  • 9
  • 32
  • 2
    Standard warning: Do **not** cast `void *` as returned by `malloc` & friends in C! – too honest for this site Jul 26 '15 at 20:19
  • I suspect your code might violate strict aliasing rule. (Sideote: You should not cast `size_t` to `int` without explicit need.) – too honest for this site Jul 26 '15 at 20:21
  • 2
    @Olaf "`malloc` does not guarantee allocating continous blocks." -- not sure what you want to say here, but a single `malloc` will *of course* return a continuous block -- and I see only one `malloc` in this code. –  Jul 26 '15 at 20:23
  • @FelixPalmen: Yes, I figured out my self now. OP seems to allocate two arrays in one. – too honest for this site Jul 26 '15 at 20:24
  • SO is no code-review or code discussion site (nor a tutoring site). If you have a _specific_ problem with your code, please state the actual problem and post a [mcve]. – too honest for this site Jul 26 '15 at 20:28
  • @Olaf - Understood - With two snippets of code I am sharing what I am doing, and which is resulting in code not working. Actual problem would be 1) how to allocate memory for given diagram - if you can help with that and 2) suggest how to access elements of it - that would help a lot – oneday Jul 26 '15 at 21:09
  • @oneday: Are you sure you understood the problem correctly? There's a *third* variant of allocating such array: *two* separate memory blocks, first is the array of pointers `Type *[rows]`, second is the *contiguous* array of data. `Type [rows * cols]`. I.e. the actual data is stored in *one contiguous block* (just like you wanted), but separately from pointers. (Note that this is different from your last variant since in that one the data is allocated in separate blocks.) – AnT stands with Russia Jul 26 '15 at 21:40
  • 1
    What you requested in your question is a hellish task of satisfying all alignment requirements and aliasing rules for two completely independent types - `Type *` and `Type` - stored in one block of memory. It is doable, but is it worth it? – AnT stands with Russia Jul 26 '15 at 21:43

1 Answers1

1

This won't work. You assume your Type * has the same size as your Type which is most of the time not true. But, what do you need the row pointers for, anyway? My first implementation idea would be something like this:

typedef struct TypeArray
{
    size_t cols;
    Type element[];
} TypeArray;

TypeArray *TypeArray_create(size_t rows, size_t cols)
{
    TypeArray *self = calloc(1, sizeof(TypeArray) + rows * cols * sizeof(Type));
    self->cols = cols;
    return self;
}

write getter and setter using e.g. self->element[row * self->cols + row].

[edit]: Following this discussion here it is doable like this:

typedef long long Type;


Type **createArray(size_t rows, size_t cols)
{
    size_t r;

    /* allocate chunk: rows times the pointer, rows * cols times the value */
    Type **array = malloc(rows * sizeof(Type *) + rows * cols * sizeof(Type));

    /* calculate pointer to first row: point directly behind the pointers,
     * then cast */
    Type *row = (Type *) (array + rows);

    /* set all row pointers */
    for (r = 0; r < rows; ++r)
    {
        array[r] = row;
        row += cols;
    }

    return array;
}

Usage could look like this:

int main()
{
    Type **array = createArray(3, 4);

    for (int r = 0; r < 3; ++r)
    {
        for (int c = 0; c < 4; ++c)
        {
            array[r][c] = (r+1) * (c+1);
        }
    }
    for (int r = 0; r < 3; ++r)
    {
        for (int c = 0; c < 4; ++c)
        {
            printf("array[%d][%d] = %lld\n", r, c, array[r][c]);
        }
    }

    free(array);
    return 0;
}

This assumes that no type needs a bigger alignment than a data pointer, otherwise you would have to calculate an amount of padding bytes to insert after your pointers. To be safe, you could use the sizeof(Type) and some modulo calculation for that (inserting the padding bytes using a char * pointer), but that would waste a lot of memory if your Type is for example a big struct.

All in all, this assignment is written by a really really clueless teacher.

  • its part of the assignment requirement to implement it as show in figure – oneday Jul 26 '15 at 21:06
  • @oneday well that's a "stupid idea", but the first step would be to have the size calculation corrected: it's `rows * sizeof(Type *) + rows * cols * sizeof(Type)` –  Jul 26 '15 at 21:14
  • @oneday: In addition to what FelixPalmen wrote, it violates strict aliasing rules and your data may be missaligned. What you are doing is also **no** 2D array, but an 1D array of pointers to 1D types. And due to the pointers, requiring to have all in a continous block is just plain nonsense. Are you sure you understand the assignment correctly? That diagram could ver well just locate the blocks next to each other for drawing. To me they look like single, seperate blocks. – too honest for this site Jul 26 '15 at 21:16
  • @FelixPalmen - with regard to memory calculation , thanks for the equation, I understand now the difference. – oneday Jul 26 '15 at 21:21
  • @Olaf That's one of the reasons I called it a "stupid idea", but in fact, the standard doesn't define the result of an `malloc` being strictly **one** allocated object .. so you *could* get this well-defined. Anyhow, any **sane** C coder would not allocate such a thing in **one** block –  Jul 26 '15 at 21:22
  • @Olaf - here is the requirement Create2D syntax: Type **Create2D(size_t rows, size_t cols); Parameters: rows – the number of rows in the 2-dimensional pointer array Create2D will create cols – the number of columns in the 2-dimensional pointer array Create2D will create Synopsis: Creates a 2-dimensional pointer array of data type Type having the number of rows and columns specified by rows and cols. All memory needed for this array is dynamically-allocated at once using a single call to the appropriate memory allocation function. – oneday Jul 26 '15 at 21:25
  • @Olaf - Continuing the requirement Return: a pointer to the first pointer in the array – oneday Jul 26 '15 at 21:27
  • @oneday that does **not** require you to allocate it all in a single block -- ok, now it does. Tell your professor he is a jerk. –  Jul 26 '15 at 21:27
  • @oneday: "Creates a 2-dimensional pointer array"! Read my comment again - carefully! That would be `Type **arrayptr = malloc(rows * cols * sizeof(Type *))`. Either the text is nonsense or the drawing. Ok, I agree with Felix. Hope I will never meet your lecturer. – too honest for this site Jul 26 '15 at 21:28
  • @Olaf - I have updated the question section again - with part how the array shouldnt look like with new image - Question for below line Type **arrayptr = malloc(rows * cols * sizeof(Type *)) I am just typecasting the void pointer and I guess that's what its taught to us- any flaw in doing that - I see you have mentioned that as first comment as well pArray = (Type **) malloc(total_elements * sizeof(Type)); – oneday Jul 26 '15 at 21:38
  • @oneday I have a working solution ... it's still braindead –  Jul 26 '15 at 21:40
  • @FelixPalmen: Does it care about alignment? That would actually require C11 to be standard compliant. – too honest for this site Jul 26 '15 at 21:42
  • @Olaf what are you refering to exactly? gcc with `-std=c99 -Wall -Wextra -pedantic` doesn't complain. –  Jul 26 '15 at 21:45
  • @FelixPalmen - Thanks for the pointer - I understand solution you presented would work - however keeping in mind requirement of the function - it wont work. – oneday Jul 26 '15 at 21:51
  • @oneday updated my answer. Try to understand the code. Even better would be if you also understand the possible alignment issue, so you could argue why this assignment was stupid. –  Jul 26 '15 at 22:09
  • @FelixPalmen - See your modified code. Thank you. Sure let me try and understand alignment issue that you have mentioned as well. Appreciate your help here !!! – oneday Jul 26 '15 at 22:22
  • @oneday and about this casting the return value of `malloc()` ... this is a "c++-ism". If your teacher wants to see this in `c` code, he's even a bigger jerk. –  Jul 26 '15 at 22:22
  • The compiler does not complain for every fault. Your code violates strict aliasing rules and may misalign the elements. For the latter only C11 added the required functions for a standard way to align, for the latter, you should read about _strict alignment_ which is required by any C standard (but worded different for each version - afaik). – too honest for this site Jul 26 '15 at 22:24
  • @Olaf it does NOT violate strict aliasing rules, read the standard text again please. About alignment, the assumption is pointed out in my answer and I made clear there is no guarantee it holds. Repeating myself here -- the assignment itself is braindead! –  Jul 26 '15 at 22:27
  • What OP wants (is wanted to do?) is actually rubbish and an example who **not** to do it, so I see not much use in further discussion. However. If you think different, welcome to state the applicable section of the standard (_effective type_) for your casts. At least we apparently agree about OPs teacher:-) – too honest for this site Jul 26 '15 at 22:34
  • Exactly, I guess you should read that again. Taking a pointer does not set the effective type, storing a value does. –  Jul 26 '15 at 22:36
  • @Olaf "If a value is stored into an object having no declared type through an lvalue having a type that is not a character type, then the type of the lvalue becomes the effective type of the object for that access and for subsequent accesses that do not modify the stored value." means you could even set the effective type more than once. My code is well-defined in that respect. But, of course, alignment is not accounted for. It's after all a void* discussion -- any sane c coder would just find a better way ;) –  Jul 26 '15 at 22:43