Find the size of an array

A

arnuld

I want to create a new array of the same size of an array already
available:


#include <stdio.h>
#include <string.h>

int main(void)
{
char arrc[] = "URI";
const int asize = sizeof(arrc) / sizeof (arrc[0]);
char new_arr[asize];

printf("new_arr size = %d\n", sizeof(new_arr));

return 0;
}

======================== OUTPUT ====================
[arnuld@dune C]$ gcc4 -ansi -pedantic -Wall -Wextra test.c
test.c: In function ‘main’:
test.c:9: warning: ISO C90 forbids variable-size array ‘new_arr’
[arnuld@dune C]$ ./a.out
new_arr size = 4
[arnuld@dune C]$



The program gives correct answer. But Why the warning. sizeof() is compile
time operator then why do I get some warning related to run-time ?
 
M

Michael

arnuld said:
I want to create a new array of the same size of an array already
available:


#include <stdio.h>
#include <string.h>

int main(void)
{
char arrc[] = "URI";
const int asize = sizeof(arrc) / sizeof (arrc[0]);
char new_arr[asize];

printf("new_arr size = %d\n", sizeof(new_arr));

return 0;
}

======================== OUTPUT ====================
[arnuld@dune C]$ gcc4 -ansi -pedantic -Wall -Wextra test.c
test.c: In function ‘main’:
test.c:9: warning: ISO C90 forbids variable-size array ‘new_arr’
[arnuld@dune C]$ ./a.out
new_arr size = 4
[arnuld@dune C]$



The program gives correct answer. But Why the warning. sizeof() is compile
time operator then why do I get some warning related to run-time ?
I have a warning on line 10. Use %lu instead of %d solves it. There is
no problems elsewhere.
 
K

Keith Thompson

Michael said:
arnuld said:
I want to create a new array of the same size of an array already
available:
#include <stdio.h>
#include <string.h>
int main(void)
{
char arrc[] = "URI";
const int asize = sizeof(arrc) / sizeof (arrc[0]);
char new_arr[asize];
printf("new_arr size = %d\n", sizeof(new_arr));
return 0;
}
======================== OUTPUT ====================
[arnuld@dune C]$ gcc4 -ansi -pedantic -Wall -Wextra test.c
test.c: In function ¡main¢:
test.c:9: warning: ISO C90 forbids variable-size array ¡new_arr¢
[arnuld@dune C]$ ./a.out new_arr size = 4
[arnuld@dune C]$ The program gives correct answer. But Why the
warning. sizeof() is compile
time operator then why do I get some warning related to run-time ?
I have a warning on line 10. Use %lu instead of %d solves it.

It solves it only if size_t happens to be a typedef for unsigned long.
For C90, the portable solution is to use "%lu" *and* convert the
sizeof expression to unsigned long:

printf("new_arr size = %lu\n", (unsigned long)sizeof new_arr);
There is
no problems elsewhere.

As I think has already been mentioned in this thread, asize is not a
constant expression, so it can't be used as an array size in C90. In
C99, new_arr is a VLA (variable-length array). A solution to this
that's portable to both C90 and C99 has already been posted: use
"sizeof arrc / sizeof arrc[0]" directly as the array size.
 
A

arnuld

gcc is being a little terse here (for understandable reasons). Yes,
sizeof's result is evaluated by the compiler. But, by the time the
compiler is looking at new_arr, it's forgotten all about the sizeof in the
previous line. All it can see is that you've got this asize thing, which
is a const int *but NOT an integral constant expression* - a puzzling
distinction, but a very real one in C.

Now what is the difference between a "const int" and an "inegeral constant
expression". I can't seem to understand it. May I have 2 examples
showing the distinction, so that I can comprehend something out of them.


You can get what you want, however, by cutting out the middle-man:
char arrc[] = "URI";
char new_arr[sizeof arrc / sizeof arrc[0]];


That works fine. Now the real problem is to understand the confusing
distinction.
 
A

arnuld

A const int is simply an int that you've promised not to change, by
"decorating" it with the word "const".


So compiler can change it, I can not ?

On the other hand, "integral constant expression" is a formal term that is
relevant to a number of aspects of C programming, amongst them the number
of elements specified in an array (or, in the case of C99, a non-VL array)
at the time of its declaration.
So - what characteristics does an integral constant expression have? "An
integral constant expression shall have integral type and shall only have
operands that are integer constants, enumeration constants, character
constants, sizeof expressions, and floating constants that are the
immediate operands of casts."
Examples of integer constants: 0, 6, 42
Examples of character constants: 'A', 'z', '.'
Example of sizeof expression: sizeof otherarray / sizeof otherarray[0]
Example of floating constants that are the immediate operands of casts:
(int)3.14159


Therefore array depends on an "integral constant expression" not a
constant integer. IOW, should I stop using const and start using enum for
a real constant ?
 
I

Ian Collins

arnuld said:
Therefore array depends on an "integral constant expression" not a
constant integer. IOW, should I stop using const and start using enum for
a real constant ?
Due to the broken nature of const in C, yes if you want a compile time
constant.

const is fine when an integral constant expression is not required.
 
K

Keith Thompson

arnuld said:
Now what is the difference between a "const int" and an "inegeral constant
expression". I can't seem to understand it. May I have 2 examples
showing the distinction, so that I can comprehend something out of them.

The tricky thing is that "const" does *not* mean "constant" -- or
rather "const" doesn't mean what C means by "constant". And yes, it's
a poor choice of words, but we're stuck with it, even though the word
"const" obviously is derived from the English word "constant".

"const" really means "read-only". If an object is declared "const",
then attempting to modify it is a constraint violation.

"constant", as in "constant expression", means roughly that the
expression can be evaluated at compile time. I say "roughly" because
that's not how the standard defines it; the standard restricts the
kinds of things that can appear in a constant expression. All
constant expressions can be evaluated at compile time, but not all
expressions that can be evaluated at compile time (by a sufficiently
clever compiler) are "constant expressions". Basically, the language
doesn't require the compiler to be *too* clever.

For example, this is perfectly legal:

const int r = rand();
const time_t start_time = time(NULL);

In both cases the initial value can't be computed until run time; the
"const" says that you're not allowed to modify the object once it's
been initialized. (You can *attempt* do modify it via a tricky
pointer cast, but that's undefined behavior.)

And here's another example where "const" is definitely not constant:

int var = 42;
const int *ptr = &var; /* pointer to const int */
var = 43; /* perfectly legal */
*ptr = 44; /* bzzzt! *ptr is const; you're not allowed
to change it */

Here the object has two "names", var and *ptr. The object itself
isn't read-only, but one of its two "names" is read-only. (The
standard doesn't use the word "name" this way, thus the quotation
marks.)

Another way to look at it: applying "const" to something asks the
compiler to complain if you attempt to modify it.

Now if I declare
const int x = 100;

then the compiler is certainly free to evaluate x at compile time,
replacing any reference to x with a literal 100. That's just a matter
of optimization; it doesn't change the visible behavior of the
program. What the compiler *can't* do is allow you to use "x" in a
context that requires a constant expression -- such as (in C90) an
array length.

(And yes, I'm using the word "allow" a bit loosely here; compilers can
permit such things, but you can't depend on it.)
 
P

Phil Carmody

Ian Collins said:
Due to the broken nature of const in C, yes if you want a compile time
constant.

Is it broken? I find for driver work that const volatile ints do
precisely what I want in C, and don't even let me try to do anything
that I wouldn't want.
const is fine when an integral constant expression is not required.

And haircuts are fine when lunch isn't required. Different beasties,
horses for courses, and stuff.

Phil
 
I

Ian Collins

Phil said:
Is it broken? I find for driver work that const volatile ints do
precisely what I want in C, and don't even let me try to do anything
that I wouldn't want.
const volatile is the exception.

There isn't any good reason why a const integral type can't be a compile
time constant. The only reason I can think of is oversight.
 
B

Barry Schwarz

const volatile is the exception.

There isn't any good reason why a const integral type can't be a compile
time constant. The only reason I can think of is oversight.

At least one problem is it requires the compiler to keep track of the
value of variable. While expressions like sizeof A / sizeof *A are
computable at compile time, there is no requirement to do so. And
what happens with
const int x = func();
 
P

Phil Carmody

Ian Collins said:
const volatile is the exception.

There isn't any good reason why a const integral type can't be a compile
time constant. The only reason I can think of is oversight.

Not all const ints, surely? I'm not sure anyone's called
for:

unsigned int foo(const unsigned int bar)
{
struct { unsigned int baz : bar; } a; // BANG!
a.baz=bar;
return a.baz;
}

(Then again, s/: bar/: ' '/ for an apparently legal horror.)


I'd vote for things "ABC"[1] being a compile-time constant with
the same value as 'B', though.

Phil
 
B

Ben Bacarisse

Ian Collins said:
const volatile is the exception.

There isn't any good reason why a const integral type can't be a compile
time constant.

How can it be one?

void f(const int x)
{
int a[x];
/* ... */
}

What you mean is probably more like "why can't a const-qualified
integer object, initialised with a constant expression, be used in
constant expressions?".
The only reason I can think of is oversight.

I think that is a very unlikely explanation. Personally, I prefer the
C++ definition but I don't think that was formalised in 1989 when C
got const as a keyword. I can't imagine that the C++ semantics were
not considered by the C99 committee but I hesitate to suggest why they
was rejected.
 
B

Ben Bacarisse

Keith Thompson said:
Michael said:
arnuld said:
I want to create a new array of the same size of an array already
available:
#include <stdio.h>
#include <string.h>
int main(void)
{
char arrc[] = "URI";
const int asize = sizeof(arrc) / sizeof (arrc[0]);
char new_arr[asize];
printf("new_arr size = %d\n", sizeof(new_arr));
return 0;
}
no problems elsewhere.

As I think has already been mentioned in this thread, asize is not a
constant expression, so it can't be used as an array size in C90. In
C99, new_arr is a VLA (variable-length array). A solution to this
that's portable to both C90 and C99 has already been posted: use
"sizeof arrc / sizeof arrc[0]" directly as the array size.

.... and if the size really does benefit from being named (for example
to help explain what is happening, or if it is used in several later
constant expressions) one could always use:

enum { vehicles = sizeof vtable / sizeof vtable[0] };
int engines[vehicles];
int wheels[4 * vehicles];
 
K

Keith Thompson

Ben Bacarisse said:
Keith Thompson said:
Michael said:
arnuld wrote:
I want to create a new array of the same size of an array already
available:
#include <stdio.h>
#include <string.h>
int main(void)
{
char arrc[] = "URI";
const int asize = sizeof(arrc) / sizeof (arrc[0]);
char new_arr[asize];
printf("new_arr size = %d\n", sizeof(new_arr));
return 0;
}
no problems elsewhere.

As I think has already been mentioned in this thread, asize is not a
constant expression, so it can't be used as an array size in C90. In
C99, new_arr is a VLA (variable-length array). A solution to this
that's portable to both C90 and C99 has already been posted: use
"sizeof arrc / sizeof arrc[0]" directly as the array size.

... and if the size really does benefit from being named (for example
to help explain what is happening, or if it is used in several later
constant expressions) one could always use:

enum { vehicles = sizeof vtable / sizeof vtable[0] };
int engines[vehicles];
int wheels[4 * vehicles];

The enum trick only works when the value is with the range of type int.

An alternative is to use a macro:

#define VEHICLES (sizeof vtable / sizeof vtable[0])
int engines[VEHICLES];
int wheels[4 * VEHICLES];
 
M

Martin Ambuhl

Ian said:
Due to the broken nature of const in C, yes if you want a compile time
constant.

Just because "const" does not behave as you want it to does not mean it
is broken. "const" is a type qualifier in the declaration of a
variable; if anything is "broken" it is your understanding.
 
I

Ian Collins

Martin said:
Just because "const" does not behave as you want it to does not mean it
is broken. "const" is a type qualifier in the declaration of a
variable; if anything is "broken" it is your understanding.
Rude as ever I see.

There's nothing wrong with my understanding, adopting the C++ definition
in C99 would have broken nothing and saved us from have to use enums (or
worse still, macros) for constants.
 
N

Nate Eldredge

Ian Collins said:
const volatile is the exception.

There isn't any good reason why a const integral type can't be a compile
time constant. The only reason I can think of is oversight.

Another issue is that a const int could also be extern. Perhaps its
definition is buried in a library somewhere, so that the value isn't
known until link time.

lib.c:
const int buffer_size = 17;

app.c:
extern const int buffer_size;
char buf[buffer_size]; /* whoops */

Now, in most cases it would be better to keep the constant in a header,
precisely so that it can be known at compile time:

lib.h:
#define BUFFER_SIZE 17

app.c:
#include "lib.h"
char buf[BUFFER_SIZE];

But I can think of some situations (beyond the scope of the C language
itself, to be sure) where delaying the computation of the constant could
be useful.

(1) The constant is contained in a shared library. You can then change
the constant by updating the shared library and not have to touch the
application that uses it.

(2) To avoid a chicken-and-egg problem, where the value might depend on
the result of the compilation:

$ cat foo.c
#include <stdio.h>
extern const int funcsize;
void func(void) {
printf("This function is %d bytes large!\n", funcsize);
/*
* imagine a lot of complicated stuff here,
* maybe knowing the size of the function will
* help it optimize its cache usage
*/
}
$ cc -c foo.c
$ echo "const int funcsize = `size foo.o |tail -1 |cut -f 1` ;" >funcsize.c
$ cc -c funcsize.c
$ cc -o foo foo.o funcsize.o main.o ...

(3) Since headers and compiled libraries are different files, they can
become out of sync. Here's a way to detect this:

lib.h:
extern const int lib_version;
#define HEADER_VERSION 15
lib.c:
const int lib_version = HEADER_VERSION;
app.c:
#include "lib.h"

if (lib_version != HEADER_VERSION) {
fprintf(stderr, "Version mismatch (header=%d, lib=%d), better recompile\n",
HEADER_VERSION, lib_version);
exit(1);
}

A useful compromise might be a rule that if a const integral variable is
initialized with an integral constant expression, then the variable can
be used as as integral constant expression for the rest of the
translation unit. That would let people do the C++ style

const int buffer_size = 17;
char buf[buffer_size];

while still permitting the uses I mentioned above.
 
I

Ian Collins

Nate said:
Ian Collins said:
const volatile is the exception.

There isn't any good reason why a const integral type can't be a compile
time constant. The only reason I can think of is oversight.

Another issue is that a const int could also be extern. Perhaps its
definition is buried in a library somewhere, so that the value isn't
known until link time.

lib.c:
const int buffer_size = 17;

app.c:
extern const int buffer_size;
char buf[buffer_size]; /* whoops */

Now, in most cases it would be better to keep the constant in a header,
precisely so that it can be known at compile time:
The simple fix there is to make constants unique. C++ does this, so one
can write

const size_t buffer_size = 17;

in a header.
A useful compromise might be a rule that if a const integral variable is
initialized with an integral constant expression, then the variable can
be used as as integral constant expression for the rest of the
translation unit. That would let people do the C++ style

const int buffer_size = 17;
char buf[buffer_size];

while still permitting the uses I mentioned above.

and not breaking existing code.
 
B

Bartc

Now what is the difference between a "const int" and an "inegeral constant
expression". I can't seem to understand it. May I have 2 examples
showing the distinction, so that I can comprehend something out of them.

These points probably already mentioned but to summarise:

"const int x" means something like "readonly int x", except x can be
initialised and can be written to if you try hard enough.

And a compiler usually can't treat it as a compile-time constant, even when
you clearly initialise it, as in for example: const int x = 1200.

To declare a proper named integer constant as in Pascal for example, you
must use:

#define x 1200

or:

enum {x = 1200};

But in both of these cases the scope of x is the entire scope of the file.
And in the enum case, x can only be an integer.

So C doesn't have proper localised named constants. (Which can be a little
frustrating as it would have been a really easy addition to the language:
properconst int asize=sizeof arrc/sizeof arrc[0]; )


You can get what you want, however, by cutting out the middle-man:

char arrc[] = "URI";
char new_arr[sizeof arrc / sizeof arrc[0]];

Defining an intermediate constant is much better, especially with several
same-sized obects to declare, or when the size is also referenced in the
following code.
 
F

Flash Gordon

Bartc wrote, On 09/11/08 11:32:
These points probably already mentioned but to summarise:

"const int x" means something like "readonly int x", except x can be
initialised and can be written to if you try hard enough.

You are "not allowed" to write to a const variable, i.e. attempting to
write to one invokes undefined behaviour and anything can happen.
And a compiler usually can't treat it as a compile-time constant, even when
you clearly initialise it, as in for example: const int x = 1200.

To declare a proper named integer constant as in Pascal for example, you
must use:

#define x 1200

or:

enum {x = 1200};

But in both of these cases the scope of x is the entire scope of the file.
And in the enum case, x can only be an integer.

Wrong.

The enum method follows normal scoping rules and so can be local to a
block etc. The #define method is not file scope, it is from the point of
definition to the end of file of undef.
So C doesn't have proper localised named constants. (Which can be a little
frustrating as it would have been a really easy addition to the language:
properconst int asize=sizeof arrc/sizeof arrc[0]; )

<snip>

Not entirely true since enum follows those rules.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top