sizeof(array[ ])

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

Hi,

I'm struggling with getting the size of a const array with undefined size. The array is defined in a .c file which is not the main .c file. See code:

// In separate.c file (listed in makefile):
const char ConstArray[]={2, 3, 5, 7, 11};

// In separate.h file (included in separate.c and main.c):
extern const char ConstArray[];

// In main.c file:
if (memchr(ConstArray, Blah, sizeof(ConstArray)))

The above approach doesn't work, and I don't know why. I get the "invalid application of 'sizeof' to incomplete type 'const char[]' " error.

To fix it, I can either set a defined size (in this case 5) of the array, or I can move the definition of the array into the main.c file.

// In separate.c file (listed in makefile):
const char ConstArray[5]={2, 3, 5, 7, 11};

// In separate.h file (included in separate.c and main.c):
extern const char ConstArray[5];

// In main.c file:
if (memchr(ConstArray, Blah, sizeof(ConstArray)))


or


// In main.c file:
const char ConstArray[]={2, 3, 5, 7, 11};
if (memchr(ConstArray, Blah, sizeof(ConstArray)))

Can anybody explain why?

µCtlr: ATmega1281
Compiler version: 20100110

Best regards,
ErikT

You're absolutely right. This member is stupid. Please help.

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

Spooky coincidence! The same question was asked yesterday:

https://www.avrfreaks.net/index.p...

C simply does not carry size/length information with a pointer to array. As I noted there you have to convey length separately.

Folks do seem to assume there is more to sizeof() than there actually is!

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

Thanks for the link.I didn't see a connection to my specific problem in that topic when I was searching.

Anyway, what I (still) don't quite get is: Why does it work when the const array is defined in the same file as the sizeof() argument, and not when it is defined in another file? Is it because the compiler doesn't pass size information from one .c file to another, but only within a .c file?

You might already have answered this, but then I'm a bit slow...

You're absolutely right. This member is stupid. Please help.

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

Quote:

Is it because the compiler doesn't pass size information from one .c file to another, but only within a .c file?

All that's passed between the files is a 16 bit number which is an address of where the data is located in memory. That 16 bit number holds NO information about the number of bytes that it is "pointing" at. Your best bet is to do:

// In separate.c file (listed in makefile):
const char ConstArray[]={2, 3, 5, 7, 11};

// In separate.h file (included in separate.c and main.c):
extern const char ConstArray[];
#define ARRAY_LEN 5

// In main.c file:
if (memchr(ConstArray, Blah, ARRAY_LEN))

or perhaps:

// In separate.c file (listed in makefile):
const char ConstArray[]={2, 3, 5, 7, 11};
int get_len(void) {
  return sizeof(ConstArray);
}

// In separate.h file (included in separate.c and main.c):
extern const char ConstArray[];

// In main.c file:
if (memchr(ConstArray, Blah, get_len())

This latter one is a very C++ kind of thing to do. To be real C++ I guess you'd have ConstArray.data and ConstArray.len - the former providing a pointer to data and the latter providing length.

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

ErikT wrote:
Is it because the compiler doesn't pass size information from one .c file to another, but only within a .c file?
Basically, yes.

In the language of the standard, int x[]; defines an incomplete type (and so does the extern int x[] which you include into the c file which "doesn't work"); but the initializer changes it into a complete type.

You can do several things, none of them very elegant. Besides defining a constant size for the array or spending a variable (which can be in PROGMEM to spare the precious RAM), you can also move the initializer into the shared header as a macro, and get the size out of it, e.g.

// In separate.c file (listed in makefile):
const char ConstArray[]=CONST_ARRAY_INIT;


// In separate.h file (included in separate.c and main.c):
#define CONST_ARRAY_INIT {2, 3, 5, 7, 11}
extern const char ConstArray[];
#define SIZE_OF_CONST_ARRAY sizeof((typeof(ConstArray))CONST_ARRAY_INIT)

// In main.c file:
if (memchr(ConstArray, Blah, SIZE_OF_CONST_ARRAY)) 

If more C99-compatibility is needed, the non-standard gcc extension (typeof()) can be replaced by explicit naming of the array type (here, (const char[]) ).

JW

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

clawson wrote:
Quote:

Is it because the compiler doesn't pass size information from one .c file to another, but only within a .c file?

All that's passed between the files is a 16 bit number which is an address of where the data is located in memory.
I'd like to add, that that number is passed during linking, not earlier. And, at that moment, all information about the types is already lost long ago.

JW

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

Quote:

Is it because the compiler doesn't pass size information from one .c file to another

As others have said: Yes.

Your question reflects a misconception / lack-of-nowledge that I think I see here from time to time. I am curious as to where this comes from. My hypothesis is that it is due to certain other programming languages that has been learned before taking on C.

So, may I ask you (so as to help me accept or reject my hypotyesis): What programming languages, if any, did you know before learning C?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Good question - I wondered about that too - it seems that new programmers are not being taught about the processes that occur at compile/link time any more. We see a lot of misunderstanding about the C pre-processor and also about what an optimising compiler will calculate during the compilation.

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

I just recalled that I really do hate C (see also the gotcha thread I am going to post right now).

C99, footnote 85 wrote:
85) When applied to a parameter declared to have array or function type, the sizeof operator yields the size of the adjusted (pointer) type (see 6.9.1).

Derek Jones, as an explanation wrote:
In the following the array b will be declared to have an upper bound of sizeof(int *), not the number of bytes in the array a.
1 void f(int a[3])
2 {
3   unsigned char b[sizeof(a)];
4 }


JW

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

Quote:
So, may I ask you (so as to help me accept or reject my hypotyesis): What programming languages, if any, did you know before learning C?

About three decades ago: Basic (well, you asked!)
After that, Turbo Pascal, later Delphi and a tiny bit of Java (brrrr!). And then C.
Quote:
...it seems that new programmers are not being taught about the processes that occur at compile/link time any more.

You are absolutely right!

I've known all along that sizeof() is not a function, but rather a macro. But much of what really happens behind the curtain, when the compiler is at work, is still a mystery.

Anyway, I figured out a workaround.

// in separate.c file
const char ConstArray[]={2, 3, 5, 7, 11};
const char ArraySize=sizeof(ConstArray);

// in separate.h file
extern const char ConstArray[];
extern const char ArraySize;

// in main.c file
if (memchr(ConstArray, Blah, ArraySize))

Might not be pretty, but seems to work.

You're absolutely right. This member is stupid. Please help.

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

Quote:

Basic (well, you asked!)
After that, Turbo Pascal, later Delphi and a tiny bit of Java (brrrr!).

Thank you. This contributes to the score for me to accept my hypothesis, which in this thread now can be revealed:

Neithwer of those compilers/languages does separate compilation and then linking in the way that C does.

Many oldies has grown up on tool chains that are working very similar to a C tool chain. In my case COBOL, FORTRAN on Univacs OS-1100 systems. And Pascal, Simula, Modula-II, COBOL, FORTRAN and more on Digitals VAX/VMS. And C and C++ on MS-DOS/Windoze. They all did separate compilation and linking. The interpreted languages where the oddballs: BASIC, APL, Lisp.

So while some (well, "me") has grown up on separate compilation and linking and find Java and C#'s build processes somewhat exotic, I suspect that for many young ones the opposite holds true. Especially the hordes of youngsters that come with mainly Java and/or C# in their "luggage from Uni". Nice languages/systems, but could contribute to trouble when learning e.g. a C/C++ tool chain.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

Quote:

I've known all along that sizeof() is not a function, but rather a macro

It's neither a function nor a macro - it's an operator.

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

Quote:
It's neither a function nor a macro - it's an operator.

Yeah, operator. Not macro. My bad. I'm tired...

Quote:
...I suspect that for many young ones the opposite holds true. Especially the hordes of youngsters...

Gee, thanks! :D

You're absolutely right. This member is stupid. Please help.

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

By the way, I forgot to add "PC assembly language" to my list. But that hardly matters, I guess...

You're absolutely right. This member is stupid. Please help.

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

clawson wrote:
It's neither a function nor a macro - it's an operator.

And don't forget that the result is an unsigned integer.

;-)

Stefan Ernst

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

ErikT wrote:
I've known all along that sizeof() is not a function, but rather a macro.
Actually, it's an operator - a prefix operator, much like the dereferencing * or the prefix ++. It means, that you don't need to write parentheses when applied to a variable (more precisely, to an expression - and those in turn may be parenthesized at your will). However, contrary to other operators, it can be applied to types, which MUST be parenthesized.

ErikT wrote:
Anyway, I figured out a workaround.
Yes that's the "spending a variable" I mentioned above.

Personally, I generally prefer cheaper solutions in microcontrollers (that's why I despise C++ and its methods, when it comes to AVRs).

JW

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

Quote:
Actually, it's an operator - a prefix operator...

Yesyesyes - IknowIknowIknow! Just being too tired in a too big office, being disturbed too often...
Quote:
Personally, I generally prefer cheaper solutions in microcontrollers...

That's where the interesting part of this job comes in. When working with optical systems, the price tag on a microcontroller doesn't matter at all.

You're absolutely right. This member is stupid. Please help.

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

Quote:
Your question reflects a misconception / lack-of-nowledge that I think I see here from time to time. I am curious as to where this comes from.

Quote:
It's neither a function nor a macro - it's an operator.
Moreover, it is a compile time operator, not a run time operator.

Regards,
Steve A.

The Board helps those that help themselves.

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

I guess it's because it looks like a function invocation that maybe people think it's a library function like strlen() or something?

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

In the situation described, I would test instead of generate:

fred.h
extern char fred[];
#define FRED_SIZE 6

fred.c
#include "fred.h"
char fred[]={2, 3, 5, 7, 11} ;

enum { fred_size_test=1/(FRED_SIZE==(sizeof(fred)/sizeof(fred[0]))) } ;

In the example given, fred.c would fail to compile because FRED_SIZE is not five.

A messier alternative is code generation: Have fred_size.h depend on fred.c .
Generate its one line from a script that counts commas between "fred[]=" and ";".

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods