How should I declare strings within C structs?

There are basically 3 common conventions for strings. All three are found in the wild, both for in-memory representation and storage/transmission.

  1. Fixed size. Access is very efficient, but if the actual length varies you both waste space and need one of the below methods to determine the end of the "real" content.
  2. Length prefixed. Extra space is included in the dynamically allocation, to hold the length. From the pointer you can find both the character content and the length immediately preceding it. Example: BSTR Sometimes the length is encoded to be more space efficient for short strings. Example: ASN-1
  3. Terminated. The string extends until the first occurrence of the termination character (typically NUL), and the content cannot contain that character. Variations made the termination two NUL in sequence, to allow individual NUL characters to exist in the string, which is then often treated as a packed list of strings. Other variations use an encoding such as byte stuffing (UTF-8 would also work) to guarantee that there exists some code reserved for termination that can't ever appear in the encoded version of the content.

In the third case, there's a function such as strlen to search for the terminator and find the length.

Both cases which use pointers can point to data immediately following the fixed portion of the structure, if you carefully allocate it that way. If you want to force this, then use a flexible array on the end of your structure (no pointer needed). Like this:

typedef struct
{
    int damage;
    char name[]; // terminated
} Item;

or

typedef struct
{
    int damage;
    int length_of_name;
    char name[];
} Item;

1) is there any other advantage to using the fixed size (1)

char name[40];

versus doing the following and using a pointer to a char array (2)?

char *name;

and if so, what is the advantage?

With your array declared as char name[40]; space for name is already allocated and you are free to copy information into name from name[0] through name[39]. However, in the case of char *name;, it is simply a character pointer and can be used to point to an existing string in memory, but, on its own, cannot be used to copy information to until you allocate memory to hold that information. So say you have a 30 character string you want to copy to name declared as char *name;, you must first allocate with malloc 30 characters plus an additional character to hold the null-terminating character:

char *name;
name = malloc (sizeof (char) * (30 + 1));

Then you are free to copy information to/from name. An advantage of dynamically allocating is that you can realloc memory for name if the information you are storing in name grows. beyond 30 characters. An additional requirement after allocating memory for name, you are responsible for freeing the memory you have allocated when it is no longer needed. That's a rough outline of the pros/cons/requirements for using one as opposed to the other.