Determine number of rows and columns of array passed by reference

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Evening all,

 

So, I've got a function with deals with interpolating lookup tables, which is somewhat cumbersome as it uses several switch/case statements to retrieve fixed values which define the size of arrays, so it knows how many times to iterate through the lookup tables.

 

However, I want to make this far more general - and be able to make it interpolate from any lookup table without knowing about that table first.

 

One thing I'm stuck with, if I pass an array by reference to a function, how can I determine the number of rows and columns of the array to which I now have a pointer to, within the function itself?

 

I'm assuming the use of sizeof still somehow...

 

Cheers!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Putting your lookup tables in a structure and passing that may be the answer your looking for, see this thread and example:

https://www.avrfreaks.net/commen...

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Jim, I'll go take a look.

These lookups are already tucked away in a struct for other reasons, so a nice lead in.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Mostly, the tradition is to pass the number of entries in the array as one of the arguments.

For char strings, the tradition is a null byte are the end.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not sure that would be any more 're-useable' than what I'm doing now though, as unless I can dynamically work out the size of arrays (bearing in mind these are 2D arrays), I'm still just pushing data around with a bunch of #defines.

Last Edited: Wed. Jun 10, 2020 - 08:17 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jtw_11 wrote:

One thing I'm stuck with, if I pass an array by reference to a function, how can I determine the number of rows and columns of the array to which I now have a pointer to, within the function itself?

 

For some reason you forgot to provide code that would illustrate what you are talking about. It is completely unclear form your question what kind of array you are trying to pass.

 

But taking what you said literally, you are apparently talking about C++ references. In that case the array sizes are always embedded into the reference type, which makes the whole question trivial. For example

 

template <size_t M, size_t N> void foo(int (&a)[M][N]) // <-- array passed by reference
{
  // Here M and N are your sizes
}

In C you can use VLA (assuming your compiler supports them) and just pass the array sizes as separate parameters

 

void foo(size_t m, size_t n, int (*a)[m][n])
{
  ...
}

 

If you meant something else, you need to clarify it.

 

 

Dessine-moi un mouton

Last Edited: Wed. Jun 10, 2020 - 08:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:
C++ references. In that case the array sizes are always embedded into the reference type, which makes the whole question trivial. For example

 

Indeed. Expanding a bit on that example:

 


uint8_t matrix0[5][3];
uint16_t matrix1[4][7];

template <class T, size_t M, size_t N>
size_t getElementSize(T (&a)[M][N]) // <-- array passed by reference
{
  return sizeof(T);
}

template <class T, size_t M, size_t N>
size_t getNSize(T (&a)[M][N]) // <-- array passed by reference
{
  return N;
}

template <class T, size_t M, size_t N>
size_t getMSize(T (&a)[M][N]) // <-- array passed by reference
{
  return M;
}

int main() {
  volatile uint8_t Element0 = getElementSize(matrix0);
  volatile uint8_t N0 = getNSize(matrix0);
  volatile uint8_t M0 = getMSize(matrix0);
  volatile uint8_t Element1 = getElementSize(matrix1);
  volatile uint8_t N1 = getNSize(matrix1);
  volatile uint8_t M1 = getMSize(matrix1);
  return 0;
}

 

 

Will generate this AVR code:

 

  volatile uint8_t Element0 = getElementSize(matrix0);
  a2:	81 e0       	ldi	r24, 0x01	; 1
  a4:	89 83       	std	Y+1, r24	; 0x01
  volatile uint8_t N0 = getNSize(matrix0);
  a6:	83 e0       	ldi	r24, 0x03	; 3
  a8:	8a 83       	std	Y+2, r24	; 0x02
  volatile uint8_t M0 = getMSize(matrix0);
  aa:	85 e0       	ldi	r24, 0x05	; 5
  ac:	8b 83       	std	Y+3, r24	; 0x03
  volatile uint8_t Element1 = getElementSize(matrix1);
  ae:	82 e0       	ldi	r24, 0x02	; 2
  b0:	8c 83       	std	Y+4, r24	; 0x04
  volatile uint8_t N1 = getNSize(matrix1);
  b2:	87 e0       	ldi	r24, 0x07	; 7
  b4:	8d 83       	std	Y+5, r24	; 0x05
  volatile uint8_t M1 = getMSize(matrix1);
  b6:	84 e0       	ldi	r24, 0x04	; 4
  b8:	8e 83       	std	Y+6, r24	; 0x06

 

C++ is great, because it allows access to much more stuff the compiler knows at compile time than plain C, like array dimensions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you are using C not C++, you can get the number of rows and columns using sizeof, just as you can for a 1D array, a 2D array is just an array of arrays.

But you can't do this from a pointer. You need the array itself.

If the compiler supports VLA as mentioned in #6 (thanks, I keep forgetting that VLAs exist!) you could do for example

static void do_stuff(int rows, int cols, int a[rows][cols])
{
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

#define NUM_ELEMENTS(a) (sizeof(a) / sizeof((a)[0]))

#define NUM_ROWS(a) NUM_ELEMENTS(a)
#define NUM_COLS(a) NUM_ELEMENTS((a)[0])

int main(void)
{
    int a[2][3] = { {1,2,3}, {4,5,6}};
    int b[3][4] = { {7,8,9,10}, {11,12,13,14}, {15,16,17,18}};

    do_stuff(NUM_ROWS(a), NUM_COLS(a), a);

    do_stuff(NUM_ROWS(b), NUM_COLS(b), b);
}

If you don't have VLA, in terms of passing into a function, the number of columns would have to be the same in all cases, it'is implicit in the definition of the parameter.

In which case you probably need to do something different, for example flatten it all out into a 1D array and sort out the indexing yourself.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For rectangular arrays, the usual kind, the tradition has long

been to pass the array dimensions as separate parameters.

 

The subject is not all that interesting unless one has non-rectangular arrays,

e.g. an array of arrays of varying sizes.

In that case, one can pass an array of pointers or of pointer/size pairs.

If the sub-arrays are contigous, the difference between two pointers will give a size.

If the sub-arrays are not contiguous, pointer/size pairs will be necessary.

 

Dealing with arrays or varying sizes of arrays of varying sizes is left as an exercise for the reader.

 

OPs difficulty with this suggests taking a step back

and looking at the problem that inspired this one.

This might be similar to the frequent requests to pass IO registers to functions.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

For rectangular arrays, the usual kind, the tradition has long been to pass the array dimensions as separate parameters.

 

Um... That' a rather strange statement. If by "rectangular array" you mean a classic C-style array `int a[M][N]`, then formally it is virtually impossible to pass this array to a function with "array dimensions as separate parameters", unless you are using a compiler that supports VLA. And VLAs are hardly a "tradition".

 

skeeve wrote:
The subject is not all that interesting unless one has non-rectangular arrays, e.g. an array of arrays of varying sizes.

 

Well, that's actually closer to the "tradition". In many cases one would implement a 2D array as "an array of pointers to sub-arrays". This representation is very different from the "classic" `int a[M][N]`, but such an array is indeed very easy to pass to a function (as `int **`) accompanied by array dimensions as separate parameters.

 

Of course, I'm not talking about the situation when the sub-arrays would have varying sizes.

Dessine-moi un mouton

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:

skeeve wrote:

For rectangular arrays, the usual kind, the tradition has long been to pass the array dimensions as separate parameters.

 

Um... That' a rather strange statement. If by "rectangular array" you mean a classic C-style array `int a[M][N]`, then formally it is virtually impossible to pass this array to a function with "array dimensions as separate parameters", unless you are using a compiler that supports VLA. And VLAs are hardly a "tradition".

The caller would pass a, M, N as &(a[0][0], M. N.  The callee would refer to a[Jm*N+Jn].

M and Jm refer to the most significant dimension, N and Jn, the other.

I'd thought this was very old news.

 

Of course, even in the Old Times (before C),

FORTRAN allowed one to declare arrays with parameters as dimensions.

Iluvatar is the better part of Valar.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

The caller would pass a, M, N as &(a[0][0], M. N.  The callee would refer to a[Jm*N+Jn].

This is different in that you aren't passing pointer to a 2D array, you are treating it as a flattened 1D array.

I've never been able to work out if there is anything technically a bit dodgy with this

ie. because it is defined as 2D array (array of arrays) and you are giving pointer to what is the first element of the first sub-array, is it technically OK to advance that pointer outside of the first sub-array into the next sub-array, or is it technically only valid within the first sub-array.

Don't know. But I can't imagine it not working.

You could of course just define it as a 1D array in the first place then clearly it must be fine.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:

AndreyT wrote:

 

 

skeeve wrote:

 

For rectangular arrays, the usual kind, the tradition has long been to pass the array dimensions as separate parameters.

 

Um... That' a rather strange statement. If by "rectangular array" you mean a classic C-style array `int a[M][N]`, then formally it is virtually impossible to pass this array to a function with "array dimensions as separate parameters", unless you are using a compiler that supports VLA. And VLAs are hardly a "tradition".

The caller would pass a, M, N as &(a[0][0], M. N.  The callee would refer to a[Jm*N+Jn].

M and Jm refer to the most significant dimension, N and Jn, the other.

I'd thought this was very old news.

 

Well, that's indeed old news. Just as old news is the fact that the behavior is undefined. Formally, accessing M*N elements through &a[0][0] pointer performs out-of-bounds access on a[0] array. The behavior is undefined.

 

We all know that this lame hack is sometimes used in mediocre-grade code and that it "works". We know why it "works". Nevertheless, it is used only by people who are too lazy or too incompetent to find a better solution.

 

Dessine-moi un mouton

Last Edited: Sat. Jun 13, 2020 - 09:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:

We all know that this lame hack is sometimes used in mediocre-grade code and that it "works". We know why it "works". Nevertheless, it is used only by people who are too lazy or too incompetent to find a better solution.

 

Yet again you show that you are a rude arrogant know-it-all. Why do you feel the need to do that?

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Brian Fairchild wrote:
Yet again you show that you are a rude arrogant know-it-all. Why do you feel the need to do that?

Agreed - The "lame code & incompetent" comment does come across as very condescending as opposed to that of a friendly freak offering advice. Crucially though; the advice for a "better solution" was missing.

---

 

So back on track:

jtw_11 wrote:
So, I've got a function with deals with interpolating lookup tables

jtw_11 wrote:
how can I determine the number of rows and columns of the array to which I now have a pointer to, within the function itself?

Surely those two phrases are mutually exclusive.

If you're doing linear interpolation between adjacent pairs of {x,y} values then surely the number of columns is fixed at two.

To determine the number of rows; I would (and have done) stored the row count in the first row.

 

PS: I suppose if you have a table that looks like {x, y1, y2} you're gong to be stuck with writing a different "getter" function to match the layout.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:
Well, that's indeed old news. Just as old news is the fact that the behavior is undefined. Formally, accessing M*N elements through &a[0][0] pointer performs out-of-bounds access on a[0] array. The behavior is undefined.

 

We all know that this lame hack is sometimes used in mediocre-grade code and that it "works". We know why it "works". Nevertheless, it is used only by people who are too lazy or too incompetent to find a better solution.

You really want something non-undefined?  Ok:

int fetch(void *av, int M, int N, int jm, int jn)
{
    // array of unsigned char can alias any object
    // in this case the array is sizeof(int)*M*N bytes
    unsigned char *ptruc=(unsigned char *)av;  // aligned if av is
    ptruc+=sizeof(int)*(jm*N+jn);  // still aligned and in range if jm and jn are
    int *ptri=(int *)ptruc;        // guaranteed to point to a member of ai
    return *ptri;
}  // fetch

Isn't that better?

 

Much of this is moot if OP has variable length arrays.

 

BTW Even if OP has variable length arrays, I still think that OP ought

to take a step back and tell us what is really being attempted.

If the functions are small and the minor dimensions are a small number of compile-time constants,

a separate function for each minor dimension might be a good idea.

If they are run-time supplied data, that might not be a good option.

 

 

Edit: The minor dimension has to be know to the compiler that compiles the caller.

A separate function for each minor dimension will almost certainly work.

Iluvatar is the better part of Valar.

Last Edited: Sun. Jun 14, 2020 - 07:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:

Well, that's indeed old news. Just as old news is the fact that the behavior is undefined. Formally, accessing M*N elements through &a[0][0] pointer performs out-of-bounds access on a[0] array. The behavior is undefined.

 

We all know that this lame hack is sometimes used in mediocre-grade code and that it "works". We know why it "works". Nevertheless, it is used only by people who are too lazy or too incompetent to find a better solution.

 

It's just natural selection.

 

Compiler A treats undefined behaviour in a sensible and logical way that most people expect, and that is useful. In your words, it "works".

Compiler B formats your hard drive, to punish you for using such heretic undefined code.

 

Naturally, most people will use compiler A, while B will become extinct. Therefore, yes, it "works" and it will keep "working".

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

El Tangas wrote:

Compiler A treats undefined behaviour in a sensible and logical way that most people expect, and that is useful. In your words, it "works".

Compiler B formats your hard drive, to punish you for using such heretic undefined code.

 

No, not really.

 

When "compiler A treats undefined behaviour in a sensible and logical way", it is called a compiler extension. The compiler explicitly documents it. And there's nothing wrong with compiler extensions. We all know they exist and we all use them when need arises. But when compiler does not document the behavior, it remains undefined. If you'll claim that you saw "sensible behavior" in your practical experiments with undefined behavior, people will only give a sad patronizing smile, avert their eyes and try to politely switch subject.

 

The matter has a long, well documented and some might say hilarious history.

 

* Back in the day we had quite a few "programming experts", who expected signed types to wrap around on overflow - a "sensible and logical way" they said (contrary to the language spec). And then the era of strict overflow semantics began, prompting a massive chorus of painful squeals from the adepts of that strange interpretation of "sensible and logical".

 

* Then we had an army of "c00l hackerZ" who'd routinely type-pun through pointers. "We know how memory works, in sensible and logical way" they said (contrary to the language spec). And then the era of strict aliasing semantics dawned, triggering another explosion of painful wails from that crowd (and a temper tantrum from Torvalds)

 

* Then we had those "memcpy always copies left-to-right" affictionados and that infamous Mozilla scandal... (and another temper tantrum from Torvalds)

 

I can continue the list. I should probably even draw a picture with a long-winded sequence of those events, and somewhere after all that and before "out-of-bounds array access" I'd put an X mark and say "you are here". But I won't. Firstly, I'm not that good at drawing. Secondly, that would simply by untrue. You are not there. You are way way way further down that line. You just don't realize that yet. It is simply that those issues I mentioned above have already come around to bite you (and us) in the arse, while "out-of-bounds array access" (or "type-punning 2D array as a 1D array") hasn't found you yet. It is busy biting in the arse someone else. But it will get you eventually.

 

So, circling back to the matter of natural selection... Yes, it is natural selection. Except that it works differently. While there's still time we are trying to educate those who are stuck in their myopic interpretation of "sensible and logical", which is incidentally what I'm doing right here right now. But eventually time runs out and reality itself has to take over. And reality itself is a harsh mistress: it puts a railroad spike of knowledge to the sculls of those who fail to learn and drives it in with a massive blow of a sledge hammer. Those who survive this become better and smarter. But some don't survive this. That's how natural selection works in such matters. Compilers don't become extinct. People who refuse to learn do.

 

 

Dessine-moi un mouton

Last Edited: Mon. Jun 15, 2020 - 02:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

AndreyT wrote:
While there's still time we are trying to educate those who are stuck in their myopic idea of "sensible and logical", which is incidentally what I'm doing right here right now.
Rubbish.  If that were really your aim, you would be presenting your:
N.Winterbottom wrote:
advice for a "better solution"
However you seem to prioritise efforts to:
Brian Fairchild wrote:
show that you are a rude arrogant know-it-all

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]