[comp.lang.c] Why doesn't this work?

sbigham@dukeac.UUCP (Scott Bigham) (03/23/89)

My annual C question:

Is there a particularly glaring reason that the following code should make the
compiler unhappy?

------------------- Quick, Robin!  The BAT-scissors! -------------------

typedef char **Block;

Block
	b1 ={"this","that"},
	b2={"yes","no"},
	B[]={b1,b2};
-------------------------------------------------------------------------

It curses at me thusly:

"test.c", line 6: warning: illegal pointer combination, op =
"test.c", line 6: initialization alignment error
"test.c", line 6: warning: illegal pointer combination, op =
Fatal error in /lib/comp
Status 0210

I thought of changing the first line to "typedef char **Block", but that gave
me worse:

"test.c", line 4: warning: illegal pointer combination, op =
"test.c", line 4: initialization alignment error
"test.c", line 4: warning: illegal pointer combination, op =
"test.c", line 4: initialization alignment error
"test.c", line 5: warning: illegal pointer combination, op =
"test.c", line 5: initialization alignment error
"test.c", line 5: warning: illegal pointer combination, op =
"test.c", line 5: initialization alignment error
"test.c", line 6: illegal initialization
"test.c", line 6: illegal initialization

I'm sure it's quite obvious to everyone but me what's wrong with this.  I
guess that's what I get for trying to understand C after midnight.  Thank
you very much for unbefuddling me.

						sbigham
-- 
Scott Bigham                         "The opinions expressed above are
Internet sbigham@dukeac.ac.duke.edu   (c) 1989 Hacker Ltd. and cannot be
USENET   sbigham@dukeac.UUCP          copied or distributed without a
...!mcnc!ecsgate!dukeac!sbigham       Darn Good Reason."

sbigham@dukeac.UUCP (Scott Bigham) (03/23/89)

In article <1309@dukeac.UUCP> I wrote:
>
>typedef char **Block;
>
>Block
>	b1 ={"this","that"},
>	b2={"yes","no"},
>	B[]={b1,b2};

I meant to write:

typedef char *Block[];
	etc...

The second "typedef char **Block;" is right.  I really shouldn't be trying to
think at this hour.  Excuse me, I'm going to go to bed.

						sbigham
-- 
Scott Bigham                         "The opinions expressed above are
Internet sbigham@dukeac.ac.duke.edu   (c) 1989 Hacker Ltd. and cannot be
USENET   sbigham@dukeac.UUCP          copied or distributed without a
...!mcnc!ecsgate!dukeac!sbigham       Darn Good Reason."

chris@mimsy.UUCP (Chris Torek) (03/23/89)

In article <1309@dukeac.UUCP> sbigham@dukeac.UUCP (Scott Bigham) writes:
>typedef char **Block;
>
>Block
>	b1 ={"this","that"},
>	b2={"yes","no"},
>	B[]={b1,b2};

It does not work because b1, b2, and B[any] have type `pointer to
pointer to char' and the first two { ... } lines are not valid initialisers
for pointer-to-pointer-to-char, and indeed are not valid initialisers
for anything but array-of-pointer-to-char.  (The third initialiser would
be correct if the first two were not rejected earlier.)

The following rerun makes various salient points.

From: chris@umcp-cs.UUCP (Chris Torek)
Newsgroups: net.lang.c
Subject: Re: C Coding Question
Date: 16 Aug 86 00:58:44 GMT

In many articles many USENET posters assert that
>... 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 1st ed. has this to say about it:

  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'.  The array must not be a global or static 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 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

tim@crackle.amd.com (Tim Olson) (03/24/89)

In article <1309@dukeac.UUCP> sbigham@dukeac.UUCP (Scott Bigham) writes:
| My annual C question:
| 
| Is there a particularly glaring reason that the following code should make the
| compiler unhappy?
| 
| ------------------- Quick, Robin!  The BAT-scissors! -------------------
| 
| typedef char **Block;
| 
| Block
| 	b1 ={"this","that"},
| 	b2={"yes","no"},
| 	B[]={b1,b2};
| -------------------------------------------------------------------------

Unfortunately, there are no anonomous arrays in C, except for strings.
You are attempting to create anonomous arrays of character pointers
and assign their addresses to the variables b1 and b2.  There are a
couple of ways to do what you want:

If b1 and b2 will always point to the initialized contents, then you
should declare them as:

	typedef	char *Static_Block[];

	Static_Block
		b1 = {"this, "that"},
	.
	.

If b1 and b2 may point to other data in the future, then you must give a
name to the initialized array contents:

	typedef	char **Block;
	typedef	char *Static_Block[];

	Static_Block
		init1 = {"this", "that"},
		init2 = {"yes", "no"};

	Block
		b1 = init1,
		b2 = init2;

________________________________________
That addresses your first problem.  Your second problem is the
declaration:

	Block
		B[] = {b1, b2};

Here you are violating the requirement that initialization values must
be constants.  I'm not quite sure what you are trying to do with the
array B.  If you simply want it to hold the same *initial* contents as
b1 and b2, then you can use:

	Block
		B[] = {init1, init2};

If you want B to "track" the changing values of b1 and b2, then you have
to use one more level of indirection:

	Block
		*B[] = {&b1, &b2};

The addresses of b1 and b2 are constants, so they can be used in the
initialization.


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

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