[m-dev.] Tool for generating DU types in C

Peter Moulder Peter.Moulder at infotech.monash.edu.au
Mon Mar 22 17:59:10 AEDT 2010


On Mon, Mar 22, 2010 at 03:29:59PM +1100, Paul Bone wrote:

> You should also make the sentinel object above const so that it is allocated in
> read-only memory at runtime.  The constructor should also return a const
> object, use cdecl(1) for help with the const keyword, I never remember the
> order of these keywards for declarations.

`const' and other qualifiers `volatile' and (in C99) `restrict' modify
whatever (non-qualifier) part of the type immediately precedes that qualifier.
So `char const *' is a non-constant pointer to a constant char.

As a special case, if there is nothing (other than other qualifiers) preceding
the qualifier, it modifies the part following it: so `const char *' is also a
non-constant pointer to a constant char.  However, I'd recommend against using
that form -- precisely because it's a special case and it promotes confusion
about what the general rule is for qualifiers.  I've seen more than one
experienced C programmer confused by const ordering, and I seem to recall
attention being brought to such an error in mercury-reviews; I believe that
consistently placing `const' after the thing being modified will reduce such
confusion.

(Another reason to prefer `T const *' over `const T *' is that `T' is typically
the more important part to know about, so it's good to have the T at the
beginning where it's more prominent than in the middle.)

So, as to the question of how to declare the sentinel if it is to be made const,
I would recommend the order:

  struct a_b_tree_a const _a_b_tree_a_sentinel = { ... };


In answer to Ralph's subsequent question as to whether or not attempts to
modify _a_b_tree_a_sentinel will result in segfaults, and supplementary to
Paul's reply noting the implementation on ELF systems: footnote 101 in §6.7.3
‘Type qualifiers’ in the C99 spec says that

  The implementation may place a const object that is not
  volatile in a read-only region of storage.

and indeed gcc-4.3 and gcc-4.4 (the only two C compiler versions I have handy)
do give segfaults on attempts to modify the object.

The main cost of doing so is that if you have pointers that sometimes point
to sentinels that mustn't be written to but sometimes are used for writing
to (presumably non-sentinel) values, then assigning &sentinel to that pointer
must be accompanied by a cast (as a reminder that it's up to the programmer
to check that this pointer will never in fact be used to modify the sentinel
object).

(Of course the programmer must do this checking whether or not we end up using
`const' for sentinels, because it would certainly be bad to modify that
sentinel.  The cost here is just the extra cast and whatever maintainability
costs and benefits that has, there's no difference in the amount of checking
required to avoid "bad stuff".)

  struct a_b_tree_a *p = &_a_b_tree_a_sentinel;         /* compile error */
  struct a_b_tree_a const *p = &_a_b_tree_a_sentinel;   /* ok */
  struct a_b_tree_a *p = (struct a_b_tree_a *) &_a_b_tree_a_sentinel;  /* ok */

pjrm.


References (within the C99 spec): 
  §6.7.3 ‘Type qualifiers’, para [#4]
  §6.7.5.1 ‘Pointer declarators’ para [#1]

I haven't checked whether C89 differs from C99 in any of these points,
though I'm not aware of any differences [other than existence of `restrict'].

--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at csse.unimelb.edu.au
Administrative Queries: owner-mercury-developers at csse.unimelb.edu.au
Subscriptions:          mercury-developers-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the developers mailing list