randy@gtx.com (Randy D. Miller) (11/13/90)
Can NULL always be cast to a function pointer, even in implementations that legally define NULL as (void *)0? For example, are the following lines legal? /* example 1 */ int (*f1)(void) = NULL; /* example 2 */ int (*f2)(void) = (int (*)(void))NULL; According to ANSI X3.159-1989 3.2.2.3 (pointer conversions) and 3.3.16.1 (assignment operator), example #1 is clearly legal even when NULL is (void *)0, because the null pointer constant is a special case (but no other pointer to void may be converted to a pointer to a function). But is the cast in example #2 legal? It just does explicitly what is done implicitly in example #1. I tried it on a few compilers and lints that define NULL as (void *)0; only Microsoft version 6.0 rejected it with an "illegal cast" error message (using cl -W4 -Za). Chris Torek, in <15047@mimsy.UUCP> said, >(remember that a cast is equivalent to assignment to an unnamed >temporary variable---I like to consider `assignment' to include all >casts). If that is true, then example #2 must also be legal. However, I can't see anything in the Standard that specifies that all the constraints of section 3.3.16.1 (assignment operator) apply to section 3.3.4 (cast operator). 3.3.16.1 specifically allows that a null pointer constant may be assigned to "a pointer," which includes function pointers. 3.3.4 doesn't specifically treat casts of the null pointer constant. But maybe this wording in 3.3.4 associates the two sections: "Conversions that involve pointers (other than as permitted by the constraints of 3.3.16.1) shall be specified by means of an explicit cast;" But surely that can't mean that if a conversion is "permitted" by 3.3.16.1, it *cannot* have an explicit cast, can it? If so, then the cast operator is unlike an assignment in this one special case where the null pointer constant is converted to a function pointer, and you must only make the assignment *without* an explicit cast. Are there any other cases where an implicit conversion is legal, but the identical explicit cast is not? Or is Chris Torek right and MSC 6.0 wrong? -- Randy D. Miller sun!sunburn!gtx!randy or hrc!gtx!randy or randy@gtx.UUCP GTX Corp., 8836 N. 23rd Avenue, Phoenix, Arizona 85021 (602) 870-1696
gwyn@smoke.brl.mil (Doug Gwyn) (11/14/90)
In article <1391@gtx.com> randy@gtx.UUCP (Randy D. Miller) writes:
-Can NULL always be cast to a function pointer, even in implementations
-that legally define NULL as (void *)0? For example, are the following
-lines legal?
- /* example 1 */ int (*f1)(void) = NULL;
- /* example 2 */ int (*f2)(void) = (int (*)(void))NULL;
No, the second is not required to be supported by the implementation,
but the first is (3.2.2.3).
volpe@camelback.crd.ge.com (Christopher R Volpe) (11/15/90)
In article <14457@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes: |>In article <1391@gtx.com> randy@gtx.UUCP (Randy D. Miller) writes: |>-Can NULL always be cast to a function pointer, even in implementations |>-that legally define NULL as (void *)0? For example, are the following |>-lines legal? |>- /* example 1 */ int (*f1)(void) = NULL; |>- /* example 2 */ int (*f2)(void) = (int (*)(void))NULL; |> |>No, the second is not required to be supported by the implementation, |>but the first is (3.2.2.3). What's wrong with the second example? A null pointer constant represented by "(void *)0" is first cast to a function pointer (perfectly legal), and the resulting expression is assigned to the variable f2, of identical type, which should also be legal. Can you explain the problem? ================== Chris Volpe G.E. Corporate R&D volpecr@crd.ge.com
gwyn@smoke.brl.mil (Doug Gwyn) (11/16/90)
In article <13799@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes: -In article <14457@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes: -|>In article <1391@gtx.com> randy@gtx.UUCP (Randy D. Miller) writes: -|>-Can NULL always be cast to a function pointer, even in implementations -|>-that legally define NULL as (void *)0? For example, are the following -|>-lines legal? -|>- /* example 1 */ int (*f1)(void) = NULL; -|>- /* example 2 */ int (*f2)(void) = (int (*)(void))NULL; -|>No, the second is not required to be supported by the implementation, -|>but the first is (3.2.2.3). -What's wrong with the second example? A null pointer constant -represented by "(void *)0" is first cast to a function pointer (perfectly -legal), and the resulting expression is assigned to the variable -f2, of identical type, which should also be legal. Can you explain the -problem? The problem is that you can't point to any requirement in the standard that the implementation support casting (void*)0 to a pointer to function. A null pointer constant can be safely compared to a pointer to function, and it can be safely assigned to a pointer to function modifiable lvalue, but that's it. Casting a null pointer constant to pointer to function is not permitted in a strictly conforming program.
barmar@think.com (Barry Margolin) (11/16/90)
In article <14463@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >A null pointer constant can be safely compared to a pointer to function, >and it can be safely assigned to a pointer to function modifiable lvalue, >but that's it. Casting a null pointer constant to pointer to function is >not permitted in a strictly conforming program. I thought that the semantics of casting were equivalent to that of assignment to an object of the given type. So how can an assignment be valid but the corresponding cast be invalid? Or is this equivalence an old rule of thumb that isn't precisely correct? Are there other examples of assignments that don't have corresponding casts or vice versa? -- Barry Margolin, Thinking Machines Corp. barmar@think.com {uunet,harvard}!think!barmar
volpe@camelback.crd.ge.com (Christopher R Volpe) (11/16/90)
In article <14463@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes: |>In article <13799@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes: |>-In article <14457@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes: |>-|>In article <1391@gtx.com> randy@gtx.UUCP (Randy D. Miller) writes: [Deleted example] |>-|>No, the second is not required to be supported by the implementation, |>-|>but the first is (3.2.2.3). |>-What's wrong with the second example? A null pointer constant |>-represented by "(void *)0" is first cast to a function pointer (perfectly |>-legal), and the resulting expression is assigned to the variable |>-f2, of identical type, which should also be legal. Can you explain the |>-problem? |> |>The problem is that you can't point to any requirement in the standard |>that the implementation support casting (void*)0 to a pointer to function. It seems to me that the "spirit" of the cast operator is to make explicit a conversion where otherwise there would be no conversion or the conversion would be implicit rather than explicit. If the implicit conversion in example 1 is allowed, shouldn't the explicit conversion to the same thing be allowed? If the above is not true, then where in the standard does it say that an implementation must support casting an int into a float? I don't see it in 3.3.4? Doesn't this fall into the same category? Neither violates the constraint in 3.3.4, but then neither is explicitly supported. Both *conversions* are supported, if done via assignment operators. ================== Chris Volpe G.E. Corporate R&D volpecr@crd.ge.com
gwyn@smoke.brl.mil (Doug Gwyn) (11/16/90)
In article <1990Nov15.204858.20696@Think.COM> barmar@think.com (Barry Margolin) writes: >I thought that the semantics of casting were equivalent to that of >assignment to an object of the given type. Nope, conversion and assignment have different rules.
gwyn@smoke.brl.mil (Doug Gwyn) (11/16/90)
In article <13870@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes: >If the above is not true, then where in the standard does it say that >an implementation must support casting an int into a float? Conversions involving pointers have an explicit set of requirements in 3.3.4 beyond the basic semantics. The basic semantics suffice for interconversion of arithmetic values; 3.2.1.3 gives details. >Both *conversions* are supported, if done via assignment operators. I can't parse that. Certain license beyond 3.3.4 is granted in 3.3.16.1 for assignment involving certain pointers that meet the constraints of 3.3.16.1. If you have further questions about this, I suggest you send X3 a request for an interpretation ruling. Possibilities for misunderstanding are limitless, and I really cannot spend much more time on this than I already have. I think the standard is clear on this.
volpe@camelback.crd.ge.com (Christopher R Volpe) (11/16/90)
Sorry for wasting the bandwidth and your time, but... In article <14484@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes: |>In article <13870@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes: |>>If the above is not true, then where in the standard does it say that |>>an implementation must support casting an int into a float? |> |>Conversions involving pointers have an explicit set of requirements |>in 3.3.4 beyond the basic semantics. The basic semantics suffice for |>interconversion of arithmetic values; 3.2.1.3 gives details. 3.3.4 doesn't clearly define "basic semantics" and certainly doesn't say that it supports interconversion of arithmetic values. 3.2.1.3 describes what happens WHEN this type of conversion takes place, but 3.3.4 doesn't say that it supports the conversions in 3.2.1.3. If this is to be inferred from the phrase "the type name shall specify ... scalar type", in 3.3.4 Constraints, then the pointer conversion in question falls under the same category. The int->float conversion is no more explicitly sanctioned in 3.3.4 than the pointer conversion. The use of the word "converts" in 3.3.4 Semantics """""implies""""" (note quotes) that conversions that are legal "in general" are legal in a cast. Finally, K&R2 (yes, I know it ain't gospel) says in 2.7: The precise meaning of the cast is AS IF [emphasis mine] the expression were assigned to a variable of the specified type... So, is K&R2 wrong here? K&R1 says the same thing, and if this is legal and well defined in K&R1, and not legal and well defined in the Standard, and no diagnostic is required (since no constraint is violated) then this qualifies as a QUIET CHANGE, which the rationale fails to mention. BTW, Doug, since you claim that "(int (*)())NULL" is illegal, I assume you mean that it is illegal whether NULL is defined as "0" or "(void *)0", right? Your argument about null pointer constants doesn't (I assume) depend on the form in which that constant is specified. So the above subject line is a little misleading. |> |>>Both *conversions* are supported, if done via assignment operators. |> |>I can't parse that. Ok, I guess I wasn't clear. I simply meant that each type of conversion (int->float, null_pointer_constant->any_pointer_type) is sanctioned in 3.3.16.1. No cast is REQUIRED for either. So an implemention must be able to perform this kind of conversion, even if the Standard doesn't explicitly say that this kind of conversion can be done via a cast operator. |> |>Certain license beyond 3.3.4 is granted in 3.3.16.1 for assignment |>involving certain pointers that meet the constraints of 3.3.16.1. |> |>If you have further questions about this, I suggest you send X3 a request |>for an interpretation ruling. Possibilities for misunderstanding are |>limitless, and I really cannot spend much more time on this than I |>already have. I think the standard is clear on this. Ok, I would like to send X3 a request for an interpretation ruling. Can someone kindly tell me how to go about doing this? At the very least, even if X3 doesn't agree with me about the legality issue, I think it would be interested to know about the discrepancy with the rationale, which states that "the only integer that can safely be converted to a pointer is the constant 0 [3.2.2.3 rat.]" and that "The conversion of the integer constant 0 to a pointer is defined similarly to the Base Document [3.3.4 rat.(cast operator)]". ================== Chris Volpe G.E. Corporate R&D volpecr@crd.ge.com
gwyn@smoke.brl.mil (Doug Gwyn) (11/17/90)
In article <13920@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com (Christopher R Volpe) writes: >In article <14484@smoke.brl.mil>, gwyn@smoke.brl.mil (Doug Gwyn) writes: >|>In article <13870@crdgw1.crd.ge.com> volpe@camelback.crd.ge.com >(Christopher R Volpe) writes: >|>>If the above is not true, then where in the standard does it say that >|>>an implementation must support casting an int into a float? >|>Conversions involving pointers have an explicit set of requirements >|>in 3.3.4 beyond the basic semantics. The basic semantics suffice for >|>interconversion of arithmetic values; 3.2.1.3 gives details. >3.3.4 doesn't clearly define "basic semantics" and certainly doesn't >say that it supports interconversion of arithmetic values. The first paragraph of 3.3.4 Semantics is what I referred to as the basic semantics. The rest of that section are further conditions that apply solely to conversions involving pointers. It is clear what arithmetic values are, but not clear what pointer values are, thus the need for additional rules explaining what is required to be supported for pointers. >3.3.4 doesn't say that it supports the conversions in 3.2.1.3. For conversion by casts, 3.3.4 Semantics first paragraph covers it. For conversions upon assignment or an operation specified to be as if by simple assignment, 3.3.16.1 covers it. 3.3.16.1 Constraints limits what is permitted for pointers (in a strictly conforming program), and 3.3.4 Semantics past the first paragraph spells out all other ways that pointers may be converted (in a strictly conforming program). If you think that these portions should not have been buried in the sections where they are in fact found, many on X3J11 would tend to agree with you. >So, is K&R2 wrong here? Yes, assignment and conversion by cast have slightly different semantics. (I won't attempt to guess whether or not this was intentional, but it should be presumed to be so unless X3J11 later declares otherwise.) >BTW, Doug, since you claim that "(int (*)())NULL" is illegal, I assume >you mean that it is illegal whether NULL is defined as "0" or "(void *)0", >right? Your argument about null pointer constants doesn't (I assume) >depend on the form in which that constant is specified. So the above >subject line is a little misleading. The subject line was inherited from the original posting. Indeed, I don't think an implementation is required to accept (int (*)())0 but it also is not required to diagnose it (because it is not a syntactic or constraint violation), and thus it may elect to support it. (Many old implementations did. They need not stop doing so, but portable coders should become aware of this and avoid using such constructs.) >Ok, I would like to send X3 a request for an interpretation ruling. >Can someone kindly tell me how to go about doing this? Send a letter to X3 Secretariat: Computer and Business Equipment Manufacturers Association, 311 First Street, N.W., Suite 500, Washington DC 20001-2178. Briefly state the issue, including references to the relevant parts of the C standard, give a good example, state the possible alternative interpretations, give an argument for the interpretation you would prefer, and request that the matter be brought before the X3J11 technical committee for a decision on how to interpret the standard in such a context. P.S. Reminder: These opinions are mine, not necessarily X3J11's, and X3J11 may decide upon a different interpretation.
chris@mimsy.umd.edu (Chris Torek) (11/18/90)
In article <14498@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >Yes, assignment and conversion by cast have slightly different semantics. Aside from casts being permitted where assignments are not, I think this is a Bad Thing. Having a cast `mean' the same thing as an assignment, with the addition that (being explicit) it is a little more `powerful' and can override objections like `the pointers have different types', significantly reduces what I might call the `cognitive size' of the language. It is much easier to think of i = (short)j; as `get the value of j, put it in a short, and pull it back out' than as `apply a short cast, with separate semantics from assignment to a short, to get a result'. The latter requires one to carry the separate semantics around in one's head in order to be able to decide what the code above means. (Obviously one must always carry assignment semantics around, unless one never uses assignment. This, however, doubles the load.) >(I won't attempt to guess whether or not this was intentional, but it >should be presumed to be so unless X3J11 later declares otherwise.) I hope they do so declare. >Indeed, I don't think an implementation is required to accept > (int (*)())0 [or (int (*)(void))0 ?] >but it also is not required to diagnose it (because it is not a syntactic >or constraint violation), and thus it may elect to support it. (Many old >implementations did. They need not stop doing so, but portable coders >should become aware of this and avoid using such constructs.) This would make it difficult to pass a `nil pointer to function of no arguments returning int' to a function for which no prototype can be provided. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris
gwyn@smoke.brl.mil (Doug Gwyn) (11/18/90)
In article <27780@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes: >This would make it difficult to pass a `nil pointer to function of no >arguments returning int' to a function for which no prototype can be >provided. But not impossible, as a suitable pointer variable initialized to a null pointer (trivially accomplished) could be used. You could even define it to be const-qualified and hope the compiler is good at optimizations.
steve@groucho.ucar.edu (Steve Emmerson) (11/18/90)
In <14517@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes: >In article <27780@mimsy.umd.edu> chris@mimsy.umd.edu (Chris Torek) writes: >>This would make it difficult to pass a `nil pointer to function of no >>arguments returning int' to a function for which no prototype can be >>provided. >But not impossible, as a suitable pointer variable initialized to a null >pointer (trivially accomplished) could be used. You could even define >it to be const-qualified and hope the compiler is good at optimizations. Whoa! So the following is non-conforming: foo((int((*)(void)))NULL) where the prototype for foo() isn't in scope and it takes the indicated argument type. This has implications for those of us who, at least, *try* to write portable code (though, as Doug indicated, the problem is avoidable). Are you sure of this, Doug? Or do we need an Official Ruling? I know. When in doubt, request a ruling ;-). Steve Emmerson steve@unidata.ucar.edu ...!ncar!unidata!steve