Declaration of main()

B

BartC

(And I'd be interested to hear what
cdecl makes of the *entire* function declaration, not just
a snippet of text out of the middle. Context Matters.)

Cdecl now working again. For this input:

'explain int main (int argc, char*(*argv)[]);'

it gives:

'declare main as function (int, pointer to array of pointer to char)
returning int'
 
S

Stefan Ram

Richard Damon said:
Also note that there is also a type difference in passing a pointer to
the array (as your code is declaring) and passing an array as the
automatically decayed value to a pointer to the first element, as is
normally more commonly done.

Yes. Decay semantics is usually used. I can define
a ragged two-dimensional array using decay semantics:

#include <stdio.h>

int const a0[ 4 ]={ 1, 2, 3, 4 };
int const a1[ 4 ]={ 4, 5, 6, 7 };
int const * const a[ 2 ]={ a0, a1 };

int main()
{ printf( "first number: %d\n", a[ 0 ][ 0 ]);
printf( "%zu rows.\n", sizeof a / sizeof 0[ a ]);
printf( "number of columns is unknown.\n" ); }

. However, it would also be possible, albeit less common,
two use proper array-pointer semantics:

#include <stdio.h>

int const a0[ 4 ]={ 1, 2, 3, 4 };
int const a1[ 4 ]={ 4, 5, 6, 7 };
int const( * const a[ 2 ])[ 4 ]={ &a0, &a1 };

int main()
{ printf( "first number: %d\n",( *a[ 0 ])[ 0 ]);
printf( "%zu rows.\n", sizeof a / sizeof 0[ a ]);
printf( "%zu columns.\n", sizeof *a[ 0 ]/ sizeof 0[ *a[ 0 ]]); }

. The common definition of the parameters of the
program-entry function »main« seems to assume decay
semantics.

This shows that when a real array-pointer is used, code has
to be written specifically for this, like for example:
»( *a[ 0 ])[ 0 ]«.

It seems be possible to omit the parentheses in »( *a[ 0 ])[ 0 ]«.
I am not sure why. But one can always write »0[ *a[ 0 ]]«
without parentheses.
 
E

Eric Sosman

[...]
The standard defines that main is called as if something like this was
coded in the library:


int argc;
... compute a value for argc
char* argv[argc];

Nitpick: `char* argv[argc+1];'.
 
B

BartC

Richard Damon said:
On 3/30/14, 6:00 AM, BartC wrote:
What makes it more bizarre, is that the actual argv data almost
certainly exists as an array of char* pointers anyway, otherwise how
would it be possible to step or index the argv value?

Yet the language spec makes it impossible (when using Clang at least) to
actually define it as an array! Which is a method completely acceptable
in any other context.

Remember, that the C language defines that you pass an array as a
pointer to the first element to the array, and it is normal that when
you allocate an array, you save a pointer to the first element of the
array, allowing the use of the name of the pointer to be used in the
place of the array name.

You are trying to make a pointer to the WHOLE array, and then use that
pointer with an immediate dereference, i.e you access and element with
(*ptr)[n] instead of ptr[n], which is an unusual use case in C.

The issue isn't that you can't define it as an array, as one of the
forms of declaring the argv parameter is char *argv[],

Which, in most contexts is an array of pointer to char. Only as a parameter
type (I can't make it work as a return type) does it become a pointer - to
the start of the array. (Although my cdecl server still told me it was an
array of char*, even when a parameter type, which sounds like a mistake in
that program.)

So C just doesn't like pointers to whole arrays, at least, unbounded ones,
which makes me wonder why it allows them at all.
i.e., the
parameter is declared as an array of pointers to chars (and based on the
rules of C, this is passed as a pointer to the the first element of that
array).

This is a 'discontinuity' in the type-system of C, which I first came across
a couple of years ago: char[] means an actual array when defining a
variable, but for a parameter, it's a pointer instead! Similarly with
char*[] and so on.

It would have been better, imo, for it to have been more consistent: eg,
make char[] denote an array in all contexts, but make it an error when used
for parameter types, rather than require one less "*" for certain parameter
types.
The real limitation is that in C, there is no difference in the
declaration of a pointer to a single object, or a pointer to an array of
that type of object.

Yes; I prefer to keep pointers and arrays separate. This is generally
supported, but involves using using pointers to incomplete arrays and the
syntax is a bit of a pain if writing directly in C.
int argc;
... compute a value for argc
char* argv[argc];
... fill in the values for argv

int retval = main(argc, argv);

To match the signature you are wanting, the call would need to have been
written as

int retfao = main(argc, &argv);

(note the added &)

That's a good way of explaining it.
 
K

Keith Thompson

Richard Damon said:
Remember, that the C language defines that you pass an array as a
pointer to the first element to the array, and it is normal that when
you allocate an array, you save a pointer to the first element of the
array, allowing the use of the name of the pointer to be used in the
place of the array name.

You are trying to make a pointer to the WHOLE array, and then use that
pointer with an immediate dereference, i.e you access and element with
(*ptr)[n] instead of ptr[n], which is an unusual use case in C.

The issue isn't that you can't define it as an array, as one of the
forms of declaring the argv parameter is char *argv[], i.e., the
parameter is declared as an array of pointers to chars (and based on the
rules of C, this is passed as a pointer to the the first element of that
array).

It's not just passed as a pointer to the first element of the array. It
*is* a pointer, not an array.

As a parameter declaration, "char *argv[]" actually means "char **argv".

The implicit conversion of an array expression to a pointer (in most
contexts) is a separate language rule.
The real limitation is that in C, there is no difference in the
declaration of a pointer to a single object, or a pointer to an array of
that type of object.

That's incorrect. For example, char* (pointer to char) and char (*)[10]
(pointer to array of 10 char) are distinct and incompatible types.

It's likely that they have the same representation, and that converting
from one to the other will do what you expect, but it's not guaranteed.
And in fact, I believe that you are allowed to
treat a pointer to a single object fully as if it was a pointer to an
array of 1 element, including computing the address of the "one pass the
end of the array"

What the standard says is that a pointer to an object may be treated as
a pointer to *the first element of* an array of 1 element.

[...]
 
K

Keith Thompson

BartC said:
Richard Damon said:
What makes it more bizarre, is that the actual argv data almost
certainly exists as an array of char* pointers anyway, otherwise how
would it be possible to step or index the argv value?

Yet the language spec makes it impossible (when using Clang at least) to
actually define it as an array! Which is a method completely acceptable
in any other context.

Remember, that the C language defines that you pass an array as a
pointer to the first element to the array, and it is normal that when
you allocate an array, you save a pointer to the first element of the
array, allowing the use of the name of the pointer to be used in the
place of the array name.

You are trying to make a pointer to the WHOLE array, and then use that
pointer with an immediate dereference, i.e you access and element with
(*ptr)[n] instead of ptr[n], which is an unusual use case in C.

The issue isn't that you can't define it as an array, as one of the
forms of declaring the argv parameter is char *argv[],

Which, in most contexts is an array of pointer to char. Only as a parameter
type (I can't make it work as a return type) does it become a pointer - to
the start of the array. (Although my cdecl server still told me it was an
array of char*, even when a parameter type, which sounds like a mistake in
that program.)

Right, there's a specific language rule about that.

N1570 6.7.6.3p7:

A declaration of a parameter as "array of type" shall be adjusted
to "qualified pointer to type", where the type qualifiers
(if any) are those specified within the [ and ] of the array
type derivation.
So C just doesn't like pointers to whole arrays, at least, unbounded ones,
which makes me wonder why it allows them at all.

C has no problem with pointers to whole arrays. It's just usually more
convenient to access an array via a pointer to the element type.

[...]

Why can't you just define main() using one of the two standard forms?
 
B

BartC

Keith Thompson said:
C has no problem with pointers to whole arrays. It's just usually more
convenient to access an array via a pointer to the element type.

When I tried g++ (which I assume is a C++ compiler), it doesn't seem to
allow such pointers to unbounded arrays at all. I was actually thinking of
just using void* for /any/ pointer parameter, and just sticking the right
cast in as needed.

Then most such problems should disappear (except for main()).
Why can't you just define main() using one of the two standard forms?

The code for main() is created by a source-to-source translator which does
not treat "main" any differently.

I am now using the equivalent of "char **" in the source; there are still
some issues, but at least the result compiles.

(However, I am trying to compile my programs with six different C compilers,
and there are plenty more annoying and often inexplicable problems to
overcome.)
 
K

Keith Thompson

BartC said:
When I tried g++ (which I assume is a C++ compiler), it doesn't seem to
allow such pointers to unbounded arrays at all. I was actually thinking of
just using void* for /any/ pointer parameter, and just sticking the right
cast in as needed.

Pointers to *incomplete* arrays (defined with empty []) might be a
problem in some cases; I've rarely used them myself. But this:

#include <stdio.h>
int main(void) {
char arr[] = "hello";
char (*p)[] = &arr;
printf("*p = %s\n", *p);
}

compiles and executes with no problem using either gcc or clang.

It doesn't compile with g++. But g++ is a compiler for *a different
language*. If you have questions about pointers to incomplete array
types in C++, I suggest posting in comp.lang.c++.
Then most such problems should disappear (except for main()).


The code for main() is created by a source-to-source translator which does
not treat "main" any differently.

Well there's your problem. The C language restricts the manner in which
"main" can be defined. You can't just translate an arbitrary "main"
function in some other language into C and expect a C compiler to accept
it, unless the C "main" follows C's rules.

You can't just generate C. You need to generate *valid* C.
I am now using the equivalent of "char **" in the source; there are still
some issues, but at least the result compiles.

(However, I am trying to compile my programs with six different C compilers,
and there are plenty more annoying and often inexplicable problems to
overcome.)

So far, it seems that you're having problems with the fact that C
compilers enforce the requirements of the C language. In some cases
(like permitted definitions for "main"), you're seeing different
behaviors with different compilers because the C standard permits
variations.

Feel free to ask about any specific problems you're having (but first I
suggest reading the standard; you might be able to solve some of them
yourself).
 
S

Seungbeom Kim

So C just doesn't like pointers to whole arrays, at least, unbounded ones,
which makes me wonder why it allows them at all.

There are certain things you can do with a pointer to unbounded arrays;
for example, you can have 'f(int (*a)[], size_t n)' that loops from
(*a)[0] through (*a)[n-1]. (A much more natural thing would be
'f(int a[], size_t n)' that loops from a[0] through a[n-1], of course.)

However, you cannot do a+1, a[1], a++ or sizeof(a).

So there seems to be little, if any, that you gain from having
'int (*a)[]' instead of 'int a[]', and that maybe explains why you see
pointers to unbounded arrays so rarely.
This is a 'discontinuity' in the type-system of C, which I first came across
a couple of years ago: char[] means an actual array when defining a
variable, but for a parameter, it's a pointer instead! Similarly with
char*[] and so on.

It would have been better, imo, for it to have been more consistent: eg,
make char[] denote an array in all contexts, but make it an error when used
for parameter types, rather than require one less "*" for certain parameter types.

Another (unfortunate) bit of history; you can find the meaning of [N] and
[] in NB (for "new B") at <http://cm.bell-labs.com/who/dmr/chist.html>.
 
M

Malcolm McLean

Yes; I prefer to keep pointers and arrays separate. This is generally
supported, but involves using using pointers to incomplete arrays and the
syntax is a bit of a pain if writing directly in C.
The heart of the language is that you can declare a buffer with the
square brackets, pass it to a subroutine either as a whole or in chunks
using pointers, then access it as a random-access array in that subroutine
using the square brackets again.
malloc() can conceptually, and in small systems is often actually, built on
top of that, with a big buffer somewhere you allocate out of. Dynamic memory
is of course crucial to allow functions to scale to arbitrary-sized input.

That's basically C. Declare buffers, take pointers to them, and shuffle
the contents around in memory.
 
T

Tim Rentsch

Richard Damon said:
On 3/29/14, 10:37 PM, Kaz Kylheku wrote:
[...]
The question is, is argv in main() *only* defined as pointer to
pointer? Clang obviously thinks so.

Yes. Clang is quite correct.

If you define main with a second parameter that's of a type
incompatible with char**, how are you going to persuade the
runtime environment to invoke it with data of the type it
expects?

You don't have to; it will work fine. Unless the machine is
completely bizarre, a pointer to an array has a representation
which is interchangeable with a pointer to the first element of
an array of the same type.

A formal parameter of type "pointer to incomplete array of T"
should have no trouble accepting a "pointer to T" actual
argument.

If you have a machine where a T ** and a T *(*[]) have a
different, incompatible representation and are passed
differently as function parameters, I'd like to hear about it.

Type mismatches can also confound due to invalid aliasing, but
in practice that isn't a problem across module boundaries in
this type of situation.

And anyway, there is no aliasing issue between an array and an
array element. Arrays cannot be assigned as a unit in C, and
if they could be, then any modification to an object of type T
as an array element involved in an array-level operation would
have o be suspected as modifying some T that is pointed-at by a
T * pointer and vice versa.

So, basically this only fails because it's "artificially"
rejected by a type check, which has nothing to do with the
run-time environment not being able to handle it if the check
is removed and code is generated anyway.


The one case I can think of is a system using run time bounds
checking through fat pointers (probably just for debugging),
where the pointer not only points to the address but also
include (or points to) information about the bounds of the
memory pointed to.

Now suppose that the bounds information for different pointer
types is binary compatible, and normalized to bytes. The argv
pointer, generated as a char ** would carry a bounds field
denoting the extent, in bytes, of the argv vector (including null
terminating entry). If this pointer is reinterpreted as a
pointer to an incomplete array, then the bounds info nicely
becomes reinterpreted as the actual size of that array.

A bound schecking system based on units of element size rather
than bytes would have a problem dealing with pointers to
incomplete types, where the element size is not known.

At run time, a type can not be truly incomplete. [snip]

In C, except for variable length arrays, types are purely a
static compile-time property of various syntatic units, notably
expressions and some declared identifiers. Any type that is
incomplete at compile time is equally incomplete at run time.

The C standard does define a notion called effective type, which
is a run-time property. However, effective type is not the same
as type, and furthermore it is rarely the case that there is an
effective type that is an array type. Memory that has been
malloc()'ed, for example, and accessed as an array typically
never acquires an effective type of a whole, but only at the
level of individual elements.
 

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,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top