0
int nrow=5,ncol=7,i,j;
float **ptr;
/*allocation*/
ptr=(float **) malloc(nrow*sizeof(float*));
ptr[0]=(float *) malloc(nrow*ncol*sizeof(float));
/*initialize*/
for (i=0;i<nrow;i++)
    for (j=0;j<ncol;j++) ptr[i][j]=0.0;

We know in the above case, the row starts from 0 and ends in nrow-1, the column starts from 0 and ends in ncol-1. But how can I let the row start from -4 and end in nrow+3, also let the column start from -4 and end in ncol+3?

Supplementary code:

#include <stdio.h>
#include <stdlib.h>

float *vector(int nl, int nh){
/* allocate a float vector with subscript range v[nl..nh] and initializing
       this vector, eg. vector[nl..nh]=0.0 */
    float *v;
    int i,NR_END=0;

    v=(float *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(float)));

    for (i=0;i<(nh-nl+1+NR_END);i++) v[i]=0.0;
    return v-nl+NR_END;
}

int main(int argc, char *argv[])
{
    int i,nrow=5, row1, row2;
    float *v;
    row1=-4;
    row2=nrow+3;
    v = vector(row1,row2);
    for (i=-4;i<(nrow+4);i++) {
        v[i]=(float)i; 
        printf("v[%d]=%f\n",i,v[i]);
    }
exit(0);
}

If I run the above code, it'll get the correct answer:

 v[-4]=-4.000000
 v[-3]=-3.000000
 v[-2]=-2.000000
 v[-1]=-1.000000
 v[0]=0.000000
 v[1]=1.000000
 v[2]=2.000000
 v[3]=3.000000
 v[4]=4.000000
 v[5]=5.000000
 v[6]=6.000000
 v[7]=7.000000
 v[8]=8.000000
coco
  • 71
  • 6
  • 1
    You can do something like `ptr0 = malloc(ncol+4)` followed by `ptr = ptr0 + 4` after which you can access `ptr[-3]` through `ptr[ncol-1]`. – Steve Summit Jul 17 '18 at 20:38
  • 1
    `nrow` is uninitialized. – Christian Gibbons Jul 17 '18 at 20:38
  • `ncol` is uninitialised. – Weather Vane Jul 17 '18 at 20:39
  • 3
    `ptr[0]=(float *) malloc(nrow*ncol*sizeof(float));` is totally wrong. Having obtained memory for `float **ptr;` you must then do a loop for each row, to obtain memory for the number of columns. – Weather Vane Jul 17 '18 at 20:42
  • @WeatherVane let's forget about this. Assume the `(nrow+8) * (ncol+8)` matrix is well allocated and initialized, how can I move the pointer to make the index starts from `-4` – coco Jul 17 '18 at 20:50
  • 4
    Nope, assume nothing: please post the [Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve) that shows the problem, to prevent what you think are off-topic comments. – Weather Vane Jul 17 '18 at 20:52
  • @WeatherVane *Having obtained memory for `float **ptr;` you must then do a loop for each row, to obtain memory for the number of columns.* No, there's no need to do an individual `malloc` for each row. You can just do a loop to fill in the pointers: `ptr[0]=malloc(nrow*ncol*sizeof(float)); for (i=1;i – Andrew Henle Jul 17 '18 at 20:59
  • Related: https://stackoverflow.com/questions/3473675/are-negative-array-indexes-allowed-in-c/3473686?s=3|43.5144#3473686 – Barmar Jul 17 '18 at 21:02
  • @AndrewHenle it is possible to make a DIY for that, but OP's first `malloc` does not get memory for a whole contiguous array, just an array of pointers for each row, yet to be allocated. – Weather Vane Jul 17 '18 at 21:03
  • @WeatherVane *[t]he first malloc does not get memory for a whole contiguous array, just an array of pointers for each row, yet to be allocated* But the **second** `malloc()` does get enough memory for a contiguous array. The fault in the code is never filling in the pointers past `ptr[0]`. – Andrew Henle Jul 17 '18 at 21:05
  • @AndrewHenle what are you on? – Weather Vane Jul 17 '18 at 21:06
  • @WeatherVane I'm on nothing. `ptr=(float **) malloc(nrow*sizeof(float*)); ptr[0]=(float *) malloc(nrow*ncol*sizeof(float));` gets plenty of memory for a contiguous array. As I posted, it just doesn't fill in the pointers past `ptr[0]`. That can be done with an almost trivial `for (i=1;i – Andrew Henle Jul 17 '18 at 21:08
  • @AndrewHenle so it doesn't work, unless you do something convoluted but "trivial". . . Tell me the point of the first `malloc`, for the row pointers? Usually, you then loop to allocate memory for each row, as I said. – Weather Vane Jul 17 '18 at 21:09
  • @WeatherVane Naively calling `malloc()` for every row in a two-dimensional "array" is horribly inefficient. Any n-dimensional "array" is almost trivial to allocate with only n calls to `malloc()`. n-1 calls for arrays of pointers, and one call for the entire data area. if you want to go through the trouble, it's even possible to do an n-dimensional "array" that's actually the common "pointers to pointers to pointers to a one-dimensional array" that is normally implemented with multiple `malloc()` calls in deeply-nested loops with only a single call to `malloc()`. – Andrew Henle Jul 17 '18 at 21:15
  • @AndrewHenle that may be true, but OP first allocated memory for an array of pointers, but then ignored that and only uses the first element to allocate enough memory for a contiguous 2D array. – Weather Vane Jul 17 '18 at 21:18
  • @WeatherVane And then he failed to fill in the rest of the pointers. I'm guessing he copied the code and missed a `for` loop. – Andrew Henle Jul 17 '18 at 21:20
  • @AndrewHenle you are contradicting yourself. I already wrote that the array of pointers needs to be allocated. OP either meant to allocate a single 2D array, or an array of pointers. Whether this is efficient is off-topic. – Weather Vane Jul 17 '18 at 21:21
  • @WeatherVane The second `malloc()` call - `ptr[0]=(float *) malloc(nrow*ncol*sizeof(float));` gets all the memory needed for a `nrow` by `ncol` two-dimensional "array". There's no need to obtain more with additional calls to `malloc()`. – Andrew Henle Jul 17 '18 at 21:25
  • Please look at my supplementary code. It can run correctly! – coco Jul 17 '18 at 21:29
  • @AndrewHenle yes it does, but 1) it wastes the allocation of the row pointers array, and 2) the first code posted won't work to index a DIY 2D array. That's why there needs to be a loop allocating memory for each row. – Weather Vane Jul 17 '18 at 21:54
  • 1
    @WeatherVane *That's why there needs to be a loop allocating memory for each row.* No, there doesn't. `ptr=malloc(nrow*sizeof(float*)); ptr[0]=malloc(nrow*ncol*sizeof(float)); for (i=1;i – Andrew Henle Jul 17 '18 at 22:11
  • @AndrewHenle you missed the best part:) One malloc means that its not possible to easily add/extend/delete/move/remove/sort or otherwise manipulate each row. Of course, that may be irrelevant in many apps, just as the extra overhead of one-malloc-per-row may be unimportant in others. – Martin James Jul 17 '18 at 22:16
  • @MartinJames True, and that's a trade off that needs to be addressed. My issue is with the claim that each row has to be allocated with a separate call to `malloc()` and there's no way to allocate a two-dimensional "array" otherwise.Of course it's possible - I posted the code in the comments. And if you want, [you can even dynamically allocate true 2-d array with a single `malloc()` call](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Andrew Henle Jul 17 '18 at 22:21

3 Answers3

1

Strictly speaking, you can't index before the start or after the end of an array. You can however make the array bigger than you intend and put the starting pointer somewhere in the middle:

int nrow = 5, ncol = 5, pre = 4, post = 4;
int i,j;
float **ptr;

ptr = malloc((nrow+pre+post)*sizeof(float*));
for (i=0;i<nrow+pre+post;i++)
    ptr[i] = malloc((ncol+pre+post)*sizeof(float));
    ptr[i] += pre;   // move up start of column pointers
}
ptr += pre;  // move up start of row pointer

/*initialize*/
for (i=0;i<nrow;i++) {
    for (j=0;j<ncol;j++) {
        ptr[i][j]=0.0;
    }
}

Note that the allocation for the columns is done once for each column, not in a bunch in the first column. The way you had it, anything past the first column doesn't point to anything.

Now you can safely access indexes from -4 to max + 3.

To clean up:

ptr -= pre;
for (i=0;i<nrow+pre+post;i++)
    ptr[i] -= pre;
    free(ptr[i]);
}
free(ptr);
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Not sure this is working. Try looping i from -4 to nrow+3 and j from -4 to ncol+3 in the initialize loop, you should get a segfault. – cleblanc Jul 17 '18 at 20:58
  • Please look at my supplementary code. It can run correctly! – coco Jul 17 '18 at 21:27
  • Having separate `pre/post` variables for row and column (and of different values) would add clarity to this solution. `ptr[i] += pre; .... ptr += pre;` _looks_ wrong. – chux - Reinstate Monica Jul 17 '18 at 21:33
  • 1
    Or indeed, you can make each array involved exactly the size you intend, and use pointers elements in the middle (or one position past the end) for the indexing base. Of course, any variation on an approach like this makes it trickier to free the allocated space. – John Bollinger Jul 17 '18 at 21:44
1

Let's say your array has 7 elements and is laid out as below:

+-----+-----+-----+-----+-----+-----+-----+
|     |     |     |     |     |     |     |
+-----+-----+-----+-----+-----+-----+-----+

If a pointer points to the first element of the array, it can be indexed as:

p
|
v
+-----+-----+-----+-----+-----+-----+-----+
|     |     |     |     |     |     |     |
+-----+-----+-----+-----+-----+-----+-----+
p[0]  p[1]  p[2]  p[3]  p[4]  p[5]  p[6]  

If a pointer points to an element in the middle of the array, it can be indexed using negative values.

                  p
                  |
                  v
+-----+-----+-----+-----+-----+-----+-----+
|     |     |     |     |     |     |     |
+-----+-----+-----+-----+-----+-----+-----+
p[-3] p[-2] p[-1] p[0]  p[1]  p[2]  p[3]  

If the pointer points to one element past the last element, it can be indexed using only negative values.

                                          p
                                          |
                                          v
+-----+-----+-----+-----+-----+-----+-----+
|     |     |     |     |     |     |     |
+-----+-----+-----+-----+-----+-----+-----+
p[-7] p[-6] p[-5] p[-4] p[-3] p[-2] p[-1] 

It is not valid for the pionter to point to anything before the first element. Hence, no matter where the pointer points to in the valid range of elements, a valid index cannot be less than -7 or greater than 6.

Coming to your question

But how can I let the row start from -4 and end in nrow+3, also let the column start from -4 and end in ncol+3?

You cannot. If a pointer points to the 5-th element of the array, you can use -4 as a valid index but then the end condition is going to be nrow-4/ncol-4. nrow + <some number>/ncol + <some number> will never be the correct end index.

float** ptr1 = &(5-th row of the array);
for ( int i = -4; i < nrow - 4; ++i )
{
   // OK to use ptr1[i];
   float* ptr2 = &(5-the element/column of the row)
   for ( int j = -4; j < ncol - 4; ++j )
   {
      // OK to use ptr2[j];
   }
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • @R Saha Please look at my supplementary code. It can run correctly! – coco Jul 17 '18 at 21:26
  • @coco, You are accessing memory beyond the valid bounds. Your code is subject to undefined behavior. It may appear to work but, unfortunately, seemingly sane behavior is also under undefined behavior. I wouldn't count on any predictable behavior from that program. – R Sahu Jul 17 '18 at 21:31
  • @RSahu, your "you cannot" seems to be predicated on the assumption that the OP's `nrow` and `ncol` represent the actual number of rows and columns allocated. If you do not apply that interpretation, then of course the behavior the OP requests is possible via an approach such as you describe here. The arrays simply need to be allocated large enough, which indeed the OP's sample code appears to do. – John Bollinger Jul 17 '18 at 21:49
  • @JohnBollinger, you are right. The OP is allocating more memory than I thought. – R Sahu Jul 17 '18 at 21:58
  • 1
    @coco, I take that comment back. Your program is not subject to undefined behavior. You are allocating more memory than I thought. Adding some more output to your program revealed some of those details. https://ideone.com/XgiYDs. My answer is still valid from a conceptual stand point. You can use any pointers and indices to your heart's content as long as the combination is valid. – R Sahu Jul 17 '18 at 22:00
0

You can do something like this, but I don't think you can access the columns offset like that, you will need to point at each row.

main()
{

    int nrow,ncol;
    int i,j;
    float **ptr;
    float *p;

/*allocation*/
    nrow=5;ncol=7; 
    ptr=(float **) malloc((nrow+7)*sizeof(float*));
    for (i=0; i<nrow+7;i++)
        ptr[i]=(float *) malloc((ncol+7)*sizeof(float));
/*initialize*/
    ptr = &ptr[4];
    for (i=-4;i<nrow+3;i++)
        for (j=-4,p=&ptr[i][4];j<ncol+3;j++)
            p[j]=0.0f;
}
cleblanc
  • 3,678
  • 1
  • 13
  • 16