Accessing array elements within a structure

B

bernd

Hi folks,

a simple question for the experts, I guess.

Obviously I am doing something wrong when trying to access an element
of an array declared within a structure:

#include <stdio.h>
#include <stddef.h>

main() {

const int SIZE = 2 ;

struct test {

long type ;
size_t files[SIZE] ;

} ;

struct test example ;

example.type = 100 ;
example.files[0] = 2 ;

printf("type %d - file size %lld:\n", example.type,
example.files[0] ) ;

}

The "intended" output is

type 100 - file size 2:

but I get:

type 100 - file size 12871933952:

(guess I am printing a memory address or something).

What am I doing wrong?

Cheers


Bernd
 
H

Harald van Dijk

bernd said:
Hi folks,

a simple question for the experts, I guess.

Obviously I am doing something wrong when trying to access an element
of an array declared within a structure:

#include <stdio.h>
#include <stddef.h>

main() {

const int SIZE = 2 ;

struct test {

long type ;
size_t files[SIZE] ;

} ;

struct test example ;

example.type = 100 ;
example.files[0] = 2 ;

printf("type %d - file size %lld:\n", example.type,
example.files[0] ) ;

}
[...]

Since you appear to be using a C99 implementation, [...]

I'm curious: what makes you think this is a C99 implementation? The
implicit int is specific to C90. The use of a const-qualified object as a
constant expression is specific to C++. The %lld format specifier is
specific to C99, but there's no indication that it's actually supported by
the implementation.
the "%zd" specifier ought to work.

In C99, %zd can be used for the signed type corresponding to size_t, but
there's no standard way to find out what this type is. %zu can be used in
C99 for size_t.

I agree with your and Mike Wahler's suggestions to use a cast, but I
wouldn't recommend either %zd or %zu as an alternative solution without
some more indication that it will actually work.
 
K

Keith Thompson

bernd said:
a simple question for the experts, I guess.

Obviously I am doing something wrong when trying to access an element
of an array declared within a structure:

#include <stdio.h>
#include <stddef.h>

main() {

const int SIZE = 2 ;

struct test {

long type ;
size_t files[SIZE] ;

} ;

struct test example ;

example.type = 100 ;
example.files[0] = 2 ;

printf("type %d - file size %lld:\n", example.type,
example.files[0] ) ;

}

The "%d" format requires an argument of type int; you're giving it an
argument of type long (example.type). It happens to "work" for you,
probably because your implementation makes int and long the same size,
but even that doesn't guarantee anything, and it will break on other
implementations.

The "%lld" format requires an argument of type long long; you're
giving it an argument of type size_t (example.files[0]).

It's not sufficient for a format to specify a type that can hold the
value of the corresponding argument, or one that's at least as big as
the argument type, or even that's the same size and signedness. You
have to match the types exactly. (There are some minor exceptions to
this, but they're best ignored; it's easier to match the type exactly
all the time than to remember the circumstances where you can fudge
things a bit.)

In C90, there's no format for size_t. C99 adds "%zu", but you might
not have a C99 implementation (if you did, it would have been required
to issue a diagnostic for your use of implicit int.)

The compiler didn't complain about the mismatch because it's not
required to. The format string is something that exists at run time;
it could have been a variable rather than a string literal. Some
compilers will do some format string checking for you, but in general
you just need to get it right yourself.

Here's how to print those values:

printf("type %ld - file size %lu:\n",
example.type,
(unsigned long)example.files[0]);

That's for C90, and it also works in C99 (as long as the value of
example.files[0] doesn't happen to exceed ULLONG_MAX). Note that the
cast is necessary to ensure that the types match exactly (this is one
of the rare cases where a cast is necesssary and appropriate).

If you're sure your code won't be used with a pre-C99 implementation,
you can do this instead:

printf("type %ld - file size %zu:\n",
example.type,
example.files[0]);

But if you happen to use the above with an implementation that doesn't
support "%zu", it's not required to tell you that it doesn't recognize
the format.

There are some other things you should fix that aren't relevant to
what you were asking about.

"main()" should be "int main(void)". The form you use can work under
some implementations, but "int main(void)" will *always* work
(assuming a hosted implementation rather than a freestanding
implementation).

SIZE is not actually a constant; it's merely a read-only object. This
means that if you use it as an array length, then it's a VLA
(variable-length array). VLAs are not allowed at all in C90, and are
not allowed as structure members even in C99. If the above compiled
without diagnostics, then either you're using a compiler that provides
some sort of extension (and you're invoking it in non-conforming
mode), or you're using a C++ compiler (<OT>in C++ SIZE *is* a
constant</OT>). You can replace your
const int SIZE = 2 ;
with
#define SIZE 2

You should add "return 0;" before the closing '}'. There are
circumstances where it's not necessary, but this is another case where
it's easier to use something that's universally valid than to keep
track of the cases where you can get away with something else. main
returns an int, so you should return an int; 0 is a safe value.

Finally, a style point. I personally dislike white space before a
semicolon. The compiler doesn't care, of course, and some programmers
will disagree with me, but I think most people write semicolons with
no preceding blank.

Here's your program as I would have written it (I've taken the liberty
of changing the output format a bit):

#include <stdio.h>
#include <stddef.h>

int main(void)
{

#define SIZE 2

struct test {
long type;
size_t files[SIZE];
};

struct test example;

example.type = 100;
example.files[0] = 2;

printf("example.type = %ld, example.files[0] = %lu\n",
example.type, (unsigned long)example.files[0]);

return 0;
}
 
K

Keith Thompson

Eric Sosman said:
Harald said:
bernd wrote: [...]
const int SIZE = 2 ;

struct test {

long type ;
size_t files[SIZE] ;

} ; [...]
Since you appear to be using a C99 implementation, [...]
I'm curious: what makes you think this is a C99 implementation? The
implicit int is specific to C90. The use of a const-qualified object
as a constant expression is specific to C++. The %lld format
specifier is specific to C99, but there's no indication that it's
actually supported by the implementation.

The variable-length array was the clue I fastened on.
[...]

C99 doesn't allow VLAs as struct members. C99 6.7.2.1p8:

A member of a structure or union may have any object type other
than a variably modified type.

I note that the original program compiles without diagnostics using
gcc with no options (i.e., in a mode that doesn't conform to any C
standard).
 
R

Richard Tobin

Keith Thompson said:
I note that the original program compiles without diagnostics using
gcc with no options

And does so even if SIZE is not declared const, and even allows this:

scanf("%d", &SIZE);

struct test2 {
long type ;
size_t files[SIZE] ;
} ;

producing a struct whose size depends on the value of SIZE when the
declaration is encountered.

-- Richard
 
R

rahul

Keith Thompson said:
I note that the original program compiles without diagnostics using
gcc with no options

And does so even if SIZE is not declared const, and even allows this:

scanf("%d", &SIZE);

struct test2 {
long type ;
size_t files[SIZE] ;
} ;

producing a struct whose size depends on the value of SIZE when the
declaration is encountered.

-- Richard

gcc has supported variable length arrays for a long time. Actually
many notable systems(linux kernel) has been written in GNU C. Some of
them has made it to C99(variable length arrays, inline functions).
<off-topic>
Why is it that the structure could not contain variable length arrays?
</off-topic>
 
B

Ben Bacarisse

rahul said:
Keith Thompson said:
I note that the original program compiles without diagnostics using
gcc with no options

And does so even if SIZE is not declared const, and even allows this:

scanf("%d", &SIZE);

struct test2 {
long type ;
size_t files[SIZE] ;
} ;

producing a struct whose size depends on the value of SIZE when the
declaration is encountered.
<snip sig>

Best not to quote sig blocks.
gcc has supported variable length arrays for a long time. Actually
many notable systems(linux kernel) has been written in GNU C. Some of
them has made it to C99(variable length arrays, inline functions).

But just in case you or anyone else is confused, *this* kind of
variable length array did *not* get into C99. The form sanctioned by
the standard is to allow the last member of a struct to be an array
with no size size:

struct test {
long type;
size_t files[];
};

-- a formalising of the old "struct hack". Of course, there are other
VLAs in C99 (as automatic storage and as function parameters) which
work differently.
 
H

Harald van Dijk

Not if you tell gcc to compile C, rather than the non-standard gnu-C.

Er... if you tell gcc to compile C, extensions to C90 available in GNU C
will disappear from the C99 standard? Your message does not make sense as
a reply to the text you have quoted. On re-reading, I assume you meant it
as a reply to the first sentence only, but then it's simply wrong. gcc
supports variable-length arrays in C90-conforming mode as well.
 
H

Harald van Dijk

You snipped a portion of my reply, re-inserted above.

Yes, I snipped a portion of your reply that I didn't respond to and that
wasn't required to follow my message.
Note the '-ansi'
component in the command string. This selects C90/C95 mode for gcc, not
c99 mode.

Yes, I am aware of that. Notice how I wrote gcc supports variable-length
arrays in C90-conforming mode, and didn't write anything about C99-
conforming mode? You are wrong about C90-conforming mode. gcc, with the
command-line options you gave, supports variable-length arrays.
C99 mode requires replacing the '-ansi' with '-std=C99'.

-std=c99, not C99.
Please don't snip within paragraphs.

I damn well will when it makes the message easier to follow. You decided
not to snip in the paragraph from rahul's message, and that made your
message hard to read.
 
A

Antoninus Twink

I damn well will when it makes the message easier to follow.

*blink*

I know CBF would try the patience of a saint, but to provoke Harold
(who usually comes across as a bit like a compiler in human form) to a
display of annoyance is quite an achievement.

We'll know CBF has really made it if he manages to get under the skin of
Chris Torek...
 
D

David Thompson

Here's how to print those values:

printf("type %ld - file size %lu:\n",
example.type,
(unsigned long)example.files[0]);

That's for C90, and it also works in C99 (as long as the value of
example.files[0] doesn't happen to exceed ULLONG_MAX). Note that the

(IQC)YM ULONG_MAX.
cast is necessary to ensure that the types match exactly (this is one
of the rare cases where a cast is necesssary and appropriate).
<snip rest>
- formerly david.thompson1 || achar(64) || worldnet.att.net
 

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,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top