[comp.unix.wizards] have I found a bug in K&R?

hubcap@hubcap.UUCP (Mike S Marshall) (09/29/87)

on page 110 of K&R it says:
  
    "...(argv) is a pointer to an array of character strings..."

but since the primary-expression operator [] has higher priority than
the unary operator *, then isn't it more correct to say that argv
(which is defined as char *argv[]) is an array of pointers to character
strings?

-Mike Marshall       hubcap@hubcap.clemson.edu        ...!hubcap!hubcap

guy%gorodish@Sun.COM (Guy Harris) (09/30/87)

(This is a C question, not a UNIX question.)

> on page 110 of K&R it says:
>   
>     "...(argv) is a pointer to an array of character strings..."
> 
> but since the primary-expression operator [] has higher priority than
> the unary operator *, then isn't it more correct to say that argv
> (which is defined as char *argv[]) is an array of pointers to character
> strings?

No, it is more correct to say that "argv" is a pointer to the first element of
an array of pointers to "char", which can be read as "'argv' is a pointer to
the first element of an array of pointers to character strings".  "argv" is
*properly* declared as "char **argv"; however, if you declare an array of type
X as an argument to a function, the compiler interprets that as a declaration
of a pointer to type X instead.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

kre@munnari.oz (Robert Elz) (09/30/87)

In article <517@hubcap.UUCP>, hubcap@hubcap.UUCP (Mike S Marshall) writes:
>     k&r: "...(argv) is a pointer to an array of character strings..."
> 
> isn't it more correct to say that argv is an array of pointers to character
> strings?

Not really, argv is a pointer to an array of pointers to null terminated
arrays of characters.

A pointer to a null terminated array of characters is what is colloquially
callyed a "character string" (eg: "abc" .. which is a pointer to a null
terminated array of characters).

So k&r's description is right.  That argv is a pointer, and not an array
is a crucial difference, if argv were an array, "argv++;" would be an
illegal statement.

kre

gwyn@brl-smoke.UUCP (10/01/87)

In article <517@hubcap.UUCP> hubcap@hubcap.UUCP (Mike S Marshall) writes:
>... (which is defined as char *argv[]) ...

Actually, due to a funny quirk in C, all correct declarations of the
second formal parameter to main() are equivalent to
	char **argv;
Whether you want to consider this as
	char *argv[];
or
	char (*argv)[];
doesn't really matter.

eao@anumb.UUCP (e.a.olson) (10/01/87)

> on page 110 of K&R it says:
>   
>     "...(argv) is a pointer to an array of character strings..."
> 
> but since the primary-expression operator [] has higher priority than
> the unary operator *, then isn't it more correct to say that argv
> (which is defined as char *argv[]) is an array of pointers to character
> strings?
> 
> -Mike Marshall       hubcap@hubcap.clemson.edu        ...!hubcap!hubcap

    The point is that in C, a 'character string' is referred to by
    a pointer to it.

sarah@laticorp.UUCP (Sarah Groves Hobart) (10/09/87)

In article <517@hubcap.UUCP>, hubcap@hubcap.UUCP (Mike S Marshall) writes:
> on page 110 of K&R it says:
>   
>     "...(argv) is a pointer to an array of character strings..."
> 
> but since the primary-expression operator [] has higher priority than
> the unary operator *, then isn't it more correct to say that argv
> (which is defined as char *argv[]) is an array of pointers to character
> strings?

What is actually being declared is a pointer to an array of pointers. 
Remember that the following declarations are equivalent:

char s[];

char *s;

Most people would tend to call the first declaration a character string, 
and the second a character pointer, but they are the same thing.  So K&R
are just using slightly different terminology, probably in the interests
of clarity.

An array of pointers to character strings would be a pointer to an array
of pointers to an array of pointers.

Sarah Groves Hobart
{ihnp4,amdahl,sun}!ptsfa!laticorp!sarah

sarah@laticorp.UUCP (Sarah Groves Hobart) (10/09/87)

In article <321@laticorp.UUCP>, sarah@laticorp.UUCP (Sarah Groves Hobart) writes:
> 
> An array of pointers to character strings would be a pointer to an array
> of pointers to an array of pointers.

Oops. . . .my fingers just wouldn't stop typing "array of pointers".  What
I meant was:  An array of pointers to character strings would be a pointer
to a bunch of pointers of type char.  I was trying to point out that 
"character string" is simply a pointer to a bunch of objects which
happen to be chars, as is an array of characters.

Sarah Groves Hobart
{ihnp4,amdahl,sun}!ptsfa!laticorp!sarah

crash@jc3b21.UUCP (Frank (Crash) Edwards) (10/11/87)

In article <321@laticorp.UUCP>, sarah@laticorp.UUCP (Sarah Groves Hobart) writes:
> What is actually being declared is a pointer to an array of pointers. 
> Remember that the following declarations are equivalent:
> 
> char s[];
> 
> char *s;
> 
> Most people would tend to call the first declaration a character string, 
> and the second a character pointer, but they are the same thing.  So K&R
> are just using slightly different terminology, probably in the interests
> of clarity.
> 
> Sarah Groves Hobart
> {ihnp4,amdahl,sun}!ptsfa!laticorp!sarah

They are the same but they are not the same!  Using the strategy above,
try the following:

----------

foo.c:

char	s[] = "This is the character array.";

bar.c:

extern	char	*s;

----------

There is a subtle but startling difference between the two; primarily in the
storage allocated for each.  The declaration in "foo.c" defines storage for
the string and sets the array variable s to that address.  The extern decl-
aration in file "bar.c" indicates that s is an n-byte pointer to the string.
This means that to print the string, say, the compiler finds the address of
s and the code generated loads a pointer value from it, whereas for a true
array the compiler simply uses the value of s at compile time.

Perhaps this is obvious to most ;-) but it wasn't to me.  My particular
run-in with this type of coding was more devious because I declared an array
of character strings (similar to argv...) and then referenced them as "ptr
to ptr to char".

I should note that there won't be a problem if these constructs are used
within function calls since the array reference generates an address (for
case #1), and the compiler code loads the ptr from address s (for case #2).
And so either way a parameter declared as "char *s" would work.  This problem
only crops up when global declarations are used.

Anyway, I hope the above is intelligible.  If not, oh well.

Frank (Crash) Edwards

stpeters@dawn.steinmetz (Dick St.Peters) (10/12/87)

In article <321@laticorp.UUCP> sarah@laticorp.UUCP (Sarah Groves Hobart) writes:
>Remember that the following declarations are equivalent:
>
>char s[];
>
>char *s;

Actually, they're not.  The difference is like that between a constant
and a variable: you can change the value of s (the address it points
to) if you declare it as a pointer [variable], but you can't if you
declare it as an array [constant].  Consider what "&s" means in the
two cases.



--
Dick St.Peters                        
GE Corporate R&D, Schenectady, NY
stpeters@ge-crd.arpa              
uunet!steinmetz!stpeters

tim@amdcad.AMD.COM (Tim Olson) (10/13/87)

In article <7608@steinmetz.steinmetz.UUCP> dawn!stpeters@steinmetz.UUCP (Dick St.Peters) writes:
| In article <321@laticorp.UUCP> sarah@laticorp.UUCP (Sarah Groves Hobart) writes:
| >Remember that the following declarations are equivalent:
| >
| >char s[];
| >
| >char *s;
| 
| Actually, they're not.  The difference is like that between a constant
| and a variable: you can change the value of s (the address it points
| to) if you declare it as a pointer [variable], but you can't if you
| declare it as an array [constant].  Consider what "&s" means in the
| two cases.

Actually, you are both right.  There are two places where a declaration
of an array with no size (and no initializer) are legal: 1) as a formal
parameter, and 2) as an external array declaration, where the array is
defined elsewhere.

As a formal parameter, char s[] and char *s are equivalent -- the
compiler coerces the former into the later.  In that case you *can*
change s.  As an external array declartion, the two are not equivalent.

	-- Tim Olson
	Advanced Micro Devices
	(tim@amdcad.amd.com)

sarah@laticorp.UUCP (Sarah Groves Hobart) (10/13/87)

In article <7608@steinmetz.steinmetz.UUCP>, stpeters@dawn.steinmetz (Dick St.Peters) writes:
> In article <321@laticorp.UUCP> sarah@laticorp.UUCP (Sarah Groves Hobart) writes:
> >Remember that the following declarations are equivalent:
> >
> >char s[];
> >
> >char *s;
> 
> Actually, they're not.  The difference is like that between a constant
> and a variable: you can change the value of s (the address it points
> to) if you declare it as a pointer [variable], but you can't if you
> declare it as an array [constant].  Consider what "&s" means in the
> two cases.
> 
> 
> 

Right.  An array name IS a constant, so something like:

   ptr = &array_name;

is illegal.  I was referring to their equivalence as formal parameters
in a function definition.  A function that has been passed an array name
can believe it has either an array or a pointer and work with it
accordingly.

It is, however, legal to pass a function something of the form &a[0], or
even &a[5].  This is useful if you only want to pass part of a larger
array.

Interested parties are referred to K&R pp. 94-96.

Sarah Groves Hobart
{ihnp4,amdahl,sun}!ptsfa!laticorp!sarah

stpeters@dawn.steinmetz (Dick St.Peters) (10/15/87)

In article <324@laticorp.UUCP> sarah@laticorp.UUCP (Sarah Groves Hobart) writes:
>Right.  An array name IS a constant, so something like:
>
>   ptr = &array_name;
>
>is illegal.  I was referring to their equivalence as formal parameters
>in a function definition.

Ok, but isn't the non-difference in this case obvious to anyone who
knows what a "formal parameter" is?

As someone who has spent many hours trying to introduce to C people
who are basically non-programmers but must do a little programming, is
there any way I can get you folks to stop using the term "formal
parameter" when "function argument" or "procedure argument" will do as
well?  I am tired of translating and being asked what an informal
parameter is.
--
Dick St.Peters                        
GE Corporate R&D, Schenectady, NY
stpeters@ge-crd.arpa              
uunet!steinmetz!stpeters

cameron@elecvax.eecs.unsw.oz (Cameron Simpson) (10/16/87)

In article <18668@amdcad.AMD.COM> tim@amdcad.AMD.COM (Tim Olson) writes:
>In article <7608@steinmetz.steinmetz.UUCP> dawn!stpeters@steinmetz.UUCP (Dick St.Peters) writes:
>| In article <321@laticorp.UUCP> sarah@laticorp.UUCP (Sarah Groves Hobart) writes:
>| >Remember that the following declarations are equivalent:
>| >char s[];
>| >char *s;
>| Actually, they're not. [ explaination deleted ]
>Actually, you are both right.  There are two places where a declaration
>of an array with no size (and no initializer) are legal: 1) as a formal
>parameter, and 2) as an external array declaration, where the array is
>defined elsewhere.
>As a formal parameter, char s[] and char *s are equivalent -- the
>compiler coerces the former into the later.  In that case you *can*
>change s.  As an external array declartion, the two are not equivalent.
>	-- Tim Olson
>	Advanced Micro Devices
>	(tim@amdcad.amd.com)

I've been following this for a while. In all this discussion I have not
once seen a word which crops up several times in K&R, namely `lvalue'.
An lvalue is something which may occur on the left-hand side of an
assignment statement. If I say
	char s[];	/* a character array of unspecified size */
	char *t;	/* a pointer to a character */
then I may say `t++;' or `t = s;' or `t = &s[2];' and so on.
I may not say `s++;' or `s = t;'.
This applies whether they are declared as formal parameters or as
external array declarations. I often declare functions like
	fn(a,b)
	char *a;
	char b[];
in order to *prevent* myself from changing b during the function.
It eliminates some coding errors by making the compiler do some
extra checking for me.
	- Cameron Simpson, Uni of NSW, Australia

chris@mimsy.UUCP (Chris Torek) (10/16/87)

In article <7639@steinmetz.steinmetz.UUCP> stpeters@dawn.steinmetz
(Dick St.Peters) writes:
>As someone who has spent many hours trying to introduce to C people
>who are basically non-programmers but must do a little programming, is
>there any way I can get you folks to stop using the term "formal
>parameter" when "function argument" or "procedure argument" will do as
>well?

It is rare that `function argument' will do as well as `formal
parameter'.  `Function argument' can also mean `actual parameter',
and formal-parameter pointer/array conversions are not performed
on actual parameters.

(Jargon *does* have its uses, and every occupation has its jargon.
Perhaps the first thing you should introduce is the jargon.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

guy%gorodish@Sun.COM (Guy Harris) (10/18/87)

> This applies whether they are declared as formal parameters or as
> external array declarations. I often declare functions like
> 	fn(a,b)
> 	char *a;
> 	char b[];
> in order to *prevent* myself from changing b during the function.
> It eliminates some coding errors by making the compiler do some
> extra checking for me.

Umm, guess again.  K&R says, in 10.1 "External function definitions":

	...Also, since a reference to an array in any context (in particular as
	an actual parameter) is taken to mean a pointer to the first element of
	the array, declarations of formal parameters declared ``array of ...''
	are adjusted to read ``pointer to ...'''.

Which means that the compiler will treat the declarations of "a" *and* "b" as
declarations of pointers.  Furthermore, PCC, at least, works this way, so most
PCC-based compilers aren't doing any extra checking for you.
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

chris@mimsy.UUCP (Chris Torek) (10/18/87)

(This discussion should have been in comp.lang.c; followups have
been directed there.)

In article <3802@elecvax.eecs.unsw.oz> cameron@elecvax.eecs.unsw.oz
(Cameron Simpson) writes:
>... An lvalue is something which may occur on the left-hand side of an
>assignment statement.  If I say
>	char s[];	/* a character array of unspecified size */
>	char *t;	/* a pointer to a character */
>then I may say `t++;' or `t = s;' or `t = &s[2];' and so on.
>I may not say `s++;' or `s = t;'.

True enough, except . . . .

>This applies whether they are declared as formal parameters or as
>external array declarations. I often declare functions like
>	fn(a,b)
>	char *a;
>	char b[];
>in order to *prevent* myself from changing b during the function.

. . . this does not work in (some? many? most? all?) compilers.
PCC is perfectly happy with `b = a' inside the code for `fn'.
I am not sure whether the draft has anything to say on the subject.

Incidentally, with the addition of `const', ANSI now has the
concept of `modifiable lvalues', so that in

	const int n = 4;

`n' is an lvalue, but not a modifiable lvalue.  (At least, I think
this is how it works---there are odd possibilities like

	const volatile int *clockaddr = (int *)CLOCK_ADDRESS;

that I am still not sure about.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@brl-smoke.ARPA (Doug Gwyn ) (10/19/87)

In article <7639@steinmetz.steinmetz.UUCP> stpeters@dawn.UUCP (Dick St.Peters) writes:
>In article <324@laticorp.UUCP> sarah@laticorp.UUCP (Sarah Groves Hobart) writes:
>>I was referring to their equivalence as formal parameters
>>in a function definition.
>Ok, but isn't the non-difference in this case obvious to anyone who
>knows what a "formal parameter" is?

Obviously not; the original question concerned the declaration for
the formal parameter `argv', but some people then piped up saying
that * and [] were not equivalent, which they in fact are in such
usage.

>As someone who has spent many hours trying to introduce to C people
>who are basically non-programmers but must do a little programming, is
>there any way I can get you folks to stop using the term "formal
>parameter" when "function argument" or "procedure argument" will do as
>well?

No, because it is incorrect to call a formal parameter an `argument'.
The argument is the value that is actually supplied when the function
is invoked, and this has very little to do with the definition of the
function itself, for which the formal parameters are quite significant.

If you think that naive programmers will be less confused were we to
use less precise terminology, well, that is not confirmed in my
experience.

stpeters@dawn.steinmetz (Dick St.Peters) (10/26/87)

In article <6585@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>If you think that naive programmers will be less confused were we to
>use less precise terminology, well, that is not confirmed in my
>experience.

It is my experience that people are less confused by terminology they
understand.

Jargon can turn a generally correct statement into a precise one, but
to a naive listener/reader, it can turn the statement into a
meaningless one.  I would rather be imprecise than incomprehensible.

This particular piece of jargon ("formal parameters") is a sore point
because I get asked about it so much.

However, I obviously chose a poor place to raise the issue, and I'm
sorry about that.  Not many naive programmers read comp.unix.wizards.
--
Dick St.Peters                        
GE Corporate R&D, Schenectady, NY
stpeters@ge-crd.arpa              
uunet!steinmetz!stpeters