[comp.sys.mac.programmer] Char * and char

jtgorman@cs.arizona.edu (J. Taggart Gorman) (06/14/91)

  This is not a true Macintosh question, but I am posting it here because I
know someone in this group will know, and besides, I have to ask this question
because programming the Mac in C and using strings is enuf to drive one mad.

  This question deals with character arrays and pointers.  In one of my data
structures, I declare :
  char  name[30];

Later, I declare :
  char *cp;

and assign :
  cp = "\pNo Name";

Now, I try to somehow assign cp to name.  I tried :
  theDecker.name = strcpy(cp, theDecker.name);

and also
  theDecker.name = cp;

  THINK C complains of an illegal op. on an array.

  How the heck do I get a string constant into a character array???

|      J. Taggart Gorman Jr.     | "I'm a no rust build up man myself."
|                                |          -Christian Slater
| jtgorman@caslon.cs.arizona.edu |             in 'Heathers'

potts@us.cc.umich.edu (Paul Potts) (06/14/91)

In article <1569@caslon.cs.arizona.edu> jtgorman@cs.arizona.edu 
(J. Taggart Gorman) writes:

>because programming the Mac in C and using strings is enuf to drive one mad.

It isn't just the Mac. The distinction between strings, arrays, pointers to
chars, etc., is and always has been tricky in C.  This is partially made up
for by the fact that, if you know exactly what you are doing, you can do
anything in a minimal number of machine cycles.
Unfortunately, this doesn't make it easy.  I've been programming for a number
of years now, at least two in C, and I still get bitten constantly by bugs
when I try to do things with strings and character arrays.
 
>  This question deals with character arrays and pointers.  In one of my data
>structures, I declare :
>  char  name[30];
>
>Later, I declare :
>  char *cp;
>
>and assign :
>  cp = "\pNo Name";

In this line, cp = "\pNo Name",  here is what you are really doing:  you
are creating a character constant that gets stored somewhere within your
code (it depends on the compiler) and then making cp point to the characters
in memory.  

Why not do the following:

char name [30] = "\PNo Name";

or:

char * cp = "\PNo Name";

or:

cp = "\PNo Name";  (cp is implicitly a pointer to char if you do this).

You don't really need to create char *cp, unless you are going to recycle
it later, and C supports initialization of a character array in one statement.

Keep in mind that these two statements are not precisely equivalent:  the
first one allocates space on the stack, and the second one does something
initialization-dependent.  Also, one of these is not an assignment in
the sense that you might think.  This becomes important if I call the procedure
a second time.  "name" might look in memory like this:

" [length byte] N o _ N a m e X X X X X X X X X X... " where X could be
anything.

and "cp" might look like this:

" [length byte] N o _ N a m e ..."                             

Now, if I change the contents of "name" and then leave the procedure and
re-enter it, the array assignment will take place again, and once more
the first 8 characters of "name" will be as shown.  If I change the area
of memory pointed to by "cp" and then re-enter the procedure, cp will
point to some garbage: no bytes are copied by "char * cp = "\PNo Name",
since all it does is assign a pointer to the address of a constant which
is stored (depending on the implementation) in the global address space
somewhere.

>Now, I try to somehow assign cp to name.  I tried :
>  theDecker.name = strcpy(cp, theDecker.name);

First, I think your call to strcpy is backwards.  It copies from param2
to param1.  Also, I wouldn't try to use strcpy on a pascal-type string.
strcpy expects a \0 to terminate the copy, and if it doesn't find it it
will just keep copying.  It doesn't do any sort of bounds checking.
It will copy half the ROM into your string if you aren't careful.
You should probably do it is a C string and then convert it into
Pascal form later if you need to.
THINK C has a built-in function to do this, or else I can post one.
It is pretty simple to convert.

Second, strcpy expects a pointer, and I think theDecker.name will
resolve to the first element of "name."  I think "name" by itself
would give you a pointer, but the struct reference may make it
a little weird, since you actually want the address of the completely
specified sub-part of your struct.

Try this:
 
 &(theDecker.name[0])

That will resolve to the address of the first element of the array
part of your struct theDecker.  If you have prototyping turned on,
you will need to cast it as a pointer to char:

(char*)&(theDecker.name[0])

The parentheses might not be necessary, but I don't have the precedence
rules memorized for C, so I use them whenever I'm not sure.

>and also
>  theDecker.name = cp;
>
>  THINK C complains of an illegal op. on an array.
>

Here it looks like you are trying to copy the address of a character
constant (just a location in memory, probably part of your global variable
space) into the address of an array.  You can't change the base address
of an array like that.  If you did, then you wouldn't be able to get at
the data already stored in the array, the sizeof operator wouldn't know
what to return; it would just be a mess.

From Andrew Koenig's "C Traps and Pitfalls" (highly recommended)

"If we use the name of an array where a pointer is appropriate,
that name is taken to mean a pointer to element 0 of that array.
Thus if we write
   p = a;
we will set p to the address of element 0 of a. Notice that we did
not say
   p = &a;
That is illegal in ANSI C because &a is a pointer to an array but
p is a pointer to an int."

Also, keep in mind that even if this did work, it would not always
do what you wanted it to.  It will only copy a pointer, not actually
move any bytes.

>  How the heck do I get a string constant into a character array???
>

If you need more help after reading the above, e-mail me or take a
look at Andrew Koenig's "C Traps and Pitfalls" or Al Kelley and Ira Pohl's
"A Book on C."  Basically, you set up your array, and then either do an
assignment or do a strcpy from a string constant. 

Disclaimer:  although I claim to know what I'm doing, I'm sure there are
those out there that have a better claim on this information.  If anyone
finds that I am claiming something incorrectly, please declaim a correction
on the net.

Paul Potts
potts@itl.itd.umich.edu
Paul_Potts@um.cc.umich.edu

dorner@pequod.cso.uiuc.edu (Steve Dorner) (06/14/91)

In article <1991Jun14.132530.12254@terminator.cc.umich.edu> potts@us.cc.umich.edu (Paul Potts) writes:
>Why not do the following:
>
>char name [30] = "\PNo Name";

Cause you can't initialize automatic arrays, for one.  'Sides, strings
don't belong in mac programs; resources should be used.  (Pedantic but true.)

>cp = "\PNo Name";  (cp is implicitly a pointer to char if you do this).

No, your statement is an illegal attempt to assign a pointer (to "\PNo Name")
to an int (which is what cp be implicitly declared to be, if your compiler
engages in implicit behavior (some do, some don't)).

> &(theDecker.name[0])

Completely synonymous with theDecker.name; just less efficient notationally
(and computationally, if you have an incredibly stupid compiler, which are
distressingly common).

>you will need to cast it as a pointer to char:
>
>(char*)&(theDecker.name[0])

Not if "name" is declared as "char name[30]".

>If anyone
>finds that I am claiming something incorrectly, please declaim a correction
>on the net.

You've got a decent handle on these things conceptually, it's just the
niggling details...

And an aside for MPW sufferers:

>   p = &a;
>That is illegal in ANSI C because &a is a pointer to an array but
>p is a pointer to an int."

But it's required in SADE (they wouldn't call it SADE if it were pleasant,
now would they?).  :-)
--
Steve Dorner, U of Illinois Computing Services Office
Internet: s-dorner@uiuc.edu  UUCP: uunet!uiucuxc!uiuc.edu!s-dorner