Questions about argv

W

WD

I have a few questions regarding argv. When passing command-line
arguments to a C program, I understand that argc (int argc) is the
number of command-line arguments with which the program was invoked,
and argv (char *argv[]) is a pointer to an array of character strings
that contain the arguments, one per string. Why does the code
illustrated below work as written on i386 using Watcom C on Windows XP
and probably also using gcc on Linux as well?

#include <stdio.h>

main(x,y)
{
printf("%s\n",*(int *)y);
if (x>1)
{
y =y+4;
printf("%s\n",*(int *)y);
}
}

A few questions come to mind:

1.Does the compiler automatically assume main(x,y) to really mean
main(int x, char *y[]) ?
2.How does the first *(int *)y refer to y[0] (the first command-line
argument, i.e. the name of the program) ?
3.After y=y+4 why does the second *(int *)y now refer to y[1] (i.e.
the second command -line argument?

Thanks,
William
 
M

Morris Keesan

I have a few questions regarding argv. When passing command-line
arguments to a C program, I understand that argc (int argc) is the
number of command-line arguments with which the program was invoked,
and argv (char *argv[]) is a pointer to an array of character strings
that contain the arguments, one per string. Why does the code
illustrated below work as written on i386 using Watcom C on Windows XP
and probably also using gcc on Linux as well?

a) because you're using a compiler which does not enforce the standard
b) because of luck, and coincidence.
#include <stdio.h>

main(x,y)
{
printf("%s\n",*(int *)y);
if (x>1)
{
y =y+4;
printf("%s\n",*(int *)y);
}
}

A few questions come to mind:

1.Does the compiler automatically assume main(x,y) to really mean
main(int x, char *y[]) ?

No. Almost certainly, it translates this as
main(int x, int y)
(note also that main(int x, char *y[]) doesn't mean what it looks like.
It doesn't declare y as a pointer to an array. It's really just another
way of writing main(int x, char **y), where y is a pointer to a (char *),
which happens to be the first (char *) in an array.
2.How does the first *(int *)y refer to y[0] (the first command-line
argument, i.e. the name of the program) ?

By coincidence, (int) and (int *) and (char **) are the same size in your
implementation, and the cast operator is implemented as not causing any
conversion of the bits of the value, so the second argument to main, even
though it's a pointer, happens to fit into an int. The (int *) cast makes
this particular compiler treat the bits of (what it thinks is) an int as if
they were the bits of a pointer, and it dereferences this pointer, getting
what it again thinks is an int, and it passes this int as an argument to
printf. This int happens to contain the bits of a (char *), which is what
printf is looking for.
3.After y=y+4 why does the second *(int *)y now refer to y[1] (i.e.
the second command -line argument?

Again by coincidence, sizeof(char *) on your platform happens to be 4, and
adding 4 to y gives you the address which is 4 bytes beyond the address
originally contained in y, which happens to be the address of the next
(char *) in the array, because of the size of pointers, and because of the
flat address space of your machine, which allows you to treat pointers as
integers and (mostly) get away with it.
 
I

Ian Collins

I have a few questions regarding argv. When passing command-line
arguments to a C program, I understand that argc (int argc) is the
number of command-line arguments with which the program was invoked,
and argv (char *argv[]) is a pointer to an array of character strings
that contain the arguments, one per string. Why does the code
illustrated below work as written on i386 using Watcom C on Windows XP
and probably also using gcc on Linux as well?

#include<stdio.h>

main(x,y)

Your compiler should have a good old whinge about this incomplete K&R
style declaration.
{
printf("%s\n",*(int *)y);
if (x>1)
{
y =y+4;
printf("%s\n",*(int *)y);
}
}

A few questions come to mind:

1.Does the compiler automatically assume main(x,y) to really mean
main(int x, char *y[]) ?
No.

2.How does the first *(int *)y refer to y[0] (the first command-line
argument, i.e. the name of the program) ?

That's just where they happen to be in memory. Whatever invokes your
program puts the values for argc and argv where a correctly defined main
would find them.
3.After y=y+4 why does the second *(int *)y now refer to y[1] (i.e.
the second command -line argument?

It might, but what you are doing is undefined.
 
B

Barry Schwarz

I have a few questions regarding argv. When passing command-line
arguments to a C program, I understand that argc (int argc) is the
number of command-line arguments with which the program was invoked,
and argv (char *argv[]) is a pointer to an array of character strings
that contain the arguments, one per string. Why does the code
illustrated below work as written on i386 using Watcom C on Windows XP
and probably also using gcc on Linux as well?

Because it invokes undefined behavior. One of the worst
manifestations of undefined behavior is to do what you expect (this
time).
#include <stdio.h>

main(x,y)

Under C89, y will have type int. The actual argument is type char*.
{
printf("%s\n",*(int *)y);

The format specification %s requires the corresponding argument to
have type char*. This argument has type int. Some compilers will
check and diagnose this error.
if (x>1)
{
y =y+4;

You were unlucky that sizeof(char*) is 4.
printf("%s\n",*(int *)y);

Same argument mismatch.
}
}

A few questions come to mind:

1.Does the compiler automatically assume main(x,y) to really mean
main(int x, char *y[]) ?
No.

2.How does the first *(int *)y refer to y[0] (the first command-line
argument, i.e. the name of the program) ?

It doesn't.
3.After y=y+4 why does the second *(int *)y now refer to y[1] (i.e.
the second command -line argument?

It doesn't.
 
K

Keith Thompson

Michael Tsang said:
This is illegal. What do you mean?

In C90, that's a legal definition. C90, and even C99, still support
K&R-style function definitions. C90 supports implicit int, which C99
does not. A more explicit verison of the above would be:

int main(x, y)
int x;
int y;
{
...
You want

int main(int x, char **y)

?

Yes, there's (almost?) never a good reason to use K&R-style
function definitions. But of course it should be:

int main(int argc, char **argv)

The language doesn't require those names, but they're conventional,
and there's no good reason not to use them.
 
P

Phred Phungus

pete said:
Keith said:
In C90, that's a legal definition.

No, it isn't. y[x] is not a null pointer.

ISO/IEC 9899: 1990

5.1.2.2.1 Program startup

The function called at program startup is named main.
The implementation declares no prototype for this function.
It can be defined 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[]) { /*...*/ }

If they are defined, the parameters to the main function
shall obey the following constraints:

— The value of argc shall be nonnegative.
— argv[argc] shall be a null pointer.

Pete looks right to me, and if so would be the third time in a month
that Keith went down on the facts.

It seems unusual to me, but maybe he always had Chuck to make an error
first.

I can be wrong as well.
 
S

spinoza1111

No, it isn't. y[x] is not a null pointer.
ISO/IEC 9899: 1990
5.1.2.2.1 Program startup
The function called at program startup is named main.
The implementation declares no prototype for this function.
It can be defined 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[]) { /*...*/ }
If they are defined, the parameters to the main function
shall obey the following constraints:
The value of argc shall be nonnegative.
argv[argc] shall be a null pointer.

Pete looks right to me, and if so would be the third time in a month
that Keith went down on the facts.

It seems unusual to me, but maybe he always had Chuck to make an error
first.

I can be wrong as well.

No, Kiki was right. He was talking about pre-Standard K & R C.
 
T

Tim Rentsch

pete said:
Keith said:
In C90, that's a legal definition.

No, it isn't. y[x] is not a null pointer. [snip elaboration]

What he said is that it's a legal definition; it _is_ a legal
definition. It might be undefined behavior (or might not, if we
consider the implementation as a freestanding implementation),
but whether or not there is undefined behavior this is a legal
definition, in the sense that no constraints are violated.
 
W

WD

#include said:
Under C89, y will have type int.  The actual argument is type char*.

I don't understand what you mean here. Are you saying y will have
type int under C89 because of the way main was declared in my example,
i.e. main(x,y), or do you mean for some other reason? How can the
actual argument type be char* if it was declared as above, and assumed
to be int by the compiler?
 
W

WD

I don't understand what you mean here.  Are you saying y will have
type int under C89 because of the way main was declared in my example,
i.e. main(x,y), or do you mean for some other reason?  How can the
actual argument type be char* if it was declared as above, and assumed
to be int by the compiler?

I forgot to say that my last post was in respose to:

Sorry,
William
 
T

Tim Rentsch

WD said:
I don't understand what you mean here. Are you saying y will have
type int under C89 because of the way main was declared in my example,
i.e. main(x,y), or do you mean for some other reason? How can the
actual argument type be char* if it was declared as above, and assumed
to be int by the compiler?

Right, the type of the parameter 'y' is int, because of how it's
declared (in the parameter list for main(), with no subsequent
declaration before the function body).

The type of the _argument_ will match 'char *[]', which is to say
the argument that is is passed will be of type 'char **'. The type
of the actual argument is different than the type of the parameter
'y'. The definition of main() above declares only the type of its
parameters (and implicitly its return type, of course), not the
types of any actual arguments.
 
K

Keith Thompson

pete said:
Keith said:
In C90, that's a legal definition.

No, it isn't. y[x] is not a null pointer.
[snip]

Good point. I was misusing the word "legal" (a word the Standard
doesn't generally use at all). I'll be more precise.

This complete translation unit:

main(x, y)
{
}

violates no C90 constraints or syntax rules, so an implementation is
not required to produce any diagnostics for it. It's equivalent to
the following (which also violates no C99 constraints or syntax
rules):

int main(x, y)
int x;
int y;
{
}

In a freestanding implementation, it might even be perfectly correct.

For a hosted implementation, it's wrong in the sense that it doesn't
define main as the standard requires, but that's not an error the
compiler is required to care about. The program's behavior is
undefined.

Apart from the wrongness of having main take two int arguments, it
uses a K&R-style definition and it uses non-conventional names for
main's arguments.
 
K

Keith Thompson

pete said:
If they are defined, the parameters to the main function
shall obey the following constraints:

-- The value of argc shall be nonnegative.
-- argv[argc] shall be a null pointer.

I just noticed that this is a rather odd use of the word
"constraints". Unlike most constraints mentioned in the standard,
these are constraints on the runtime environment, not on the program.
If most other constraints are violated, the implementation must
issue a diagnostic. If these "constraints" are violated, either
the implementation is non-conforming, or the program's behavior
is undefined (because of the violation of a "shall" outside a
constraint).

Possibly the authors were using the word "constraints" in its
normal English sense rather than in the sense defined by the
standard itself. Perhaps "conditions" would have been a better word.
 
K

Keith Thompson

WD said:
I don't understand what you mean here. Are you saying y will have
type int under C89 because of the way main was declared in my example,
i.e. main(x,y), or do you mean for some other reason? How can the
actual argument type be char* if it was declared as above, and assumed
to be int by the compiler?

The standard requires main to have either no arguments, or two
arguments with types int and char**. The code in the runtime
environment that invokes your program will invoke your main function
appropriately, assuming that you've defined it properly.

Instead, you've defined it to take two int arguments. This is an
error, though it's not an error that the implementation is required
to detect or diagnose.

Defining a function named "main" is an implicit promise from the
programmer to the implementation that it will take the right number
and type of arguments and return a result of type int. By violating
that promise, you have effectively lied to the compiler, which
could make it impossible for the compiler to do its job.

So don't do that, and don't waste too much time worry about what
happens if you do.

Note 1: All this applies only to hosted implementations.
For freestanding implementations (think embedded systems), the
program's entry point is implementation-defined.

Note 2: Implementations are permitted to support and document forms
of main() other than the two listed in the standards. It's unlikely
that "main(x, y)" is such an implementation-defined form.
 

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,582
Members
45,058
Latest member
QQXCharlot

Latest Threads

Top