[net.lang.c] C Coding Question

tad@killer.UUCP (Tad Marko) (08/10/86)

[line eater fodder]

Hey, everybody, I discovered a "new" (new to me) way of coding such static
things as descriptions, helps, and the like.  It goes like this:

#include <stdio.h>

char *help[] = {
	"You do it this way, dummy:",
	"",
	"so on and so forth...",
	NULL
	};

char *description[] = {
	"I do this...",
	"no much of anything...",
	NULL
	};

main()
{
	say(help);
	printf("\n");
	say(description);
}

say(dp)
char *dp[];
{
	while (*dp != NULL)
		printf("%s\n", *dp++);
}

Here's the question:  Should help and description be declared char *help[]
or char **help?  Should say() be declared void, or is there some sort of 
error checking I should implement?  Are there any other suggestions or
comments?  How about any other neat "tricks" anyone has discovered lately?

						Tad
--
Tad Marko
..!ihnp4!killer!tad		||	..!ihnp4!alamo!infoswx!ntvax!tad
UNIX Connection BBS AT&T 3B2		North Texas State U. VAX 11/780
If it's not nailed down, it's mine; If I can pick it up, it's not nailed down.

guy@sun.uucp (Guy Harris) (08/11/86)

> Here's the question:  Should help and description be declared char *help[]
> or char **help?

Since you've already shown a declaration for them, and since you've done it
correctly, I'm not sure why you're asking.  Since "help" would be an array
of pointers to characters, which is in no way equivalent to a pointer to a
pointer to a character, you obviously declare it as such - "char *help[]".
Try it the other way ('char **help = { "foo", "bar", ... };') and the
compiler will let you know, in no uncertain terms, that it's not correct.

> Should say() be declared void, or is there some sort of error checking I
> should implement?

That depends on whether you can do anything useful if you detect an error.
For example, it's rarely useful to check for errors on an "fprintf" to
"stderr" - if it fails, what can you do, print a message to "stderr"
indicating you can't print a message to "stderr"?  (The VMS routine that
prints error messages never returns a failure indication, probably for much
the same reason.)  Even if you have something useful to do on an error, you
may want to do it entirely within "say".
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

gary@darth.UUCP (Gary Wisniewski) (08/12/86)

Distribution:


In article <248@killer.UUCP> tad@killer.UUCP writes:
>Hey, everybody, I discovered a "new" (new to me) way of coding such static
>things as descriptions, helps, and the like.  It goes like this:
>

[At this point, the author includes code which builds a table of char *'s
which are preinitialized and terminated with a NULL ptr, then builds a
function say(char *) which prints out the entire list.]

>
>Here's the question:  Should help and description be declared char *help[]
>or char **help?  Should say() be declared void, or is there some sort of 
>error checking I should implement?  Are there any other suggestions or
>comments?  How about any other neat "tricks" anyone has discovered lately?
>
>						Tad
>--
>Tad Marko


The technique you've discovered is actually a small part of a much more
general facility in C.  For more interesting ideas about ways to create
complex pre-initialized structures and tables, look at K&R, page 124.

As far as your question about "char *help[]" and "char **help": the two
forms are IDENTICAL to virtually every C compiler (that's worth its
salt).  Arrays in C are merely special cases of pointers.  In other
words, both forms are correct.  Section 5.3 of K&R explain this
more fully.  Happy C-ing.

Gary J. Wisniewski
Pittsburgh, PA

Usenet:		{allegra, bellcore, cadre}!pitt!darth!gary

chris@umcp-cs.UUCP (Chris Torek) (08/16/86)

In article <138@darth.UUCP> gary@darth.UUCP (Gary Wisniewski) writes:
>As far as your question about "char *help[]" and "char **help": the two
>forms are IDENTICAL to virtually every C compiler (that's worth its
>salt).  Arrays in C are merely special cases of pointers.  In other
>words, both forms are correct.

	NO!

Ai!  This has been asserted far too often.  Arrays and pointers are
not at all the same thing in C!

>Section 5.3 of K&R explain this more fully.

Indeed it does, and I suggest you read it rather more carefully.

  The correspondence between indexing and pointer arithmetic is
  evidently very close. ... The effect is that an array name *is*
  a pointer expression.  (p. 94)

This does not say that arrays and pointers are *the same*.

  There is one difference between an array name and a pointer
  that must be kept in mind.

Aha!  See p. 94 for that difference.

  As formal parameters in a function defintion,

	char s[];

  and

	char *s;

  are exactly equivalent.... (p. 95)

Here they *are* the same---but note the qualifier: `As formal
parameters'.  In the (unquoted) original example, the array was a
global variable.

There is one other thing which, I guess, adds to this confusion.
Both of the following are legal global declarations in C:

	char	msg0[] = "Hello, world";
	char	*msg1 = "Hello, world";

Given both declarations,

	printf("%s\n", msg0);

and

	printf("%s\n", msg1);

produce the same output.  Yet msg0 and msg1 are not the same:

	printf("%d %d\n", sizeof (msg0), sizeof (msg1));

prints

	13 4

on a Vax; for msg0 is an array, and msg1 is a pointer.  The code
generated for the two declarations is different:

	/* edited assembly output from ccom */
		.data			# Switch to data segment.
		.globl	_msg0		# The array ...
	_msg0:	.asciz	"Hello, world"	# and here it is.

		.data	2		# Switch to alternate data segment.
	L12:	.asciz	"Hello, world"	# The object to which msg1 will point.
		.data			# Back to regular data segment.
		.globl	_msg1		# The pointer ...
	_msg1:	.long	L12		# which points to the object.

String constants comprise two special cases in the compiler.  The
first case is when the constant appears anywhere *except* as an
initialiser for a `char' array.  Here the compiler uses the alternate
data segment to suddenly `create' a new array, initialised to the
string text; it then generates a pointer to that array.  In the
second case the string constant is generated in the primary data
segment, and `is' the array being initialised: the constant is
`unwrapped' into an aggregate initialisation.

The second case is actually the more `conventional' of the two;
other aggregates cannot be created at run time:

	int a[] = { 0, 1, 2, 3 };

is legal only outside functions.  What seems surprising to some is
that the same is true of

	char s[] = "foo";

because, unwrapped, this is equivalent to

	char s[] = { 'f', 'o', 'o', '\0' };
	
---even though

	char *s = "foo";

is legal anywhere a declaration is legal.

Ah, if only C had aggregate initialisers!
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

mac@tflop.UUCP (Mike McNamara) (08/20/86)

In article <248@killer.UUCP> tad@killer.UUCP writes:
> Should say() be declared void, or is there some sort of 
> error checking I should implement?  

Progammer's Maxim # 17 :
Never test for an error condition if you don't know what to do with it.


-- 
---------------------------------+--------------------------------------------
| Michael Mc Namara              | Let the words by yours, I'm done with mine.
| UUCP: dual!vecpyr!tflop!mac    | May your life proceed by its own design.
| ARPA: tflop!mac@ames.arpa      |
---------------------------------+--------------------------------------------

throopw@dg_rtp.UUCP (Wayne Throop) (08/20/86)

> chris@umcp-cs.UUCP (Chris Torek)
>> gary@darth.UUCP (Gary Wisniewski)

>>> [ Original poster asks (among other things):
>>>     char *a[] = {"foo","bar"}; works...
>>>     will char **p = {"foo","bar"}; ?        ]
>>As far as your question about "char *help[]" and "char **help": the two
>>forms are IDENTICAL to virtually every C compiler (that's worth its
>>salt).  Arrays in C are merely special cases of pointers.  In other
>>words, both forms are correct.
>
> 	NO!
>
> Ai!  This has been asserted far too often.  Arrays and pointers are
> not at all the same thing in C!

Naturally, Chris is correct here, and points out that a reasonably
careful reading of K&R reveals the truth of the matter.  However, I'd
like to expand on the assertion that "virtually every C compiler [treats
char *x[] and char **x identically]".  In fact, I doubt that there are
very many such compilers.  I even think it is likely that the compiler
Gary uses does *NOT* treat these cases identically.  Which brings me to
the point.

TEST YOUR ASSERTIONS ABOUT THE C COMPILER BEFORE POSTING!

Let's see what we get when we try to compile this:

    char *a[] = {"foo","bar"};
    char **p  = {"foo","bar"};

Lint says

        (2)   **** cannot recover from this error ****

Not very informative, but should give you the idea that the declaration
of p is wrong, and the declaration of a is OK.  A local typechecker says

        2   too many values in initialization
        2   wrong type initializer

Ah.  A little more informative.  We are trying to initialize a single
pointer value with multiple pointers.  And they have type mismatch
problems (we are trying to initialize a (char **) with a (char *)).
Last, let's see what the compiler says.

        char **p  = {"foo","bar"};
                           ^
        You supplied p more initial values than there were variables
        or fields to initialize.  The compiler ignored the excess
        elements.

The compiler didn't diagnose the type mismatch problems because
typechecking isn't part of its job.

The point?   In this specific case, every C-understanding tool I
applied to the example Gary said would work for "every C compiler worth
its salt" found problems with it.  So, since they are easy to check,
CHECK THESE CLAIMS BEFORE YOU POST.

It helps to carefully read the standards.  But every assertion posted
about implementations of these standards ought to be backed up with an
example that has actually been fed to the compiler/tool/whatnot in
question.

--
You can't tell how deep a puddle is until you step in it.
                                --- Miller's Law
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

karl@haddock (08/22/86)

>["type *" and "type []"] are IDENTICAL to virtually every C compiler ...
>Arrays in C are merely special cases of pointers.

You are wrong, but you've got a lot of company.

I almost wish the language had kept arrays and pointers completely separate,
requiring "&a[0]" to change an array into a pointer, and "*(p+n)" to index
from a pointer.  Or maybe "p@[n]", where "@" is some postfix operator that
converts a pointer into an array of unknown size.

Karl W. Z. Heuer (ihnp4!ima!haddock!karl), The Walking Lint

tad@killer.UUCP (Tad Marko) (08/23/86)

In my original article, I wasn't asking if this was legal:

char **thing = {"stuff","and more stuff"};

I was asking if I should use 

foo(parm)
char **parm;
{...}

or

foo(parm)
char *parm[];
{...}

and I *DID* test both of them, and they both worked.

C'mon, give me at least a *little* credit...sheeeesh!

--
Tad Marko
..!ihnp4!killer!tad		||	..!ihnp4!alamo!infoswx!ntvax!tad
UNIX Connection BBS AT&T 3B2		North Texas State U. VAX 11/780
flames to:  /dev/your_ear