Some Questions Asked in Interview

C

Chris Torek

You're of course right in stating that expressions used as operands
of sizeof are not examined for their value ("evaluated"),

[except in some cases in C99...]
but their type - this therefore one of the notable exceptions to
Chris Torek's The Rule" [1] (when being operand of the address
operator is the other exception).

Actually, this is why the first part is "In a value context". When
x appears after "&" or "sizeof" (i.e., &x or sizeof x), this is
*not* a "value context".

There are additional "not value context"s:

++x; /* x is required to name an object */
x = 9; /* x is required to name an object */

and in these cases, if "x" is an array, the code is invalid.
 
J

James Kanze

|> James Kanze wrote:

|> > |> James Kanze wrote:
|> > |> > |> Mabden wrote:
|> > |> > |> >>Q1 . What is the use of pointer to an array?
|> > |> > |> > TO step through the array, or pass it by reference to
|> > |> > |> > another function.
|> > |> > |> You can't step through an array using a pointer to it.
|> > |> > |> If you increment a pointer to an array by 1 you will
|> > |> > |> point one past the end of the object (the array in this
|> > |> > |> case) this is defined, but you cannot dereference this
|> > |> > |> pointer as that leads to undefined behaviour. A pointer
|> > |> > |> to an array is rarely useful, I have never had the need
|> > |> > |> for one.
|> > |> > Nonsense. If a is a two dimensional array, the expression
|> > |> > a returns a pointer to an array.
|> > |> Nonsense. C does not have multi dimensional arrays. Only
|> > |> arrays of arrays.

|> > OK. Formally, what C uses for multi dimensional arrays is in fact
|> > arrays of arrays. Formally, what C uses for indexing is pointer
|> > arithmetic.

|> > |> The expression of an array yields (not returns) the address of
|> > |> its first element.

|> > No. An expression of array type yields an array. Which will
|> > convert implicitly to a pointer in certain contexts only. And the
|> > conversion obviously doesn't continue, because what you get after
|> > the decay is a pointer, not an array.

|> This seems argumentive

No, just a simple statement of facts, drawn from the C standard.

|> but, the argument to sizeof is either an object or a type.

Since when? I use things like "sizeof( f() )" frequently. (Mostly in C++
template resolution, but it is perfectly legal in C as well.) Or what
about the frequent macro:
#define lenght( a ) (sizeof( a ) / sizeof( a[ 0 ] ))
to determine the number of elements in an array?

Roughly speaking, an expression has a type, and evaluates a value. When
used as the operand of sizeof, only the type is relevant.

And given something like:
int a[ 5 ][ 10 ] ;
sizeof( a[ 0 ] )
the expresion a[ 0 ] in the second line has a type int[10] (and sizeof
yields sizeof(int)*10).

This is C 101. I'm surprised that anyone who uses C isn't aware of
this.

|> The compiler yields the size of it in bytes. The argument to sizeof
|> is never evaluated and is not an expression.

The argument of sizeof is not evaluated. It is an expression.

|> > And of course, if what you have is an array of arrays, what the
|> > decay gives you is a pointer to an array.

|> > |> It's all about decadence. :)
|> > |> int i = 0;
|> > |> char a[5][10];
|> > |> This defines (creates) exactly 50 bytes of memory. An array
|> > |> [5] of array [10] of char. Expressing a yields 'pointer to
|> > |> array [10] of char'.

|> > You mean, like in &a, or sizeof( a )?

|> You're missing something. &a is an address by definition.

But an address of what? What is the type of the pointer?

|> sizeof &a is 4 at my house. sizeof a yields 50.

|> > Of course, in contexts where the decay occurs... Well, that's
|> > exactly my point. You have a pointer to an array.

|> > |> Expressing a yields 'pointer to char'.

|> > No, it yields an array [10] of char, which isn't the same thing.
|> > Try sizeof( a ), for example.

|> You're much too hung up on sizeof.

Simply because that is the most common case where decay doesn't occur.
I could just have easily used &a in my example, but people don't
offen take the address of arrays. Taking the address of an array could
be considered a bit of C exotica, where as sizeof... Anyone who doesn't
know about sizeof doesn't know C.

|> Arguments to sizeof are not expressions.

Since when?

|> sizeof a is 10. Interesting but unimportant.

Except that the argument of sizeof in this expression is an expression,
which has array type.

|> char *cp = a;

|> is correct.

Because decay (implicit conversion) occurs in this context.

|> char (*ap)[10] = &a;

|> is also correct.

Because there is no decay (implicit conversion) when an expression of
array type is used as the argument of the & operator.

|> > |> It would be &a to get 'pointer to array [10] of char'.

|> > How about a + i?

|> Looks like &a to me. What do you think?

In C90, the two are different. There's a special exception in C99 which
changes the meaning of [] if it is the operand of &.
 
J

James Kanze

|> In article <|> >You're of course right in stating that expressions used as operands
|> >of sizeof are not examined for their value ("evaluated"),

|> [except in some cases in C99...]

|> >but their type - this therefore one of the notable exceptions to
|> >Chris Torek's The Rule" [1] (when being operand of the address
|> >operator is the other exception).

|> Actually, this is why the first part is "In a value context". When x
|> appears after "&" or "sizeof" (i.e., &x or sizeof x), this is *not*
|> a "value context".

The C++ standard speaks of an lvalue to rvalue conversion in this case.
Taken in isolation, I sort of like the formulation. On the other hand,
lvalue-ness is only partially (ambiguously) introduced into the type
system; I don't like the partialness, and I'm not convinced that things
would work well if it were completely integrated into the type system
(although I suspect that most of the problems wouldn't affect C).

|> There are additional "not value context"s:

|> ++x; /* x is required to name an object */
|> x = 9; /* x is required to name an object */

|> and in these cases, if "x" is an array, the code is invalid.

More generally, a "not value context" is one that requires an lvalue?
 
A

Arthur J. O'Dwyer

|> The compiler yields the size of it in bytes. The argument to sizeof
|> is never evaluated and is not an expression.

The argument of sizeof is not evaluated. It is an expression.

...or a type. The 'char' in 'sizeof (char)' is not an expression.
Careful not to contradict the wrong answer so strongly that your
own answer is wrong. :)

|> > How about a + i?

|> Looks like &a to me. What do you think?

In C90, the two are different. There's a special exception in C99 which
changes the meaning of [] if it is the operand of &.


Could you elaborate on the difference in C90?

-Arthur
 
C

Chris Torek

Chris Torek said:
|> There are additional "not value context"s:
|> ++x; /* x is required to name an object */
|> x = 9; /* x is required to name an object */
|> and in these cases, if "x" is an array, the code is invalid.

More generally, a "not value context" is one that requires an lvalue?

Almost. The sizeof operator confuses things, because sizeof can
take a type-name, an lvalue, or an rvalue:

long var;
size_t x;

x = sizeof(long); /* valid */
x = sizeof var; /* valid and same as sizeof(long) */
x = sizeof 3L; /* valid and same as sizeof(long) */

In C99, "sizeof" has even more special oddness when applied to VLAs.
 
J

James Kanze

|> On Thu, 20 May 2004, James Kanze wrote:


|> > |> The compiler yields the size of it in bytes. The argument to
|> > |> sizeof is never evaluated and is not an expression.

|> > The argument of sizeof is not evaluated. It is an expression.

|> ...or a type. The 'char' in 'sizeof (char)' is not an expression.
|> Careful not to contradict the wrong answer so strongly that your own
|> answer is wrong. :)

Yes. I was, obviously, thinking about the case where it wasn't a type.

|> > |> > How about a + i?

|> > |> Looks like &a to me. What do you think?

|> > In C90, the two are different. There's a special exception in C99
|> > which changes the meaning of [] if it is the operand of &.

|> Could you elaborate on the difference in C90?

#define i 10
int a[ i ] ;
&a[ i ] /* Undefined behavior in C90, equivalent of a + i in C99
 
A

anonymous

Static variables are declared in neither heap nor stack, they are declared
in C source code files. And they are *allocated* whereever the
implementor chooses to, typically neither in heap (the malloc and friends
arena) nor in stack (automatic allocation arena and function call related
data).

But is it not required to allocate static variables in heap, so
that their values are intact even after a function is called more than once?
Then generally where are they allocated?.
 
L

Lew Pitcher

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

anonymous wrote:
|>Static variables are declared in neither heap nor stack, they are declared
|>in C source code files. And they are *allocated* whereever the
|>implementor chooses to, typically neither in heap (the malloc and friends
|>arena) nor in stack (automatic allocation arena and function call related
|>data).
|
|
| But is it not required to allocate static variables in heap,

Nope. The C language doesn't even recognize the concept of 'heap', let alone
require static variables to be allocated in it. However, a specific C compiler
/might/ implement the language such that static variables are allocated in the
'heap', if such a thing exists in the implementation platform.

For that matter, it is perfectly concievable (although hardly likely) that a C
compiler will allocate static variables in a 'file', or even in a 'database'.
There are no restrictions on the mechanism that the compiler uses to allocate
the variables, so long as the allocation and access mechanisms do not conflict
with the requirements of the C language.

| so
| that their values are intact even after a function is called more than once?

Static variables are guaranteed (by the C language) to retain their values
across function calls. How the compiler manages that is entirely up to the
compiler.

| Then generally where are they allocated?.

OK, lets step away from the theory for a second. There's only so many times we
can say that the /mechanism/ isn't something that we know about or care about,
so long as the C language /policy/ is adhered to.

In practice, compilers don't use /either/ 'heap' or 'stack' to store static
variables. Both 'heap' and 'stack' are typically /dynamic/ allocation
mechanisms, and since we're talking about static data, these mechanisms are
unwieldly to use in that fashion.

C compilers (at least of the Unix kind) typically store static variables in
specific, pre-allocated areas of the BSS or DATA segments of the program's
memory map. They typically allocate the program's data area (in the resulting
'load module') such that it looks like...

~ | constants & initialized statics | uninitialized statics | heap | stack |
~ +=================================+-----------------------+......'.......+
~ | populated at pgm compile time | populated at program execution time |
~ \ /
~ bottom of memory top of memory


The constants and initialized statics consist of those data elements that
a) are of global scope and have been given initializers in the source code, or
b) are of local scope, defined as "static", and have been given initializers
~ in the source code, or
c) are string constants, or other truely static data

The uninitialized statics consist of those data elements that
a) are of global scope which have not been given initializers in the source
~ code, or
b) are of local scope, defined as "static", but have not been given
~ initializers in the source code,

The 'heap' and 'stack' are just whatever's left over after all the statics are
loaded into memory. It's just one big scratchpad area that gets managed by the
C runtime. Function calls to malloc() typically reserve blocks of memory
starting at the 'heap' end of this scratchpad area and build towards the
'stack' end. The C runtime allocates automatic variables starting at the top
of the 'stack' end, working down toward the 'heap' end.

So, you see, in theory, there is no 'heap'. In practice, statics aren't
allocated in the 'heap', if there is one.

Does this make it any clearer?

- --
Lew Pitcher

Master Codewright & JOAT-in-training | GPG public key available on request
Registered Linux User #112576 (http://counter.li.org/)
Slackware - Because I know what I'm doing.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFAsLZ+agVFX4UWr64RAvoxAJ4zzpq0X+5dQeoUqnZ64b4iuom7WACfYj0o
hiR8Knqi9tA+ZFBUeUwh0Ys=
=BjYz
-----END PGP SIGNATURE-----
 
V

Viktor Lofgren

Mabden said:
TO step through the array, or pass it by reference to another function.
On the other hand, that's whats indexing is for.
A dinosaur. DOS based device-driver accessor: ioctl (handle, cmd, &dx, &cx);
Time to upgrade.

<OT in comp.lang.c>
It is actually what's used POSIX enviorments for IO controlling. So
it's not that outdated.
</OT in comp.lang.c>
 
A

anonymous

So, you see, in theory, there is no 'heap'. In practice, statics aren't
allocated in the 'heap', if there is one.

Does this make it any clearer?

Thank you for the detail explanation - I was always under the impression that
if something is stored it is either in the stack/heap or registers.
 
D

Dan Pop

In said:
But is it not required to allocate static variables in heap, so
that their values are intact even after a function is called more than once?

Then, why the hell would you want to allocate them in a *dynamic* data
structure?
Then generally where are they allocated?.

In a static data structure, usually called the program's data segment.
Depending on how they are initialised, they can be allocated in its
initialised part (that is "mirrored" in the executable file) or in its
"uninitialised" part (that is zero filled by the OS) that occupies no
space in the executable file (only its size and relative address are
stored).

Things are slightly different in the case of freestanding implementations,
where, instead of executable file, the initialised data might be stored
in some kind of non-volatile, read-only memory, before being copied to the
actual read/write memory where it was actually allocated, as part of
the program startup procedure, before main() is actually executed.

Anyway, the program doesn't care where its static data is allocated,
and this is the only thing that matters in the context of this newsgroup.

Dan
 

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

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top