Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.c > #20027 > unrolled thread

A short-but-bizarre program that shouldn't work but does.

Started byRobbie Hatley <see.my.signature@for.my.contact.info>
First post2012-04-28 14:59 -0700
Last post2012-04-29 12:26 +0200
Articles 12 — 7 participants

Back to article view | Back to comp.lang.c


Contents

  A short-but-bizarre program that shouldn't work but does. Robbie Hatley <see.my.signature@for.my.contact.info> - 2012-04-28 14:59 -0700
    Re: A short-but-bizarre program that shouldn't work but does. Kaz Kylheku <kaz@kylheku.com> - 2012-04-28 22:58 +0000
    Re: A short-but-bizarre program that shouldn't work but does. pete <pfiland@mindspring.com> - 2012-04-28 19:40 -0400
      Re: A short-but-bizarre program that shouldn't work but does. Robbie Hatley <see.my.signature@for.my.contact.info> - 2012-04-28 22:15 -0700
        Re: A short-but-bizarre program that shouldn't work but does. Philip Lantz <prl@canterey.us> - 2012-04-29 01:13 -0700
        Re: A short-but-bizarre program that shouldn't work but does. pete <pfiland@mindspring.com> - 2012-04-29 10:02 -0400
    Re: A short-but-bizarre program that shouldn't work but does. James Kuyper <jameskuyper@verizon.net> - 2012-04-28 22:15 -0400
    Re: A short-but-bizarre program that shouldn't work but does. Keith Thompson <kst-u@mib.org> - 2012-04-28 20:18 -0700
    Re: A short-but-bizarre program that shouldn't work but does. "Heinrich Wolf" <invalid@invalid.invalid> - 2012-04-29 11:20 +0200
      Re: A short-but-bizarre program that shouldn't work but does. "Heinrich Wolf" <invalid@invalid.invalid> - 2012-04-29 11:31 +0200
      Re: A short-but-bizarre program that shouldn't work but does. "Heinrich Wolf" <invalid@invalid.invalid> - 2012-04-29 11:38 +0200
      Re: A short-but-bizarre program that shouldn't work but does. "Heinrich Wolf" <invalid@invalid.invalid> - 2012-04-29 12:26 +0200

#20027 — A short-but-bizarre program that shouldn't work but does.

FromRobbie Hatley <see.my.signature@for.my.contact.info>
Date2012-04-28 14:59 -0700
SubjectA short-but-bizarre program that shouldn't work but does.
Message-ID<jN-dnRcXh_z89QHSRVn_vwA@giganews.com>
Greetings, group.  I was just looking through my source code
folders and found the following program, author unknown
(did I write this myself years ago and forget? dunno).
According to the C Standard, as I interpret it, this should
not work... and yet, it does:

#include <stdio.h>
int main(int argc, char **argv, char **envp)
{
    char **envstr = envp;
    while(*envstr)
    {
       puts(*envstr);
       ++envstr;
    }
    return 0;
}

My expectation: either won't compile, or will crash if ran.

Actuality: gcc compiles this without error.  It does give
warnings about the unused "argv" and "argc", but it does NOT
give any error or warning about the illegal third parameter
"envp".  On execution, the program does not crash, but prints
a list of all environmental variables and their values.

How can this even compile?
C99 section 5.1.2.2.1(2) clearly states:
"The function called at program startup is named main.
The implementation declares no prototype for this function.
It shall be defined with a return type of int and with no
parameters:
int main(void) { /* ... */ }
or with two parameters (referred to here as argc and argv,
though any names may be used, as they are local to the function
in which they are declared):
int main(int argc, char *argv[]) { /* ... */ }
or equivalent; or in some other implementation-defined manner."

So, how can main() have *three* parameters if the standard
demands it have either zero or two?  Why does the above
program even compile?  Is this some kind of non-standard
extension or something?

-- 
Confused,
Robbie Hatley
Santa Ana, CA, USA
lonewolf (at) well (dot) com

[toc] | [next] | [standalone]


#20028

FromKaz Kylheku <kaz@kylheku.com>
Date2012-04-28 22:58 +0000
Message-ID<20120428152521.3@kylheku.com>
In reply to#20027
On 2012-04-28, Robbie Hatley <see.my.signature@for.my.contact.info> wrote:
> Actuality: gcc compiles this without error.  It does give

GCC also compiles this without error:

   int x = ({ puts("foo"); 42 });

if you don't give it any options to control dialect.

There is no restriction on what may compile. Firstly, compilers often
do not accept the one of the ISO C dialects but their own, unless they are
asked to accept one of the ISO C dialects (and then it's still a question of
which one).

Then, ISO C only requires programs which break syntax rules and semantic
constraints to be diagnosed. But to be diagnosed does not mean to be
rejected.

For instance GCC compiles this also, even in some ISO C mode, like -ansi
or -std=c99.

   double x = 3.0;
   int *p = &x;

There is a diagnostic, but it is treated as a warning and the code
is translated anyway.

Of course, the behavior is then undefined. As far as ISO C is concerned,
if the translation unit requires a diagnostic, it is incorrect. If the
implementation chooses to run it, that is in the domain of behavior that
is not deined by ISO C.

Note there is no diagnostic required if a program uses a nonstandard form of
the main startup function. That is just undefined behavior without
the requirement for a diagnostic.

> C99 section 5.1.2.2.1(2) clearly states:
> "The function called at program startup is named main.
> The implementation declares no prototype for this function.

See, since there is no prototype, no rule is being broken that requires
a diagnostic (no conflict between declaration and definition). The behavior is
simply undefined.

That just means that ISO C gives no opinion on what the behavior should be,
nor any recommended handling of the situation. Not that it is wrong.

Your environment does have an opinion: it supports the additional parameter for
passing the environment pointer.

> Is this some kind of non-standard extension or something?

Yes, this extra argument to main is a traditional extension found in Unix
environments which evidently has not been standardized.

It is not documented in POSIX 1003/Single Unix Spec.

What /is/ documented is the "extern char *environ[]" array; so it is better
to rely on that (if you're targetting POSIX and you need to enumerate
the environment variables rather than just perform lookup).

Reference: http://pubs.opengroup.org/onlinepubs/9699919799/functions/environ.html

Further down in the page it talks about main. Nothing is mentioned about
any third parameter, and I cannot find anything else in the document that
gives requirements about main.

-- 
If you ever need any coding done, I'm your goto man!

[toc] | [prev] | [next] | [standalone]


#20029

Frompete <pfiland@mindspring.com>
Date2012-04-28 19:40 -0400
Message-ID<4F9C7FE7.1103@mindspring.com>
In reply to#20027
Robbie Hatley wrote:
> 
> Greetings, group.  I was just looking through my source code
> folders and found the following program, author unknown
> (did I write this myself years ago and forget? dunno).
> According to the C Standard, as I interpret it, this should
> not work... and yet, it does:
> 
> #include <stdio.h>
> int main(int argc, char **argv, char **envp)
> {
>     char **envstr = envp;
>     while(*envstr)
>     {
>        puts(*envstr);
>        ++envstr;
>     }
>     return 0;
> }
> 
> My expectation: either won't compile, or will crash if ran.
> 
> Actuality: gcc compiles this without error.  It does give
> warnings about the unused "argv" and "argc", but it does NOT
> give any error or warning about the illegal third parameter
> "envp".  On execution, the program does not crash, but prints
> a list of all environmental variables and their values.
> 
> How can this even compile?
> C99 section 5.1.2.2.1(2) clearly states:
> "The function called at program startup is named main.
> The implementation declares no prototype for this function.
> It shall be defined with a return type of int and with no
> parameters:
> int main(void) { /* ... */ }
> or with two parameters (referred to here as argc and argv,
> though any names may be used, as they are local to the function
> in which they are declared):
> int main(int argc, char *argv[]) { /* ... */ }
> or equivalent; or in some other implementation-defined manner."
> 
> So, how can main() have *three* parameters if the standard
> demands it have either zero or two?  Why does the above
> program even compile?  Is this some kind of non-standard
> extension or something?

It's called a "Common extension".

ISO/IEC 9899:1999 (E)

Annex J
(informative)
Portability issues
1 This annex collects some information about portability
  that appears in this International Standard.
 
J.5 Common extensions

J.5.1 Environment arguments
1 In a hosted environment, 
  the main function receives a third argument, 
  char *envp[],
  that points to a null-terminated array of pointers to char,
  each of which points to a string 
  that provides information about the environment 
  for this execution of the program (5.1.2.2.1).

-- 
pete

[toc] | [prev] | [next] | [standalone]


#20035

FromRobbie Hatley <see.my.signature@for.my.contact.info>
Date2012-04-28 22:15 -0700
Message-ID<NaOdndbE44DkUwHS4p2dnAA@giganews.com>
In reply to#20029
Regarding "int main(int argc, char **argv, char **envp)",
"Pete" writes:

> It's called a "Common extension".
>
> ISO/IEC 9899:1999 (E)
>
> Annex J
> (informative)
> Portability issues
> 1 This annex collects some information about portability
>    that appears in this International Standard.
>
> J.5 Common extensions
>
> J.5.1 Environment arguments
> 1 In a hosted environment,
>    the main function receives a third argument,
>    char *envp[],
>    that points to a null-terminated array of pointers to char,
>    each of which points to a string
>    that provides information about the environment
>    for this execution of the program (5.1.2.2.1).

Ah-ha!  Thanks so much for the explanation.  Obviously, then,
this program's author made use of that, and my OS (Win-XP)
and compiler (DJGPP's port of GCC) do support that, so when
I compile it and run it, it works as intended.  Not portable,
but on my setup works quite well.  Could be useful for certain
file maintenance utilities I write from time to time if they
need to look at an environmental variable in real time.

-- 
Cheers,
Robbie Hatley
Santa Ana, CA, USA
lonewolf (at) well (dot) com
http://www.well.com/user/lonewolf/

[toc] | [prev] | [next] | [standalone]


#20037

FromPhilip Lantz <prl@canterey.us>
Date2012-04-29 01:13 -0700
Message-ID<MPG.2a06980f2f759d1898969e@news.eternal-september.org>
In reply to#20035
Robbie Hatley wrote:
> 
> Regarding "int main(int argc, char **argv, char **envp)",
> "Pete" writes:
> 
> > It's called a "Common extension".
> >
> > ISO/IEC 9899:1999 (E)
> >
> > Annex J
> > (informative)
> > Portability issues
> > 1 This annex collects some information about portability
> >    that appears in this International Standard.
> >
> > J.5 Common extensions
> >
> > J.5.1 Environment arguments
> > 1 In a hosted environment,
> >    the main function receives a third argument,
> >    char *envp[],
> >    that points to a null-terminated array of pointers to char,
> >    each of which points to a string
> >    that provides information about the environment
> >    for this execution of the program (5.1.2.2.1).
> 
> Ah-ha!  Thanks so much for the explanation.  Obviously, then,
> this program's author made use of that, and my OS (Win-XP)
> and compiler (DJGPP's port of GCC) do support that, so when
> I compile it and run it, it works as intended.  Not portable,
> but on my setup works quite well.  Could be useful for certain
> file maintenance utilities I write from time to time if they
> need to look at an environmental variable in real time.

If by "real time", you mean that the value of an environment variable 
passed to your program is updated by some external program while your 
program is running, don't count on it.

If all you need to do is read specific environment variables, you should 
use getenv, not this feature.

However, this feature (or the Posix standard "extern char *environ[]") 
can be useful if you need to do something with all the environment 
variables, or to find environment variables where you don't know the 
exact name.

[toc] | [prev] | [next] | [standalone]


#20051

Frompete <pfiland@mindspring.com>
Date2012-04-29 10:02 -0400
Message-ID<4F9D4A06.1F4@mindspring.com>
In reply to#20035
Robbie Hatley wrote:
> 
> Regarding "int main(int argc, char **argv, char **envp)",
> "Pete" writes:
> 
> > It's called a "Common extension".
> >
> > ISO/IEC 9899:1999 (E)
> >
> > Annex J
> > (informative)
> > Portability issues
> > 1 This annex collects some information about portability
> >    that appears in this International Standard.
> >
> > J.5 Common extensions
> >
> > J.5.1 Environment arguments
> > 1 In a hosted environment,
> >    the main function receives a third argument,
> >    char *envp[],
> >    that points to a null-terminated array of pointers to char,
> >    each of which points to a string
> >    that provides information about the environment
> >    for this execution of the program (5.1.2.2.1).
> 
> Ah-ha!  Thanks so much for the explanation.  Obviously, then,
> this program's author made use of that, and my OS (Win-XP)
> and compiler (DJGPP's port of GCC) do support that, so when
> I compile it and run it, it works as intended.  Not portable,
> but on my setup works quite well.  Could be useful for certain
> file maintenance utilities I write from time to time if they
> need to look at an environmental variable in real time.

You're welcome.

-- 
pete

[toc] | [prev] | [next] | [standalone]


#20030

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-04-28 22:15 -0400
Message-ID<jni88b$97s$1@dont-email.me>
In reply to#20027
On 04/28/2012 05:59 PM, Robbie Hatley wrote:
....
> C99 section 5.1.2.2.1(2) clearly states:
> "The function called at program startup is named main.
> The implementation declares no prototype for this function.
> It shall be defined with a return type of int and with no
> parameters:
> int main(void) { /* ... */ }
> or with two parameters (referred to here as argc and argv,
> though any names may be used, as they are local to the function
> in which they are declared):
> int main(int argc, char *argv[]) { /* ... */ }
> or equivalent; or in some other implementation-defined manner."
> 
> So, how can main() have *three* parameters if the standard
> demands it have either zero or two?  Why does the above
> program even compile?  Is this some kind of non-standard
> extension or something?

Look at the last line again: "or in some other implementation-defined
manner".
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#20033

FromKeith Thompson <kst-u@mib.org>
Date2012-04-28 20:18 -0700
Message-ID<ln1un7w7h8.fsf@nuthaus.mib.org>
In reply to#20027
Robbie Hatley <see.my.signature@for.my.contact.info> writes:
> Greetings, group.  I was just looking through my source code
> folders and found the following program, author unknown
> (did I write this myself years ago and forget? dunno).
> According to the C Standard, as I interpret it, this should
> not work... and yet, it does:
>
> #include <stdio.h>
> int main(int argc, char **argv, char **envp)
> {
>     char **envstr = envp;
>     while(*envstr)
>     {
>        puts(*envstr);
>        ++envstr;
>     }
>     return 0;
> }
>
> My expectation: either won't compile, or will crash if ran.
>
> Actuality: gcc compiles this without error.  It does give
> warnings about the unused "argv" and "argc", but it does NOT
> give any error or warning about the illegal third parameter
> "envp".  On execution, the program does not crash, but prints
> a list of all environmental variables and their values.
>
> How can this even compile?
> C99 section 5.1.2.2.1(2) clearly states:
> "The function called at program startup is named main.
[snip]
> or equivalent; or in some other implementation-defined manner."
>
> So, how can main() have *three* parameters if the standard
> demands it have either zero or two?  Why does the above
> program even compile?  Is this some kind of non-standard
> extension or something?

James Kuyper already pointed out the phrase "or in some other
implementation-defined manner".

In addition, that section specifies how the main function "shall be
defined", but when the word "shall" appears outside a constraint, it
states a rule whose violation causes undefined behavior.  There's no
requirement for a diagnostic -- even if the implementation *doesn't*
define
    int main(int argc, char **argv, char **envp) /* ... */
as a valid definition of main.

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#20038

From"Heinrich Wolf" <invalid@invalid.invalid>
Date2012-04-29 11:20 +0200
Message-ID<jnj143$qu1$1@news.m-online.net>
In reply to#20027
Hi,

my old Borland Turbo C 2.0 also compiles that program and that lists the 
environment correctly without error.

Heiner 

[toc] | [prev] | [next] | [standalone]


#20040

From"Heinrich Wolf" <invalid@invalid.invalid>
Date2012-04-29 11:31 +0200
Message-ID<jnj1pg$42j$1@news.m-online.net>
In reply to#20038
In the help of Borland C++ Builder 5 the third parameter env is documented. 
Here is the documentation. I am sorry that it is German.



Drei Parameter (Argumente) werden von der Borland 
C++Builder-Programmstartroutine an main übergeben: argc, argv und env.

argc ist eine Ganzzahl und gibt die Anzahl der an main übergebenen 
Kommandozeilenargumente an, einschließlich des Namens des ausführbaren 
Programms selbst.
 argv ist ein Array von Zeigern auf Strings (char *[]).

- argv[0] ist der vollständige Pfadname des laufenden Programms.
- argv[1] zeigt auf den ersten String nach dem Programmnamen, der in der 
Kommandozeile des Betriebssystems eingegeben wurde.
- argv[2] zeigt auf den zweiten String, der nach dem Programmnamen 
eingegeben wurde.
- argv[argc-1] zeigt auf das letzte an main übergebene Argument.
- argv[argc] enthält NULL.

env ist ebenfalls ein Array von Zeigern auf Strings. Jedes Element von env[] 
enthält einen String der Form ENVVAR=Wert.

- ENVVAR ist der Name einer Umgebungsvariablen, wie etwa PATH oder COMSPEC.
- Wert ist der Wert, auf den ENVVAR gesetzt ist, wie beispielsweise 
C:\APPS;C:\TOOLS; (für PATH) oder C:\DOS\COMMAND.COM (für COMSPEC).

Bei der Deklaration dieser Parameter muß die exakte Reihenfolge eingehalten 
werden: argc, argv, env. So sind beispielsweise sämtliche folgenden 
Deklarationen von Argumenten zu main gültig:

int main()
int main(int argc)                 /* erlaubt, aber sehr ungewöhnlich */
int main(int argc, char * argv[])
int main(int argc, char * argv[], char * env[])]

Die Deklaration int main(int argc) ist zulässig; es ist jedoch sehr 
ungewöhnlich, argc in einem Programm zu verwenden, ohne zugleich die 
Argumente von argv zu benutzen.

Das Argument env ist auch über die globale Variable environ verfügbar.

In allen Umgebungen sind argc und argv ebenfalls über die globalen Variablen 
_argc und _argv verfügbar.

main mit einer Unicode-Anwendung

Die Unicode-Version der Funktion main lautet:

int wmain (int argc, wchar_t *argv[])

Der Parameter argv (und optional der Parameter envp) unterstützt 
wide-character-Typen.

Die folgende _tmain-Funktion ist ein Makro, das - abhängig von dem 
Anwendungstyp - auf die entsprechende main-Funktion expandiert wird.

int _tmain (int argc, _TCHAR *argv[]) 

[toc] | [prev] | [next] | [standalone]


#20041

From"Heinrich Wolf" <invalid@invalid.invalid>
Date2012-04-29 11:38 +0200
Message-ID<jnj25u$kgi$1@news.m-online.net>
In reply to#20038
"Heinrich Wolf" <invalid@invalid.invalid> schrieb im Newsbeitrag 
news:jnj143$qu1$1@news.m-online.net...
> Hi,
>
> my old Borland Turbo C 2.0 also compiles that program and that lists the 
> environment correctly without error.
>
> Heiner

My manual of Borland Turbo C 2.0 also documents env as third parameter of 
main. 

[toc] | [prev] | [next] | [standalone]


#20042

From"Heinrich Wolf" <invalid@invalid.invalid>
Date2012-04-29 12:26 +0200
Message-ID<jnj507$onn$1@news.m-online.net>
In reply to#20038
man gcc on my Fedora 14 Linux writes:

...
       -Wmain
           Warn if the type of main is suspicious.  main should be a 
function with external linkage, returning int, taking either zero arguments, 
two, or
           three arguments of appropriate types.  This warning is enabled by 
default in C++ and is enabled by either -Wall or -pedantic.
... 

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.c


csiph-web