[comp.lang.c] static char

dhesi@bsu-cs.UUCP (06/12/87)

The April 1987 issue of DEC Professional magazine has this interesting 
example of possible obfuscation in C.

What is the meaning of the following declaration?

     static char (*b)[6];

The compiler and lint checker used by the magazine columnist seem to
treat the declaration of b as equivalent to char ** but the following
strange things happen.

main()
{
     static char (*b)[6];	/* an unusual declaration */
     static char a[] = "Hello";
     char *c = a;
     char **d = &c;
     b = d;			/* So b == d ... Watch. */

     printf ("%s\n", *d);	/* prints "Hello" */
     printf ("%s\n", *b);	/* prints 0 */
}

Other code fragments omitted.  The columnist continues, "...This means
that *b is a constant and while (b) can be modified, *b never will
change its original value.  Thus, setting b = d does not mean that 
*b == *d.  Also, while *b is treated as a constant (unchangeable), the
value of b can vary."

He goes on to ask if other C compiles do the same thing, and wonders
if Ritchie had this in mind when he defined the language.

On our 4.3BSD system, the above code printed the character sequence
"Hello\n^H^T\n" (without quotes).  On my UNIX PC (software revision 3.0) 
it printed "Hello\n\n".
-- 
Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

davew@whuxm.UUCP (WONNACOTT) (06/12/87)

In article <761@bsu-cs.UUCP>, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
> The April 1987 issue of DEC Professional magazine has this interesting 
> example of possible obfuscation in C.
> 
> What is the meaning of the following declaration?
> 
>      static char (*b)[6];
> 

Parenthesis can be used in declarations to alter the meaning of the
declaration.  Baiscly, you read the part of the declaration in
parenthesis first, followed by whats outside of the parenthesis.
For example:

	char *argv[6];	/* [] has higher precedence than *, so
 			   argv is an array of 6 pointers to char */
	char (*b)[6];	/* now the * is in parenthesis, so we read the * first
			   b is a pointer to an array of 6 chars  */

	char *gets();	/* () has higher precedence than *, so
			   gets is a function returning pointer to char */
	char (*fp)();	/* now the * is in parethesis, so we read it first
			   fp is a pointer to a function returning char */


					The compiler and lint seem to
> treat the declaration of b as equivalent to char ** but the following
> strange things happen.

"seem to" is right -- just as char *ptr and char array[6] "seem to" be
the same to many c novices -- but ptr allocates a pointer and array
allocates space for 6 characters.

> 
> main()
> {
>      static char (*b)[6];	/* an unusual declaration */

b should be used to point to an array of 6 characters.

>      static char a[] = "Hello";
>      char *c = a;
>      char **d = &c;
>      b = d;			/* So b == d ... Watch. */

b is used here to point to a pointer to a character -- not to an
array of 6 characters, as it should be used.  Perhaps C compilers
should catch this sort of abuse of pointers...

	b = &a;

would be better in some sense, but most C compilers assume that you
really meant &a[0] if you write &a, and ignore the &, and you get

	b = a;

instead, which is an illegal pointer combination, so you have to
put in an explicit cast:

	b = (char (*)[6]) a;

which works (it casts a from type char * into the type of b: pointer to
an arrya of 6 chars).  Many people find the above code (mine and the
original) too ugly to read, and take to using typedef to clarify the
situation (a vary good idea, in my opinion):

	typedef char (* pointer_to_array_of_6_chars)[6];

	static pointer_to_array_of_6_chars b;

	b = (pointer_to_array_of_6_chars) a;

In any case, back to what happened in the original and why:

> 
>      printf ("%s\n", *d);	/* prints "Hello" */

yes -- d is a pointer to a pointer to a character, so *d is a pointer to
a character (the H in Hello, to be specific)

>      printf ("%s\n", *b);	/* prints 0 */

The variable b is a pointer to an array of 6 chars, so *b is an array of 6
bytes starting where b points (that is, at the address of c).  Arrays in C
are passed by passing the address of the first element of the array.  So --
since *b is an array of 6 characters starting with the first byte of the
pointer c, C will pass the address of the variable c, not the address of
the variable a.

	b------b	c------c	a------a
	|      |      / |      |<--	|  H   |
	|      |      | |      |  |	|  e   |
	|      |----->| |      |--+---->|  l   |
	|      |      | |      |  |	|  l   |
	b------b      | c------c  |	|  o   |
		      | d------d  |	|  \0  |
		      | |      |  |	a------a
		      \ |      |  |
			|      |--'
			|      |
			d------d

I've done my best to draw this with the limited capabilities of an ASCII
terminal: Assuming 4 byte pointers, and that c and d happen to be contiguous
on the stack.  Try to look at the above paragraph and the diagram at the
same time (they'll both fit on the screen together).

b and d point to the same address -- the beginning of c.  But *d is a character
pointer located there, and *b is a 6 byte array located there.  *d (which == c)
will print the string "Hello".  *b (the array of 6 bytes encompassing the
variable c and some of the bytes of d), will not print anything sensible.

You would get the same printout from

	printf("%s\n", &c);

> 
> Other code fragments omitted.  The columnist continues, "...This means
> that *b is a constant and while (b) can be modified, *b never will
> change its original value.

just as you cannot assign a new value to an array name.

>                            Thus, setting b = d does not mean that 
> *b == *d.  Also, while *b is treated as a constant (unchangeable), the
> value of b can vary."

note that the above is true in other cases as well:

	int *b;

	float f = 1.234;
	float *d = &f;

	b = d;		/* illegal on modern compilers without a cast,
 			   but similar to the example above in spirit */

	/* in this case, *b != *d */

> 
> He goes on to ask if other C compiles do the same thing, and wonders
> if Ritchie had this in mind when he defined the language.

Other C compilers do the same thing -- its consistent with the C treatment
of arrays.  The trick to understanding it is to remember that b is a
pointer to an array of 6 characters, not a pointer to a pointer.

> 
> -- 
> Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

I hope this has helped the original poster and any others who didn't
understand what was going on in the original article.

-- 
David Wonnacott			"They said Van Gogh was crazy, didn't they?"
whuxm!davew  or  rz3bb!davew
AT&T Corporate Education
The above opinions are not necessarily those of AT&T, or of anyone else anywhere

rjnoe@uniq.UUCP (Roger J. Noe) (06/12/87)

In article <761@bsu-cs.UUCP>, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
> What is the meaning of the following declaration?
>      static char (*b)[6];
> The compiler and lint checker used by the magazine columnist seem to
> treat the declaration of b as equivalent to char ** . . .

But it's obviously NOT equivalent.  You could make a pretty good case
that lint (if not the compiler as well) should complain about the "b = d;"
pointer assignment statement, but that does not excuse the programmer
from understanding the difference between b and d.  (Try passing b to a
function which expects a "char **" argument and see what happens.  You
should see lint complain about a parameter inconsistency and it should
compile and execute correctly.)

As for the example program:
> main()
> {
>      static char (*b)[6];	/* an unusual declaration */
>      static char a[] = "Hello";
>      char *c = a;
>      char **d = &c;
>      b = d;			/* So b == d ... Watch. */
> 
>      printf ("%s\n", *d);	/* prints "Hello" */
>      printf ("%s\n", *b);	/* prints 0 */
> }

change the final printf() call to "printf("%s\n", *(char **)b);" and
everything should work just as you'd expect.  If b is not "char **" and
you dereference it, you're not going to get a "char *", which is what
you'd need to see this do what is desired.

> . . . .  The columnist continues, "...This means
> that *b is a constant and while (b) can be modified, *b never will
> change its original value.  Thus, setting b = d does not mean that 
> *b == *d.  Also, while *b is treated as a constant (unchangeable), the
> value of b can vary."

Incorrect.  The values of b and d are identical, but the programmer has
told the compiler that they do not point to the same types.  The use of
the static (internal) storage class has much less to do with what is
going on here.  All that it does is prevent b from being automatic while
still restricting its scope to main().  The problem is that "*b", as the
program was written, denotes an aggregate array of 6 characters, not a
pointer to them.  The array cannot be passed as a function parameter.

> He goes on to ask if other C compiles do the same thing, and wonders
> if Ritchie had this in mind when he defined the language.

Yes, I suspect most compilers will do the same thing.  I think a C compiler
that worked as the columnist seemed to anticipate would have to be a broken
compiler, or it implements something other than C language.  As far as I
can tell the compiler, lint, and DMR are all correct as far as this issue
is concerned.

I think the referenced publication should look for a programmer who knows
his/her/its C better than this one.  Or maybe change their name to
"Unprofessional".
--
long tloc = 507314353L;
	Roger Noe			ihnp4!uniq!rjnoe
	Uniq Digital Technologies	rjnoe@uniq.UUCP
	28 South Water Street		+1 312 879 1566
	Batavia, Illinois  60510	41:50:56 N.  88:18:35-

decot@hpisod2.UUCP (06/13/87)

> What is the meaning of the following declaration?
> 
>      static char (*b)[6];

b has static storage and is a pointer to a (pseudo-named) array of
6 characters (not to the first character of an array, mind you, but
to something which may be used in any context where a name of an
array is allowed).  Initially, b is a null-pointer.  sizeof(*b) is
6*sizeof(char).  For instance, ++b increases the pointer address by
six characters.  *b is equivalent to the name of a particular array,
and since it has no initial value, it never will.

> The compiler and lint checker used by the magazine columnist seem to
> treat the declaration of b as equivalent to char ** but the following
> strange things happen.

Not really, since sizeof(*b) is not sizeof(char *), although (*b)[0] is
a character.

> main()
> {
>      static char (*b)[6];	/* an unusual declaration */
>      static char a[] = "Hello";
>      char *c = a;
>      char **d = &c;
>      b = d;			/* So b == d ... Watch. */

This statement should have encountered an error, since b and d are not
of compatible types.

>      printf ("%s\n", *d);	/* prints "Hello" */
>      printf ("%s\n", *b);	/* prints 0 */
> }

> Other code fragments omitted.  The columnist continues, "...This means
> that *b is a constant and while (b) can be modified, *b never will
> change its original value.  Thus, setting b = d does not mean that 
> *b == *d.  Also, while *b is treated as a constant (unchangeable), the
> value of b can vary."

This is accurate.

Dave Decot
hpda!decot

msb@sq.UUCP (06/14/87)

Rahul Dhesi (dhesi%bsu-cs@iuvax.UUCP) writes:
> What is the meaning of the following declaration?
> 
>      static char (*b)[6];

b is what is commonly called a "pointer to array of 6 chars", but better
described as "pointer to arrays of 6 chars" (each).  b is static.

When C sees a function definition beginning like this:

	func(x, b)
	char x[], b[][6];
	{

the declaration is translated* (only because these are function parameters
and therefore not allowed to really be arrays) to

	char *x, (*b)[6];

The object you describe is of the same type, but static.

> The compiler and lint checker used by the magazine columnist seem to
> treat the declaration of b as equivalent to char **

which is isn't.

> main()
> {
>      static char (*b)[6];	/* an unusual declaration */
>      static char a[] = "Hello";
>      char *c = a;
>      char **d = &c;

So far so good, c and *d are both pointers-to-character (or as I prefer,
pointers-to-characters) indicating the H in "Hello".

>      b = d;			/* So b == d ... Watch. */

The two sides are incompatible pointer types, so there really should be
a cast here, but I'll get back to that later.  Anyway, what this
is saying is that the value in d, which is the address of the pointer c,
is to be stuffed into b.  Well, what happens when you then try *b?  It treats
the 6 bytes beginning at the variable c as if they were an array of characters!

No wonder different values are printed on different machines.  What the 
coder presumably meant is:

	b = *d;

NOW *b and *d are pointing to the same place -- but to a different number
of characters.  *d points to the H, *b to the whole array "Hello".
Further indexing through either pointer does produce a char, so they
are similar in that respect, but not identical just the same.

Remember that *x is the same as x[0], and consider:

	d		is a pointer to pointer to char(s) which points to c
	d[0]		is a pointer to char(s), equal to c
	d[0][0]		is the char 'H', same as c[0]
	d[0][1]		is the char 'e', same as c[1]
	d[1]		would be garbage obtained by treating the memory
			location following d as a pointer

	b		is a pointer to array(s) of 6 chars
	b[0]		is an array of 6 chars containing "Hello"
	b[0][0]		is the char 'H'
	b[0][1]		is the char 'e'
	b[1]		would be garbage obtained by treating the memory
			location following the "Hello" bytes as an array
	b[1][0]		would be the char after the closing null in "Hello"
			(if there is one, rather then running off memory)

The printf of *b is legitimate, or would be if b had a reasonable value;
since *b has an array-of-chars type it is converted to pointer-to-char(s)
on being passed to a function.

Now about that cast.  In ANSI C as currently drafted, it is a requirement
that assignments involving pointer conversion be explicitly specified by
cast.  (That is, the "(type)" syntax.)  Some modern compilers at least
warn about such assignments now.  Here, *d has type "pointer to char",
while b is what I said above; therefore b = *d involves a conversion and
should really be written

	b = (char (*)[6]) *d;

> Other code fragments omitted.  The columnist continues, "...This means
> that *b is a constant and while (b) can be modified, *b never will
> change its original value.  Thus, setting b = d does not mean that 
> *b == *d.  Also, while *b is treated as a constant (unchangeable), the
> value of b can vary."

This starts to make no sense.  It is true that for a particular value of
b, it is impossible to assign to *b, but only because *b has array type.
(Assigning to *d would be assigning to c, on the other hand).  But it is
certainly possible to assign to b again, or to **b while b is held constant.
If the columnist is in the habit of confusing d and *d, I would suggest that
the column probably isn't worth reading.  (But I haven't seen it myself).

Mark Brader, SoftQuad Inc., Toronto, utzoo!sq!msb
#define	MSB(type)	(~(((unsigned type)-1)>>1))

*For my opinions on this alleged syntactic sugar, see Karl Heuer's article.
 I agree with him that it should be eliminated in the new syntax.
 But that's another story.

guy@sun.UUCP (06/14/87)

> >      b = d;			/* So b == d ... Watch. */
>
> This statement should have encountered an error, since b and d are not
> of compatible types.

In fact, if you fix a bug in the type-checking code, PCC will issue
an error for this.  The compiler running on my machine (basically the
SunOS 3.2 compiler) prints

	"foo.c", line 7: warning: illegal pointer combination

The problem is that the routine "chkpun" in "mip/trees.c" is
extremely bogus.  It certainly checks for something, but that
something certainly isn't any reasonable form of type-equality - for
one thing, it treats pointers and arrays as equivalent.  (And no,
that isn't necessary; array-valued expressions are converted to
pointer-valued expressions, according to the ANSI C standard, and
this isn't too bad a description of what C does according to K&R.  By
the time you get to "chkpun", the conversion has already been
performed, so you don't even have to treat them as equivalent at the
outermost level of the declaration; treating them as equivalent at
any other level is, of course, completely wrong.)

Here's the fix.  The "diff -c" comes from our compiler, so I don't
guarantee that the fix applies exactly as is to other flavors of PCC
(or PCC2, QCC, RCC, etc.), but it shouldn't be too far off.  Note
that there are zillions of other similar things wrong with the PCC
front end (many of which are still wrong in PCC's successors);
properly checking for errors and properly reporting errors didn't
seem to be considered important, which I guess isn't too surprising
since PCC was an experiment with code-generation technology, not
parsing technology.

*** /tmp/geta5443	Sun Jun 14 13:31:34 1987
--- /tmp/getb5443	Sun Jun 14 13:31:39 1987
***************
*** 717,747 ****
  			}
  		}
  	else {
! 		d1 = p->in.left->fn.cdim;
! 		d2 = p->in.right->fn.cdim;
! 		for( ;; ){
! 			if( t1 == t2 ) {;
! 				if( p->in.left->fn.csiz != p->in.right->fn.csiz ) {
! 					werror( "illegal structure pointer combination" );
! 					}
! 				return;
  				}
! 			if( ISARY(t1) || ISPTR(t1) ){
! 				if( !ISARY(t2) && !ISPTR(t2) ) break;
! 				if( ISARY(t1) && ISARY(t2) && dimtab[d1] != dimtab[d2] ){
! 					werror( "illegal array size combination" );
! 					return;
  					}
! 				if( ISARY(t1) ) ++d1;
! 				if( ISARY(t2) ) ++d2;
  				}
- 			else break;
- 			t1 = DECREF(t1);
- 			t2 = DECREF(t2);
  			}
! 		werror( "illegal pointer combination" );
  		}
- 
  	}
  
  #ifdef VAX
--- 711,748 ----
  			}
  		}
  	else {
! 		/*
! 		 * type is "ptr to (...)" or "array of (...)"; walk through
! 		 * the type constructors in the TWORD and check dimensions
! 		 * of any array parts.  They have to be the same in order
! 		 * for the types to be equivalent.
! 		 *
! 		 * NOTE: this code assumes that a reference to an item of
! 		 * array type has already mapped into a value of pointer
! 		 * type.  Also, note that within a composite type constructor,
! 		 * PTR and ARY are NOT equivalent.
! 		 */
! 		if( t1 == t2 ) {
! 			if( p->in.left->fn.csiz != p->in.right->fn.csiz ) {
! 				werror( "illegal structure pointer combination" );
  				}
! 			d1 = p->in.left->fn.cdim;
! 			d2 = p->in.right->fn.cdim;
! 			for( ;; ){
! 				if( ISARY(t1) ){
! 					if( dimtab[d1] != dimtab[d2] ){
! 						werror( "illegal array size combination" );
! 						return;
! 						}
! 					++d1;
! 					++d2;
  					}
! 				else if( !ISPTR(t1) ) break;
! 				t1 = DECREF(t1);
  				}
  			}
! 		else werror( "illegal pointer combination" );
  		}
  	}
  
  #ifdef VAX

-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

daveb@geac.UUCP (Dave Brown) (06/15/87)

In article <2550018@hpisod2.HP.COM> decot@hpisod2.UUCP writes:
>> What is the meaning of the following declaration?
>> 
>>      static char (*b)[6];
>

An old-fashioned trick for reading C declarations is to read the 
implementation out in words, from the inside out:

	there is a word called b 
		which points to
		an array of 6
		characters,
		and is static.

Reading it left-to-right usually causes confusion:

	a static char star b of 6 ... uh... er... Huh?

 --dave

karl@haddock.UUCP (Karl Heuer) (06/15/87)

>In article <761@bsu-cs.UUCP>, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
>>[after declaring char (*b)[6]; char **d;]
>>      b = d;			/* So b == d ... Watch. */

In article <535@whuxm.UUCP> davew@whuxm.UUCP (WONNACOTT) writes:
>[explains why this is a type mismatch, then says]
>	b = &a;
>would be better in some sense, but most C compilers assume that you
>really meant &a[0] if you write &a, and ignore the & ...

Fortunately, X3J11 has recognized this translation as bogus.  In ANSI C, "&a"
means what it says: the address of "a" (with type "pointer to one or more
arrays", not "pointer to first element of array").

(A side effect of the bogus translation is that, given char a[M][N], "&a[i]"
doesn't work in most pre-ANSI compilers -- you must instead write "a+i".)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

throopw@dg_rtp.UUCP (06/16/87)

> dhesi@bsu-cs.UUCP (Rahul Dhesi)

Others have already responded, but I'll throw in a slightly different
viewpoint, covering the same material from my own perspective, under the
principle that "You can't explain too much about pointers and arrays in
C."

> What is the meaning of the following declaration?
>      static char (*b)[6];

Static pointer to an array of six characters.  The same memory layout
(though *not* the same type) can be had by the declarations:

        char q[6];
        static char *r = q;

The b from the first example and the r from the second are pointing at
memory which has the same layout, though operations (such as indirection
or arithmetic) on these two pointers (a and r) will give very different
results.  Note that the array of characters in the first example does
*not* have static scope (and is in fact not even allocated).  The
pointer b has static scope.

> main()
> {
>      static char (*b)[6];	/* an unusual declaration */
>      static char a[] = "Hello";
>      char *c = a;
>      char **d = &c;
>      b = d;			/* So b == d ... Watch. */
>      printf ("%s\n", *d);	/* prints "Hello" */
>      printf ("%s\n", *b);	/* prints 0 */
> }
> 
> Other code fragments omitted.  The columnist continues, "...This means
> that *b is a constant and while (b) can be modified, *b never will
> change its original value.  Thus, setting b = d does not mean that 
> *b == *d.  Also, while *b is treated as a constant (unchangeable), the
> value of b can vary."

The part of the first quoted sentence after the "thus" is correct, but
the rest of the quoted material is flat wrong.  First, let's see why
what follows the "thus" is correct.  b and d point to different types,
similar to the situation where b is of type (float *) and d is of type
(int *).  Let's say that we have this code fragment:

        { float *b; int i=1, *d = &i; b = d;  ... }

Now, at the point of the omitted code indicated by "..." above, can we
be sure that *b == *d, just because we set b = d?  No, we cannot.  A
similar situation exists where

        { char (*b)[6], *p = "str", **d = &p; b = d;  ... }

This says that b points to an array, while d points to a pointer.
References to *b, the array pointed to by b, attempt to interpret the
pointer that d points to as an array of characters.

Next, let's see why the rest is bogus.  First of all, *b is not a
constant.  It is true that cannot be assigned to, but that does *not*
mean that it is a constant.  It is, according to draft X3J11, an
unmodifiable lvalue, but it is *not* a constant.  Second, it is true
that b can be modified, but it is *not* true that *b will never change
its original value.  In fact, the value of *b will (potentially) change
whenever the value of b changes.  (It might not change when b changes...
b might point to an identical array, for example.)

It all comes from thinking that pointers and arrays are the same thing,
which they most emphatically are *NOT*.  A local typechecker had this to
say about the above example:

        8 Use of undeclared identifier "printf"
        7 inconsistent types discovered
            (=
                (:IDENTIFIER b :STATIC ... )
                (:IDENTIFIER d :AUTO ... ))
            Types are:
              (:POINTER_TO (:ARRAY_OF (:CHAR) 6 ()))
            and:
              (:POINTER_TO (:POINTER_TO (:CHAR)))
        1 missing return statement at bottom of function
              (:IDENTIFIER main :EXTERN ... )
        3 errors

While lint had this to say:

        (10)  warning: main() returns random value to invocation environment
        function returns value which is always ignored: printf

We can see that both tools caught the fact that main didn't return
anything, and both mentioned something about printf (though they are not
really catching quite the same problem).  The most glaring point is that
lint totally missed the big error in this program, the assigning of a
pointer to a pointer onto a variable of incompatible type.  See Guy
Harris's posting in this subject line for a description of why lint
doesn't catch this bug.

The moral: tools don't always know what they purport to know, but it
helps to run a variety of checking tools early and often.

(Many folks disagree with this moral, of course...)

An alternative way to fix the example to make it type-correct under
draft X3J11:

        #include <stdio.h>
        main()
        {
             static char a[] = "Hello";
             char (*b)[6], *c, **d;
             b = &a;
             c = a;
             d = &c;
             /* both of these should now print "Hello" */
             printf ("%s\n", *d);
             printf ("%s\n", *b);
             return( 0 );
        }

Sadly, many current tools, including lint and some PCCs, will complain
or botch the assignment to b, or taking the address of a, or both.
--
You can't put too much coolant in a nuclear reactor.
                        --- Ed Asner in a sketch on SNL
-- 
Wayne Throop      <the-known-world>!mcnc!rti!dg_rtp!throopw

mick@auspyr.UUCP (06/16/87)

>> In article <761@bsu-cs.UUCP>, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
	Rahul started this all off. If you are not familiar with it,
	hit 'n' now.

	Here is one response:

>in article <304@uniq.UUCP>, rjnoe@uniq.UUCP (Roger J. Noe) says:
> still restricting its scope to main().  The problem is that "*b", as the
> program was written, denotes an aggregate array of 6 characters, not a
> pointer to them.  The array cannot be passed as a function parameter.
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
			Think again, squire.

> I think the referenced publication should look for a programmer who knows
> his/her/its C better than this one.  Or maybe change their name to
> "Unprofessional".
> 	Roger Noe			ihnp4!uniq!rjnoe
> 	Uniq Digital Technologies	rjnoe@uniq.UUCP

I read the original article in April DEC Professional magazine, and thought
it was an excellent discussion of C pointers.  It was a little terse at times,
but if you didn't understand it the first time, then re-read it until you do.

Now then, Roger, did you read the article, or are you taking the clippings
in the original posting out of context?

When I saw Rahul's posting, I knew we were in for another round of 
"leap before you look postings" (isn't it wonderful what you read in this
newsgroup?), but I just couldn't let this one pass by.

Recommendations:
	1)  (to all) Read the article if you can get a copy.
	2)  (to Roger) Consider a name change.

-- 
----
Mick
	{sdencore,necntc,cbosgd,amdahl,ptsfa,dana}!aussjo!mick
	{styx,imagen,dlb,gould,sci,altnet}!auspyr!mick

rjnoe@uniq.UUCP (06/17/87)

In article <6658@auspyr.UUCP>, mick@auspyr.UUCP (Mick Andrew) writes:
> >in article <304@uniq.UUCP>, rjnoe@uniq.UUCP (Roger J. Noe) says:
> > . . . The array cannot be passed as a function parameter.
>         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 			Think again, squire.

Until some real standard becomes established and generally accepted the
next best thing we have to a definition of the C language is found in
Kernighan and Ritchie, 1978.  From section 1.8 "Arguments - Call by Value"
(bottom of page 24):  "When the name of an array is used as an argument,
the value passed to the function is actually the location or address of the
beginning of the array.  (There is *no* copying of array elements.)"  [emphasis
in the original]  Similarly in section 5.3 "Pointers and Arrays" (pp. 94-95).
Also in section 10.1 "External function definitions" (p. 205):  "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 ...'."

Even when enums were added in 1978 and restrictions on structs were relaxed
to permit assignment, return by function and passing as arguments, there was
no change in C to pass arrays as function arguments.  Perhaps some C pro-
grammers are accustomed to compilers which do allow this but they are very
nonstandard C compilers.  I should think that every novice C programmer would
know you cannot pass an array as a function argument.  All you can do is pass
a pointer to one of its elements.  So when you've got "char (*b)[6];" and you
try to pass "*b" to a function, the compiler knows it cannot pass an actual
"char [6]" but has to take the address of it and pass the pointer.  In effect
what happens is to pass "&(*b)" or just "b".  This is why the value of "b"
is passed whether one writes "b" or "*b" as the actual parameter.  It's
much like having "char x[6];" and passing "x" as an argument.

> > I think the referenced publication should look for a programmer who knows
> > his/her/its C better than this one.  Or maybe change their name to
> > "Unprofessional".
> Now then, Roger, did you read the article, or are you taking the clippings
> in the original posting out of context?

It must be one of Murphy's laws that whenever you omit a ":-)" from an
article, somebody will get very offended and flame back at you.  I did not
read the original article in The DEC Professional until after I posted mine.
What prompted me to add a bit of sarcasm was the exceedingly opaque and
almost completely false quote from Rahul Dhesi's posting:

> In article <761@bsu-cs.UUCP>, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
> . . . .  The columnist continues, "...This means
> that *b is a constant and while (b) can be modified, *b never will
> change its original value.  Thus, setting b = d does not mean that 
> *b == *d.  Also, while *b is treated as a constant (unchangeable), the
> value of b can vary."

What's wrong with this is that these words are not those of the columnist,
Rex Jaeschke, but of the "college level" teacher of C who wrote to Jaeschke
in the first place.  My snide remark, based on the posting that started all
this in comp.lang.c, was misplaced and undeserved as far as Jaeschke and
The DEC Professional are concerned.  I retract the statement and apologize
to anyone who was offended by it.

HOWEVER, had a publication of this caliber employed a writer for a column
of the same nature as "Let's C Now", holding himself forth to be a professional
C programmer, and had he made the statement above, I would not retract the
comment I made in response.  If such a situation were to occur, the remark
would be deserved.  I'm glad Jaeschke doesn't identify the writer of the
letter; I truly do not want to know where this person teaches "college level"
C language.

> When I saw Rahul's posting, I knew we were in for another round of 
> "leap before you look postings" . . .

It's funny, Mick, but yours was the first such posting I saw.  It's also
the first one that failed to make any technical contribution to the issue.

> Recommendations:
> 	1)  (to all) Read the article if you can get a copy.

I concur.  It's not always easy to get a copy, but Jaeschke's articles
on C have been and continue to be worthwhile.

> 	2)  (to Roger) Consider a name change.
> ----
> Mick

In fact I considered a name change.  I thought either Mick or even Andrew
might be interesting choices, but I was afraid people would think I
was an idiot. :-)

I had the courtesy not to mention the name of the publication when being
sarcastic.  Courteous netters don't make disparaging remarks *at* other
netters, especially not in a "technical" newsgroup.  And they do apologize
when they make mistakes.  Consider it, Mick Andrew.
--
long tloc = 507314353L;
	Roger Noe			ihnp4!uniq!rjnoe
	Uniq Digital Technologies	rjnoe@uniq.UUCP
	28 South Water Street		+1 312 879 1566
	Batavia, Illinois  60510	41:50:56 N.  88:18:35 W.

dhesi@bsu-cs.UUCP (06/18/87)

In article <306@uniq.UUCP> rjnoe@uniq.UUCP (Roger J. Noe) writes:
>What prompted me to add a bit of sarcasm was the exceedingly opaque and
>almost completely false quote from Rahul Dhesi's posting:
>
>> In article <761@bsu-cs.UUCP>, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
[...exceedingly opaque and almost completely false quote omitted...]
>
>What's wrong with this is that these words are not those of the columnist,
>Rex Jaeschke, but of the "college level" teacher of C who wrote to Jaeschke
>in the first place.

Roger Noe is correct.  I was quoting the columnist quoting somebody
else.
-- 
Rahul Dhesi         UUCP:  {ihnp4,seismo}!{iuvax,pur-ee}!bsu-cs!dhesi

peter@sugar.UUCP (Peter DaSilva) (06/19/87)

I'm holding the line-eater for ransom somewhere in lower slobbovia!

In article <2117@dg_rtp.UUCP>, throopw@dg_rtp.UUCP (Wayne Throop) writes:
> > What is the meaning of the following declaration?
> >      static char (*b)[6];
> Static pointer to an array of six characters.  The same memory layout
> (though *not* the same type) can be had by the declarations:
>         char q[6];
>         static char *r = q;

except that static char (*b)[6] doesn't actually allocate the 6 bytes. Better
would be

typedef char mytype[6];
static mytype *b;

throopw@xyzzy.UUCP (Wayne A. Throop) (06/27/87)

> peter@sugar.UUCP (Peter DaSilva)     
>> throopw@xyzzy.UUCP (Wayne Throop)
>>   [ static char (*b)[6];    is a  ]
>> Static pointer to an array of six characters.  The same memory layout
>> (though *not* the same type) can be had by the declarations:
>>         char q[6];
>>         static char *r = q;
> except that static char (*b)[6] doesn't actually allocate the 6 bytes.

Well, now, I had already said this in the text immediately following,
which Peter removed.  To quote myself:

   Note that the array of characters in the first example does
   *not* have static scope (and is in fact not even allocated).

> Better would be
>   typedef char mytype[6];
>   static mytype *b;

But this misses the entire point.  Peter has defined a type that is
exactly the same as the original declaration of b with type 
(char (*)[6]).  The point was to show that a simple (char *) could
indicate the same memory layout, while (obviously) not being the same
type.  Again, the typedef just above does indeed indicate the same
memory layout.  But it is *exactly* the same type, and does nothing to
clarify the standard confusion, in particular the erronious opinion that
(char **) and (char (*)[6]) indicate the same memory structure.  It is
important to note that they do NOT.

--
Adam and Eve had many advantages, but the principal one
was, that they escaped teething.
                --- Pudd'nhead Wilson's Calendar (Mark Twain)
-- 
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw