starting into H&S

B

Ben Bacarisse

Richard Heathfield said:
Ben Bacarisse said:
What, even in comp.lang.c?

Yes, I think so.
The case is (quite reasonably) that since "argv[argc] shall be null
a pointer" (5.1.2.2.1p2) argv can't, itself, be == NULL. Let's
reason unreasonably for while...

The evaluation of argv is undefined when argv == NULL, so what
prevents the DS9000's C implementation from defining NULL == NULL
for all i? This will make argv[argc] == NULL even when argv ==
NULL.


That's a circular argument, because it presupposes that argv is
allowed to be a null pointer.


Which is an excellent presupposition unless there is some reason to
think that it can't be so. For example, I assume that argc can be
negative right up to the point where the standard says that it
non-negative. Similarly, there is no reason to imagine argv is
constrained other than by some restriction placed on it by the
standard. That argv[argc] shall be a null pointer is not (to me)
quite enough.

The circular argument would be to assume that it can't be null,
surely? That would be to assume the conclusion. The only way to
proceed is to see if argv == NULL is prevented by some part of the
standard.
Note, too, the text "the strings pointed to by the argv array shall be
modifiable by the program". The wording is sloppy, but we know it
means the strings pointed to by the members of the array to whose
first element argv points. We can easily accept that the number of
strings in that array may be 0 (because argc can be 0 and argv[argc]
must be a null pointer), but it seems to me that the argv pointer
must at least point at that array - and therefore it can't be null.

I would buy this except for the problematic wording. If it said "the
array pointed to by argv" the case would be closed because a null
pointer can't point to an array. Since argv is not an array, that
clause can't say anything about it at all. argv /usually/ points to
an array and, when it does, I suppose we can take your meaning for
that clause, but to reply on it alone seems rather thin.

To be clear: there are two ways to correct the loose wording. One is
to make it say what I think we both agree is meant: "the strings
pointed to by the elements of the array pointed to by argv"; but there
are lots of other possible corrections "if argv points to an array,
both the pointers in that array and the strings pointed to by its
elements are modifiable".

Note that in this later case I chose to clarify something else. The
loose wording is vague about exactly what can be modified. It seems
to permit setting argv and, if argv[0] points to a string, setting
argv[0][0] but it is not clear if argv[0] itself can be assigned to
(when it exists).
 
J

jameskuyper

Keith said:
What does "equal to a pointer" mean other than as defined by the "=="
operator?

It means "is a pointer with the same value". A null pointer constant
gets converted in such a context into a null pointer that could be
equal to some other pointer. However, if it is an expression of
integer type, it is not itself equal to that pointer (it can't be, it
hasn't the right type), it only compares equal to it (as a result of
the conversion).
 
B

Barry Schwarz

Richard Heathfield said:
Joe Wright said:


Dead in the water.

What, even in comp.lang.c?

The case is (quite reasonably) that since "argv[argc] shall be null a
pointer" (5.1.2.2.1p2) argv can't, itself, be == NULL. Let's reason
unreasonably for while...

The evaluation of argv is undefined when argv == NULL, so what
prevents the DS9000's C implementation from defining NULL == NULL


Because while the DS9000 is perverse, it is compliant. In order for
NULL[0] to be NULL, NULL[0] must evaluate to an object (whose value is
NULL). That would require NULL to point to that object. But NULL
never points to an object (6.3.2.3-3).
for all i? This will make argv[argc] == NULL even when argv == NULL.
 
B

Ben Bacarisse

Barry Schwarz said:
The evaluation of argv is undefined when argv == NULL, so what
prevents the DS9000's C implementation from defining NULL == NULL


Because while the DS9000 is perverse, it is compliant. In order for
NULL[0] to be NULL, NULL[0] must evaluate to an object (whose value is
NULL). That would require NULL to point to that object. But NULL
never points to an object (6.3.2.3-3).


In what way does the standard constrain what meaning an implementation
may put on NULL[0]?

BTW, 6.3.2.3-3 just tells us what a null-pointer constant is. That
NULL expands to such an expression is not in question. Other sections
tell us that NULL[0] is UB.
for all i? This will make argv[argc] == NULL even when argv == NULL.
 
K

Keith Thompson

Ben Bacarisse said:
I would buy this except for the problematic wording. If it said "the
array pointed to by argv" the case would be closed because a null
pointer can't point to an array. Since argv is not an array, that
clause can't say anything about it at all. argv /usually/ points to
an array and, when it does, I suppose we can take your meaning for
that clause, but to reply on it alone seems rather thin.

To be clear: there are two ways to correct the loose wording. One is
to make it say what I think we both agree is meant: "the strings
pointed to by the elements of the array pointed to by argv"; but there
are lots of other possible corrections "if argv points to an array,
both the pointers in that array and the strings pointed to by its
elements are modifiable".
[...]

Quibble: argv doesn't point to an array it points (assuming it's
non-null) to the first element of an array. The standard lets us
refer to a pointer to the first character of a string as a pointer to
the string; it doesn't permit similar looseness when talking about a
pointer to the first element of an array (since a pointer to an array
and a pointer to its first element are two different things).
 
K

Keith Thompson

jameskuyper said:
It means "is a pointer with the same value". A null pointer constant
gets converted in such a context into a null pointer that could be
equal to some other pointer. However, if it is an expression of
integer type, it is not itself equal to that pointer (it can't be, it
hasn't the right type), it only compares equal to it (as a result of
the conversion).

That's not an unreasonable definition, but it could be argued either
way.
 
B

Barry Schwarz

Barry Schwarz said:
The evaluation of argv is undefined when argv == NULL, so what
prevents the DS9000's C implementation from defining NULL == NULL


Because while the DS9000 is perverse, it is compliant. In order for
NULL[0] to be NULL, NULL[0] must evaluate to an object (whose value is
NULL). That would require NULL to point to that object. But NULL
never points to an object (6.3.2.3-3).


In what way does the standard constrain what meaning an implementation
may put on NULL[0]?


I was using your imprecise but intuitively obvious (at least to me)
notation. If you are going to object to it, by all means let's be
precise. The question was never about the source code "NULL". It
was about the expression argv[0] when argv happens to compare equal to
NULL (or if you prefer, happens to have been assigned the value NULL).
You assert that it might be possible for argv[0] to evaluate to NULL.
I claim for that to occur, argv[0] must evaluate to an object but that
such an evaluation is not possible due to the referenced paragraph.
Instances of argv for i>0 follow by induction.
BTW, 6.3.2.3-3 just tells us what a null-pointer constant is. That

Read the second sentence. It places a restriction on a pointer which
has been assigned NULL as a value.
NULL expands to such an expression is not in question. Other sections
tell us that NULL[0] is UB.
for all i? This will make argv[argc] == NULL even when argv == NULL.
 
B

Ben Bacarisse

Barry Schwarz said:
The evaluation of argv is undefined when argv == NULL, so what
prevents the DS9000's C implementation from defining NULL == NULL

Because while the DS9000 is perverse, it is compliant. In order for
NULL[0] to be NULL, NULL[0] must evaluate to an object (whose value is
NULL). That would require NULL to point to that object. But NULL
never points to an object (6.3.2.3-3).


In what way does the standard constrain what meaning an implementation
may put on NULL[0]?


I was using your imprecise but intuitively obvious (at least to me)
notation. If you are going to object to it, by all means let's be
precise. The question was never about the source code "NULL". It
was about the expression argv[0] when argv happens to compare equal to
NULL (or if you prefer, happens to have been assigned the value NULL).
You assert that it might be possible for argv[0] to evaluate to NULL.
I claim for that to occur, argv[0] must evaluate to an object but that
such an evaluation is not possible due to the referenced paragraph.


The cited paragraph tells us the null pointers don't point at objects.
I don't think objects are required for expressions to have a value
(otherwise 1 + 2 would have no value). argv[0] (when argv == NULL) is
an expression whose possible meanings are defined by (amongst others)
6.5.2.1 (Array subscripting), 6.5.6 (Additive operators) and 6.5.3.2
(Address and indirection operators). This last is not needed since
the behaviour is undefined once we calculate argv+0.
Instances of argv for i>0 follow by induction.
BTW, 6.3.2.3-3 just tells us what a null-pointer constant is. That

Read the second sentence. It places a restriction on a pointer which
has been assigned NULL as a value.


It tells us that a null pointer (suitably converted) will not compare
equal to a pointer to any object or function. Together with the
definition of [] (and therefore of +) we can say that evaluating
argv+0 (when argv == NULL) is undefined behaviour. But that, I think,
is all we can say as far as the standard is concerned.

You can't be saying that argv[0] (when argv == NULL) is well-defined
-- I am sure you agree it is undefined -- but you seem to be saying
that it is some sort of constrained undefined behaviour upon which the
standard /does/ impose some requirements.

<snip>
 
R

Richard Bos

Keith Thompson said:
argc can't be 3; argc is the name of a parameter, and 3 is a decimal
constant.

Hm. 3 is a value. It's a constant, but it's also a decimal value. NULL,
OTOH, is not a value. It is an identifier (which 3 is not), which is
#defined to correspond to a value of 0 (or that value cast to void *).
There is an extra level of indirection here.
Of course I'm not serious, but it's not uncommon to use various forms
of the verb "be" to refer to equality. Saying that "argv is NULL"
merely means that the expression ``argv == NULL'' is true.

But yes, saying argv is null rather than NULL would be clearer.

The thing is, that extra level of indirection _already_ causes a lot of
confusion among novice C programmers. Especially in the case of NULL, I
would say that as much clarity is needed as possible.

Richard
 
F

Frank

Nick Keighley said:
why can't argv be NULL.

Because argv[argc] must be NULL.

I haven't read the rest of this thread carefully, as it seems to deal
more with the source that our italian friend posted. That said, do I
understand you correctly that for the following code:

F:\gfortran\dan>gcc bb7.c -Wall -o indent.exe

F:\gfortran\dan>indent 5 ot3.txt
argv1 is 5
argv2 is ot3.txt


F:\gfortran\dan>type bb7.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>

int main(int argc, char *argv[])
{
int n;
FILE *fin, *fout;
char *path_exp, *exp_suffix = ".exp";
const char *path;
int c, nc;

path=argv[2];
if (argc != 3)
{
fprintf(stderr, "%s: <num> <filename>\n", argv[0]);
return EXIT_FAILURE;
}

, that I could just as well test for whether (argv[4] == NULL) to see
if someone entered too many parameters?
 
K

Keith Thompson

Frank said:
I haven't read the rest of this thread carefully, as it seems to deal
more with the source that our italian friend posted. That said, do I
understand you correctly that for the following code: [...]
int main(int argc, char *argv[])
{
int n;
FILE *fin, *fout;
char *path_exp, *exp_suffix = ".exp";
const char *path;
int c, nc;

path=argv[2];
if (argc != 3)
{
fprintf(stderr, "%s: <num> <filename>\n", argv[0]);
return EXIT_FAILURE;
}

, that I could just as well test for whether (argv[4] == NULL) to see
if someone entered too many parameters?

No, because if someone entered too few parameters, argv[4] won't even
exist.

In any case, checking the value of argc is much clearer, at least if
you want to require a specific number of arguments.

But if you want to traverse the arguments, starting at the first, then
checking for NULL is reasonable; you might not even need to look at
argc in that case.

For example, here's a program that's similar to the echo command
except that each argument is printed on a line by itself:

#include <stdio.h>

int main(int argc, char **argv) {
int i;
for (i = 1; argv != NULL; i ++) {
puts(argv);
}
return 0;
}
 
M

Morris Keesan

Because argv[argc] must be NULL.

I haven't read the rest of this thread carefully, as it seems to deal
more with the source that our italian friend posted. That said, do I
understand you correctly that for the following code: ....
int main(int argc, char *argv[])
{ ....
path=argv[2];
if (argc != 3)
{
fprintf(stderr, "%s: <num> <filename>\n", argv[0]);
return EXIT_FAILURE;
}

, that I could just as well test for whether (argv[4] == NULL) to see
if someone entered too many parameters?

No. (argc == 4) implies (argv[4] == NULL), but (argv[4] != NULL) doesn't
tell you anything about argc except that it's not 4. If (argc == 2), for
example, the value of argv[4] is undefined, and even evaluating argv[4]
could cause program failure. In the above code snippet, executing the
statement
path=argv[2]
could lead to program failure (e.g. a segmentation fault, or demons flying
out
of your nose) if (argc < 2).
 
B

Barry Schwarz

On Mon, 31 Aug 2009 17:41:26 -0700 (PDT), Frank

snip
F:\gfortran\dan>type bb7.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>

int main(int argc, char *argv[])
{
int n;
FILE *fin, *fout;
char *path_exp, *exp_suffix = ".exp";
const char *path;
int c, nc;

path=argv[2];
if (argc != 3)

If argc < 2, your test is too late. You have already invoked
undefined behavior.
{
fprintf(stderr, "%s: <num> <filename>\n", argv[0]);
return EXIT_FAILURE;
}

, that I could just as well test for whether (argv[4] == NULL) to see
if someone entered too many parameters?

If argc is 3, argv[4] does not exist and testing it would invoke
undefined behavior.
 
F

Frank

[...]


I haven't read the rest of this thread carefully, as it seems to deal
more with the source that our italian friend posted.  That said, do I
understand you correctly that for the following code: [...]
int main(int argc, char *argv[])
{
  int n;
  FILE *fin, *fout;
  char *path_exp, *exp_suffix = ".exp";
  const char *path;
  int   c, nc;
  path=argv[2];
  if (argc != 3)
  {
    fprintf(stderr, "%s: <num> <filename>\n", argv[0]);
    return EXIT_FAILURE;
  }
, that I could just as well test for whether (argv[4] == NULL) to see
if someone entered too many parameters?

No, because if someone entered too few parameters, argv[4] won't even
exist.

In any case, checking the value of argc is much clearer, at least if
you want to require a specific number of arguments.

But if you want to traverse the arguments, starting at the first, then
checking for NULL is reasonable; you might not even need to look at
argc in that case.

For example, here's a program that's similar to the echo command
except that each argument is printed on a line by itself:

#include <stdio.h>

int main(int argc, char **argv) {
    int i;
    for (i = 1; argv != NULL; i ++) {
        puts(argv);
    }
    return 0;


Thanks, Keith. I've altered my latest indent's output to reflect this
constructive criticism.

F:\gfortran\dan>gcc hh2.c -Wall -o indent.exe

F:\gfortran\dan>indent 3 ot3.txt
3
ot3.txt

F:\gfortran\dan>type hh2,c
The system cannot find the file specified.
Error occurred while processing: hh2.
The system cannot find the file specified.
Error occurred while processing: c.

F:\gfortran\dan>type hh2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>

int main(int argc, char *argv[])
{
int n;
FILE *fin, *fout;
char *path_exp, *exp_suffix = ".exp";
const char *path;
int c, nc;
char d;


if (argc != 3)
{
fprintf(stderr, "usage: \n");
fprintf(stderr, "%s <num> <filename>\n", argv[0]);
return EXIT_FAILURE;
}
path=argv[2];
errno = 0;
n = atoi(argv[1]);
if (n < 0 || n > 999 )
{
fprintf(stderr, "%s: give me a natural number"
" less than 1000\n", argv[0]);
return EXIT_FAILURE;
}
// if detab
if (n == 0)
{
fprintf(stderr, "this will detab your file\n");
fprintf(stderr, "If that's what you want, enter zero again\n");
fprintf(stderr, "To abort, enter anything else\n");
d = getchar();
if (d == '0')
fprintf(stderr, "detabbing %s \n", argv[2]);
else
{
fprintf(stderr, "Aborting");
return EXIT_FAILURE;
}
}
// Process path and open file
nc = strlen (path);
c = strlen (exp_suffix);
path_exp = malloc(c + nc + 1);
if (!path_exp)
{
fprintf(stderr, "malloc error: %s\n", __func__);
return EXIT_FAILURE;
}
strcat(strcpy(path_exp, path), exp_suffix);
fin = fopen(path, "r");
if (!fin)
{
fprintf(stderr, "%s %s\n", "fopen failed to open", path );
return EXIT_FAILURE;
}
// main control



// print some values
int i;
for (i = 1; argv != NULL; i ++) {
puts(argv);
}

fout = fopen(path_exp, "w");
if (!fout)
{
fprintf(stderr, "%s \n", "fout failed");
fclose(fin);
return EXIT_FAILURE;
}
fclose(fin);
fclose(fout);
free(path_exp);
return EXIT_SUCCESS;
}

// gcc hh2.c -Wall -o indent.exe

F:\gfortran\dan>

Is there a standard way of knowing whether

F:\gfortran\dan>type hh2,c
The system cannot find the file specified.
Error occurred while processing: hh2.
The system cannot find the file specified.
Error occurred while processing: c.

hh2,c already exists?
 
F

Frank

Because argv[argc] must be NULL.
I haven't read the rest of this thread carefully, as it seems to deal
more with the source that our italian friend posted.  That said, do I
understand you correctly that for the following code: ...
int main(int argc, char *argv[])
{ ...
  path=argv[2];
  if (argc != 3)
  {
    fprintf(stderr, "%s: <num> <filename>\n", argv[0]);
    return EXIT_FAILURE;
  }
, that I could just as well test for whether (argv[4] == NULL) to see
if someone entered too many parameters?

No.  (argc == 4) implies (argv[4] == NULL), but (argv[4] != NULL) doesn't
tell you anything about argc except that it's not 4.  If (argc == 2), for
example, the value of argv[4] is undefined, and even evaluating argv[4]
could cause program failure.  In the above code snippet, executing the  
statement
   path=argv[2]
could lead to program failure (e.g. a segmentation fault, or demons flying  
out
of your nose) if (argc < 2).

Then say argc less than four.
 
D

David Thompson

On Mon, 31 Aug 2009 22:26:04 -0700 (PDT), Frank
Is there a standard way of knowing whether

F:\gfortran\dan>type hh2,c
The system cannot find the file specified.
Error occurred while processing: hh2.
The system cannot find the file specified.
Error occurred while processing: c.

hh2,c already exists?

That command to Windows CMD doesn't even try to access a file named
hh2,c ; it looks for two separate files named hh2 and c , as the
errors show. If you really want a file of that name, use quotes:
X:\blah> type "hh2,c"

<ObC> In a program, the only portable way is to try fopen for read. If
that succeeds, the file must exist (or at least be considered to). But
if it fails, that doesn't mean the file doesn't exist; there are many
other things that can cause open failure. errno _might_ be set, and if
so it _might_ be set to something recognizable like ENOENT .
 

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,774
Messages
2,569,598
Members
45,151
Latest member
JaclynMarl
Top