Path: csiph.com!eternal-september.org!feeder.eternal-september.org!mx02.eternal-september.org!.POSTED!not-for-mail From: Keith Thompson Newsgroups: comp.lang.c Subject: Re: One over another: (*structp).component structp->component Date: Tue, 10 Nov 2015 14:13:54 -0800 Organization: None to speak of Lines: 112 Message-ID: References: <69d66e0c-4b66-4921-842d-e7754b5f3c68@googlegroups.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: mx02.eternal-september.org; posting-host="945944de09706c9b4e29b53c9d2efdc2"; logging-data="17321"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18wBpjv+nhdnTKk3coygxEZ" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux) Cancel-Lock: sha1:1s1QfcIRlC2WX9EAfsLBkNv9/7Q= sha1:uBCqCkIC8Y/mKx021VDgo3MFQEY= Xref: csiph.com comp.lang.c:75687 Tim Rentsch writes: > Keith Thompson writes: [...] >> The use of an unsigned* argument violates the "shall", resulting in >> undefined behavior. I don't propose changing that. > > Oh yeah. I think I have had this pointed out to me before > but I forgot. > > However, I believe there is still a related problem. Consider > this calling sequence: > > unsigned u; > scanf( "%d", (signed *) &u ); Ok, that's legitimate. The effective type rules (N1570 6.5p7) aren't directly relevant, since they discuss accessing the stored value of an object and we're not doing that here, but I think it's reasonable to assume that if the input is in the range 0..INT_MAX, then that value will be stored in u. (Proving that is left as an exercise for the pedant.) Which means that "an appropriate type" (N1570 7.21.6.2p10) could be either int or unsigned int, because of the partial interchangeability of those types. I don't see anything explicit in the standard to support that. For a value outside that range, specifically a negative value, I suppose the value of u is implementation-defined and can be determined by the representation of int and unsigned int. So -- what's the problem? The scanf call is treating u as if it were an object of type signed int. The behavior is determined by the format. Assume 16-bit 2's-complement int (just to save typing). Currently, the behavior is undefined if the input is, for example, "-32659" or "32768". In my proposal, either of those inputs would be treated like a matching failure, causing scanf() to return 0. The fact that the actual type of u is unsigned int would not affect that, only the interpretation of the value (if any) after it's been stored. > Now we haven't violated the "shall" sentence (taking that to be a > statment about the type of the argument, which I think it what's > intended), but we still have a problem storing a value that isn't > in the intersection of the two types' values. This possibility > also explains why the aforementioned paragraph talks about "/an/ > appropriate type" rather than "the appropriate type", and why it > references the type of the object being stored into rather than > just the type of the argument pointer. I'd say it's not scanf's job to worry about that. It's no different from any other method of storing an int value in an unsigned object: unsigned u; *(int*)u = -1; >> In practice it's very likely to behave as expected for values in the >> range 0..INT_MAX. >> >> The "%d" format specifies that the input must have the form of an >> optionally signed integer constant, and that its value must be within >> the range INT_MIN .. INT_MAX. The fact that the resulting int value >> (in case of success) is (ignoring the UB) then copied to an object >> of type unsigned int doesn't affect any checking done by scanf(). > > I believe the "shall" undefined behavior here is meant to > reference the type of the argument, not the type of the object > being stored into. There may also be undefined behavior if > effective type rules are violated, but those rules specifically > allow using the corresponding signed or unsigned type (for integer > types). It says: Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result. If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined. Is "the object pointed to" of type int (what the pointer points to), or unsigned (how the object was defined)? If both types are "appropriate", I suppose it doesn't matter. >> I'm not sure what to make of the phrase "an appropriate type" rather >> than "the appropriate type". Perhaps it's meant to refer to the fact >> that type correct type varies with different formats, but as far as I >> can tell each format specifies a unique required type, with undefined >> behavior if a different type is used. > > The argument (pointer) type is uniquely determined by the format; > the type of the object to be stored into is not. That distinction > explains both: the "shall" requirement; and the phrase "an > appropriate type", and referring to the object that the value is > placed in (rather than referring again to the argument type). > > Of course, it still would be possible to add a rule that says > anything outside the range of the argument type would be treated > the same way as a match failure. But that would still leave a > potential undefined behavior for storing into an object whose > type cannot represent the value to be stored. Sure, if the types don't match the behavior is undefined. I don't suggest changing that, since it's easy to avoid by writing correct code. All I propose changing is the currently undefined behavior that cannot be avoided without restricting the input. -- Keith Thompson (The_Other_Keith) kst-u@mib.org Working, but not speaking, for JetHead Development, Inc. "We must do something. This is something. Therefore, we must do this." -- Antony Jay and Jonathan Lynn, "Yes Minister"