Is it OK to modify argv ???

C

Charles Sullivan

I'm working on a program which has a "tree" of command line arguments,
i.e.,
myprogram level1 [ level2 [ level3 ...]]]
such that there can be more than one level2 argument for each level1
argument and more than one level3 argument for each level2 argument, etc.

Suppose I code it similar to this fragment:

int main( int argc, char *argv[] )
{
...
if ( strcmp(argv[1], "blivet") == 0 )
return do_blivet( --argc, ++argv );
else if ( ...
...
}

int do_blivet( int argc, char *argv[] ) {
...
if ( strcmp(argv[1], "whosis") == 0 )
return do_whosis( --argc, ++argv );
else if ( ...
...
}

It turns out to be somewhat more convenient in practise to increment
argv as above than to pass the argument (argv + 1).

My question: Is it correct C code to increment argv as above?
I'm wondering whether doing this might prevent memory
allocated for array *argv[] from being freed upon program exit,
under some operating systems.

Thanks for your advice.

Regards,
Charles Sullivan
 
M

Mike Wahler

Charles Sullivan said:
I'm working on a program which has a "tree" of command line arguments,
i.e.,
myprogram level1 [ level2 [ level3 ...]]]
such that there can be more than one level2 argument for each level1
argument and more than one level3 argument for each level2 argument, etc.

Suppose I code it similar to this fragment:

int main( int argc, char *argv[] )
{
...
if ( strcmp(argv[1], "blivet") == 0 )
return do_blivet( --argc, ++argv );
else if ( ...
...
}

int do_blivet( int argc, char *argv[] ) {
...
if ( strcmp(argv[1], "whosis") == 0 )
return do_whosis( --argc, ++argv );
else if ( ...
...
}

It turns out to be somewhat more convenient in practise to increment
argv as above than to pass the argument (argv + 1).

My question: Is it correct C code to increment argv as above?
I'm wondering whether doing this might prevent memory
allocated for array *argv[] from being freed upon program exit,
under some operating systems.

Thanks for your advice.

Remember that C arguments are passed 'by value'.
So any changes you make to a function's parameters
will not be reflected by the caller. 'main()' is
no different from any other function in this respect.

-Mike
 
R

Richard Heathfield

Charles said:
I'm working on a program which has a "tree" of command line arguments,

int main(int argc, char **argv)
{
it's okay to modify argv, because it's just a copy of a pointer.

argv++; /* no problem here */

it's okay to modify argv[n], provided such a character exists. Thus:

if(strlen(argv[1]) > strlen("hello")
{
strcpy(argv[1], "hello"); /* no problem here */
}

it's NOT okay to modify the pointer argv:

argv = "new pointer value"; /* BUG! */
It turns out to be somewhat more convenient in practise to increment
argv as above than to pass the argument (argv + 1).

My question: Is it correct C code to increment argv as above?

Yes, that's fine.
I'm wondering whether doing this might prevent memory
allocated for array *argv[] from being freed upon program exit,
under some operating systems.

As long as you stick to the rules I outlined above (and all the other rules,
and some more that I haven't thought of yet), you'll be fine.
 
I

Irrwahn Grausewitz

int do_blivet( int argc, char *argv[] ) {
...
if ( strcmp(argv[1], "whosis") == 0 )
return do_whosis( --argc, ++argv );
else if ( ...
...
}

It turns out to be somewhat more convenient in practise to increment
argv as above than to pass the argument (argv + 1).

My question: Is it correct C code to increment argv as above?
I'm wondering whether doing this might prevent memory
allocated for array *argv[] from being freed upon program exit,
under some operating systems.
argc and argv have function scope, IOW they are local to main();
thus, you may arbitrarily change their values without doing harm
to anything that happens to take place outside of main().

Regards,

Irrwahn
--
do not write: void main(...)
do not use gets()
do not cast the return value of malloc()
do not fflush( stdin )
read the c.l.c-faq: http://www.eskimo.com/~scs/C-faq/top.html
 
K

Kevin Easton

Charles Sullivan said:
I'm working on a program which has a "tree" of command line arguments,
i.e.,
myprogram level1 [ level2 [ level3 ...]]]
such that there can be more than one level2 argument for each level1
argument and more than one level3 argument for each level2 argument, etc.

Suppose I code it similar to this fragment:

int main( int argc, char *argv[] )
{
...
if ( strcmp(argv[1], "blivet") == 0 )
return do_blivet( --argc, ++argv );
else if ( ...
...
}

int do_blivet( int argc, char *argv[] ) {
...
if ( strcmp(argv[1], "whosis") == 0 )
return do_whosis( --argc, ++argv );
else if ( ...
...
}

It turns out to be somewhat more convenient in practise to increment
argv as above than to pass the argument (argv + 1).

My question: Is it correct C code to increment argv as above?

Yes, it's just a regular function paramater.

- Kevin.
 
M

Malcolm

Charles Sullivan said:
It turns out to be somewhat more convenient in practise to increment
argv as above than to pass the argument (argv + 1).

My question: Is it correct C code to increment argv as above?
It is OK. It may not be a good idea. There is a school of thought that
function parameters shouldn't be modified at all by the callee. In a
mathematical function this makes sense, since in mathematical convention the
symbols have constant values.
In the case of argv and argc , a maintaining programmer wouldn't expect
these to be modified, and might wonder why argv[0] no longer pointed to the
program name.
On the other hand, if the code does look a lot clearer with the
modification, then it is quite hard to argue that you shouldn't use it.
 
J

Jan Engelhardt

I'm working on a program which has a "tree" of command line arguments,
int main(int argc, char **argv)
{
it's okay to modify argv[n], provided such a character exists. Thus:

if(strlen(argv[1]) > strlen("hello")
{
strcpy(argv[1], "hello"); /* no problem here */
}

it's NOT okay to modify the pointer argv:

argv = "new pointer value"; /* BUG! */


Why that? argv[n] is just a copy too, that is, argv and argv[*] are within the
stack, and even argv[*][*] is.
 
I

Irrwahn Grausewitz

Jan Engelhardt said:
I'm working on a program which has a "tree" of command line arguments,

int main(int argc, char **argv)
{
it's okay to modify argv[n], provided such a character exists. Thus:

if(strlen(argv[1]) > strlen("hello")
{
strcpy(argv[1], "hello"); /* no problem here */
}

it's NOT okay to modify the pointer argv:

argv = "new pointer value"; /* BUG! */


Why that? argv[n] is just a copy too, that is, argv and argv[*] are within the
stack, and even argv[*][*] is.


Errm..., could you please elaborate on this?

Hint: Where does argv point to?

BTW: There's nothing like a stack from the C POV.

Regards

Irrwahn
--
do not write: void main(...)
do not use gets()
do not cast the return value of malloc()
do not fflush( stdin )
read the c.l.c-faq: http://www.eskimo.com/~scs/C-faq/top.html
 
R

Richard Heathfield

Jan said:
I'm working on a program which has a "tree" of command line arguments,

int main(int argc, char **argv)
{
it's okay to modify argv[n], provided such a character exists. Thus:

if(strlen(argv[1]) > strlen("hello")
{
strcpy(argv[1], "hello"); /* no problem here */
}

it's NOT okay to modify the pointer argv:

argv = "new pointer value"; /* BUG! */


Why that? argv[n] is just a copy too,


Wrong. Consider the following code:

#include <stdlib.h>
int foo(char **v)
{
char *p = "Hello";
v[0] = p;
}
int main(void)
{
char *v[1] = {0};
v[0] = malloc(10);
if(v[0] != NULL)
{
foo(v);
free(v[0]); /* bang! */
}
return 0;
}
that is, argv and argv[*] are within
the stack, and even argv[*][*] is.

The Standard says:

The parameters argc and argv and the strings pointed to by the argv
array shall be modifiable by the program, and retain their last-stored
values between program startup and program termination.

Since nothing is said about the pointers themselves, we must assume they are
non-modifiable.
 
M

Matt Gregory

Richard said:
The Standard says:

The parameters argc and argv and the strings pointed to by the argv
array shall be modifiable by the program, and retain their last-stored
values between program startup and program termination.

Since nothing is said about the pointers themselves, we must assume they are
non-modifiable.

Is it non-portable to swap them, though, like getopt() does?

char *temp = argv[2];
argv[2] = argv[1];
argv[1] = temp;

What's the story on that?
 
R

Richard Heathfield

Matt said:
Richard said:
The Standard says:

The parameters argc and argv and the strings pointed to by the argv
array shall be modifiable by the program, and retain their last-stored
values between program startup and program termination.

Since nothing is said about the pointers themselves, we must assume they
are non-modifiable.

Is it non-portable to swap them, though, like getopt() does?

char *temp = argv[2];
argv[2] = argv[1];
argv[1] = temp;

What's the story on that?

Undefined behaviour, IMHO, for the reason stated above. Of course, I'm ready
to be corrected by anyone with appropriate C&V.
 
C

Charles Sullivan

Thanks folks. You've answered my question and also helped clarify my
thinking.

Much appreciated!

Regards,
Charles Sullivan
 
T

The Real OS/2 Guy

Jan Engelhardt said:
I'm working on a program which has a "tree" of command line arguments,

int main(int argc, char **argv)
{
it's okay to modify argv[n], provided such a character exists. Thus:

if(strlen(argv[1]) > strlen("hello")
{
strcpy(argv[1], "hello"); /* no problem here */
}

it's NOT okay to modify the pointer argv:

argv = "new pointer value"; /* BUG! */


Why that? argv[n] is just a copy too, that is, argv and argv[*] are within the
stack, and even argv[*][*] is.


Errm..., could you please elaborate on this?

Hint: Where does argv point to?


argv points to an array of pointers. It is possible to change one or
all of them.
argv[] points to an zero termitated array of chars. It is possible to
change any char in that array. You may even store any other string
that is equal in size or shorter than the initial one there. Storing a
string that is longer results in undefined behavior.
 
T

The Real OS/2 Guy

What is argv? It is a pointer to an array of pointers. So any pointer
argv points to is changeable.
Any string a pointer in the array of pointers is changeable too.
Is it non-portable to swap them, though, like getopt() does?

char *temp = argv[2];
argv[2] = argv[1];
argv[1] = temp;

What's the story on that?
It is portable. The only you can't do is to insert more pointers as
argc says it has at startup.

At startup argc contains the number of pointers argv points to.
Changing the sequence in the array is ok. Changing any pointer in the
array to whatever is ok too. Adding more pointers as the initial value
has is NOT ok.

Changing an array arv[] poits to is ok. You may shorten this array as
kile. Extending it results in undefined behavior.

argv is in memory the application has unlimited access to (else the
CRT would fail to build it).

argv is nothing than each other pointer to an array of pointers that
points to strings.
 
I

Irrwahn Grausewitz

The Real OS/2 Guy said:
Jan Engelhardt said:
Richard Heathfield wrote:

it's NOT okay to modify the pointer argv:

argv = "new pointer value"; /* BUG! */

Why that? argv[n] is just a copy too, that is, argv and argv[*] are within the
stack, and even argv[*][*] is.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Errm..., could you please elaborate on this?

Hint: Where does argv point to?


argv points to an array of pointers. It is possible to change one or
all of them.
argv[] points to an zero termitated array of chars. It is possible to
change any char in that array. You may even store any other string
that is equal in size or shorter than the initial one there. Storing a
string that is longer results in undefined behavior.


This discussion was not about changing the /characters/ pointed to, it
was about changing the /pointer/ values. And JE's claim that argv[n]
is a local copy of a pointer (just like argv, which undoubtely is), is
plain wrong.

Irrwahn
 
I

Irrwahn Grausewitz

The Real OS/2 Guy said:
What is argv? It is a pointer to an array of pointers. So any pointer
argv points to is changeable.
Any string a pointer in the array of pointers is changeable too.
Is it non-portable to swap them, though, like getopt() does?

char *temp = argv[2];
argv[2] = argv[1];
argv[1] = temp;

What's the story on that?
It is portable. The only you can't do is to insert more pointers as
argc says it has at startup.

At startup argc contains the number of pointers argv points to.
Changing the sequence in the array is ok. Changing any pointer in the
array to whatever is ok too.

To whatever? So you consider the last line of the following code that
Richard posted upthread to be correct (comment delimiters deliberately
added)?

RH> int main(int argc, char **argv)
RH> {
RH> /* it's okay to modify argv, because it's just a copy of a
RH> pointer.*/
RH>
RH> argv++; /* no problem here */
RH>
RH> /* it's okay to modify argv[n], provided such a character
RH> exists. Thus:*/
RH>
RH> if(strlen(argv[1]) > strlen("hello")
RH> {
RH> strcpy(argv[1], "hello"); /* no problem here */
RH> }
RH>
RH> /* it's NOT okay to modify the pointer argv: */
RH>
RH> argv = "new pointer value"; /* BUG! */
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the interesting line.

Irrwahn
 
R

Richard Heathfield

The said:
char *temp = argv[2];
argv[2] = argv[1];
argv[1] = temp;

What's the story on that?
It is portable.

I disagree. The standard gives explicit permission to modify argv itself and
the strings pointed to by argv[0] through argv[argc - 1], but it doesn't
give permission to modify argv[0] through argv[argc - 1] themselves.

If you can provide C&V to back up your point of view, please do so.
 
T

The Real OS/2 Guy

To whatever? So you consider the last line of the following code that
Richard posted upthread to be correct (comment delimiters deliberately
added)?
RH> /* it's NOT okay to modify the pointer argv: */
RH>
RH> argv = "new pointer value"; /* BUG! */
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the interesting line.

Yes - so long i is < argc and he has either saved the string argv
points to before or has really no interest on the parameter he's
loosing now.
 
I

Irrwahn Grausewitz

The Real OS/2 Guy said:
To whatever? So you consider the last line of the following code that
Richard posted upthread to be correct (comment delimiters deliberately
added)?
RH> /* it's NOT okay to modify the pointer argv: */
RH>
RH> argv = "new pointer value"; /* BUG! */
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the interesting line.

Yes - so long i is < argc and he has either saved the string argv
points to before or has really no interest on the parameter he's
loosing now.


Which part of the standard makes you think so? Chapter and verse,
please.
 
A

Arthur J. O'Dwyer

To whatever? So you consider the last line of the following code that
Richard posted upthread to be correct (comment delimiters deliberately
added)?

RH> /* it's NOT okay to modify the pointer argv: */
RH>
RH> argv = "new pointer value"; /* BUG! */
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the interesting line.


Yes - so long i is < argc and he has either saved the string argv
points to before or has really no interest on the parameter he's
loosing now.



% cat last-message

% /imaginary/bin/remove-rhetoric < last-message
So you're wrong, obviously. Consult the Standard. argv is
not modifiable. Period. End of discussion.


%


-Arthur
 

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,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top