home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / std / c / 2632 < prev    next >
Encoding:
Internet Message Format  |  1992-09-15  |  6.3 KB

  1. Path: sparky!uunet!mcsun!uknet!acorn!ixi!clive
  2. From: clive@x.co.uk (Clive Feather)
  3. Newsgroups: comp.std.c
  4. Subject: Re: struct hack, and other out-of-array references
  5. Message-ID: <1992Sep15.201137.805@x.co.uk>
  6. Date: 15 Sep 92 20:11:37 GMT
  7. References: <1992Sep07.104932.20060@x.co.uk <1992Sep8.124655.1498@Urmel.Informatik.RWTH-Aachen.DE> <1992Sep10.014137.16209@sq.sq.com>
  8. Organization: IXI Limited, Cambridge, UK
  9. Lines: 140
  10.  
  11.  
  12. [<> = Norman Diamond;  o' = me (Clive Feather) previously;
  13.  ^^ = Stephen R. van den Berg; ~~ = Mark Brader;
  14.  || = Interpretation Ruling;  ## = the standard;  no indent = me now.]
  15.  
  16. [Sorry for so much quoting, but I can't summarise the previous
  17. discussion any more than I have.]
  18.  
  19. The question:
  20. =============
  21. In the code:
  22.  
  23. o'      struct fred { int i; char s [1]; } *f;
  24. o'      char *s;
  25. o'  
  26. o'      /* ... */
  27. o'      f = malloc (sizeof f + strlen (ss));
  28. o'      if (f != NULL)
  29. o'          strcpy (f->s, ss);
  30.  
  31. If strlen (ss) is greater than 0, is the access to f->s [1] allowed in a
  32. strictly conforming program, or does it invoke undefined behaviour
  33. (access outside the bounds of an array) ?
  34.  
  35. The story so far:
  36. =================
  37. <>  You can't go past the end of an array object.  But if malloc() or some
  38. <>  other variable has defined the end of the actual array object, then the
  39. <>  + operator can get you that far, regardless of the declared type that
  40. <>  some other array variable had before getting flattened to a pointer.
  41.  
  42. o'  But there is an interpretation that says that, given
  43. o'     int a [5][5];
  44. o'  the access "a [1][6]" is illegal, because it goes past the bounds of the
  45. o'  array "a [1]". In other words, the declared type of the array does
  46. o'  restrict what can happen to a pointer derived from it.
  47.  
  48. [RFI 17 item 16]
  49.  
  50. ||  For an array of arrays, the permitted pointer arithmetic in Standard
  51. ||  ##3.3.6 Semantics (page 48, lines 12-40) is to be understood by
  52. ||  interpreting the use of the word "object" as denoting the specific
  53. ||  object determined directly by the pointer's type and value, *not* other
  54. ||  objects related to that one by contiguity. For example, the following
  55. ||  code has undefined behaviour:
  56. ||      int a [4][5];
  57. ||      a [1][7] = 0;  /* undefined */
  58. ||  Some conforming implementations may choose to diagnose an "array bounds
  59. ||  violation", while others may choose to interpret such attempted accesses
  60. ||  successfully with the "obvious" extended semantics.
  61.  
  62. [Standard section 6.3.6 (ANSI 3.3.6)]
  63.  
  64. ##  If both the pointer operand and the result point to elements of the
  65. ##  same array object, or one past the last element of the same array
  66. ##  object, the evaluation shall not produce an overflow; otherwise, the
  67. ##  behavior is undefined.
  68.  
  69. In "a [1][7]", the pointer operand is "a [1]".
  70.  
  71. ~~ Now, the important thing is that nowhere in the standard is there any
  72. ~~ license to treat the object a as an array of 20 ints.  It is an array
  73. ~~ of 4 arrays of 5 ints each.  If you write a[1][7], you are calling for
  74. ~~ the computation of a[1]+7.  a[1] here decays to a pointer to int, which
  75. ~~ points to the first element of the array a[1].  a[1]+7 does not point
  76. ~~ to that array.
  77.  
  78. o'  This RFT applies to slices of arrays, but, in *my* opinion, it is
  79. o'  extendable to this case:
  80. [The code at the top of this article]
  81.  
  82. o'  The pointer f->s points to an object with type "char [1]", and so, if
  83. o'  strlen (ss) > 0, the access to (f->s)[1] required by strlen is undefined
  84. o'  according to this RFI, even though the array has already decayed to a
  85. o'  pointer.
  86.  
  87. ~~ Clive's opinion here is wrong.  The difference
  88. ~~ between this and the other case is that the standard provides (in ANSI
  89. ~~ section 4.10.3, ISO 7.10.3) specific dispensation for the value returned by
  90. ~~ malloc() to:
  91.  
  92. ##  ... be assigned to a pointer to any type of object and then used to
  93. ##  access such an object or an array of such objects in the space
  94. ##  allocated ...
  95.  
  96. ~~ The pointer f->s does point to an object with type char[1], but it is
  97. ~~ also a pointer into the space returned by malloc(), which may be treated
  98. ~~ as an array of chars.  That is, when strcpy computes the equivalent of
  99. ~~ (f->s)[1], and therefore (f->s)+1, both the pointer operand of +, i.e.
  100. ~~ f->s, and the computed result *are* within the same array, namely the one
  101. ~~ returned by malloc(), and all is well.
  102.  
  103. Now read on:
  104. ============
  105. I don't think Mark is correct here (obviously, or I wouldn't be
  106. writing :-). The intent of the section about malloc quoted is clearly to
  107. indicate that the space returned can be used for any purpose, and is not
  108. restricted to (say) char arrays only. If the pointer returned from
  109. malloc is cast to a pointer to a character type, then of course any part
  110. of the space can be accessed. However, once the pointer has been cast to
  111. a "struct fred *", then it can only be used to access the contents of a
  112. "struct fred".
  113.  
  114. I have to say that the Standard seems very confused when talking about
  115. pointers to objects within objects. For example, the above ruling
  116. implies that a single pointer cannot walk through the array "a", because
  117. there is an "iron curtain" between a[0] and a[1], and so on. On the
  118. other hand, it is easy to show that a[0][4] and a[1][0] must be adjacent
  119. in memory, which implies that the pointer can walk through, in code
  120. like:
  121.  
  122.     for (i = 0, p = &(a [0][0]); i < sizeof a / sizeof a [0][0]; i++)
  123.         printf ("%d\n", *p++);
  124.  
  125. I think that the only way we are going to decide this question is by
  126. putting together a set of RFIs which cover all the relevant issues
  127. and submitting them.
  128.  
  129. ~~ I would also argue that the Interpretation Ruling cited above would not
  130. ~~ apply if the type of the example array "a" had been char instead of int.
  131. ~~ The definitions of "object" and "byte" (in ANSI section 1.6, ISO 3.14 and
  132. ~~ 3.4) in effect require that it be possible to treat any object as an
  133. ~~ array of any character type.
  134.  
  135. But even then "a" is an array of type "char [5]", not of type "char", so
  136. that doesn't apply. However, to avoid doubt, change my code to:
  137.  
  138.     struct fred { double d; int arr [1]; } *f;
  139.     int i, n;
  140.  
  141.     /* ... */
  142.     f = malloc (sizeof f + (n - 1) * sizeof (int));
  143.     if (f != NULL)
  144.         for (i = 0; i < n; i++)
  145.             f->arr [i] = 0;
  146. -- 
  147. Clive D.W. Feather     | IXI Limited         | If you lie to the compiler,
  148. clive@x.co.uk          | 62-74 Burleigh St.  | it will get its revenge.
  149. Phone: +44 223 462 131 | Cambridge   CB1 1OJ |   - Henry Spencer
  150. Fax:   +44 223 462 132 | United Kingdom      |
  151.