12 ms·
In c89, there doesn't seem to be any distinction between pointer to functions and pointer to data. Do you know what c89 does explicitly say is undefined? Any p
by phire 1mo ago
In c89, there doesn't seem to be any distinction between pointer to functions and pointer to data.
Do you know what c89 does explicitly say is undefined? Any pointer arithmetic at all (or array subscripting) for pointers are not pointing to a member of an array. It's already invalid to do pointer arithmetic a pointer to a struct.
Not that it matters for this. This isn't doing pointer arithmetic, f is simply the identifier for the array of "sizeof(f) ints" that make up the function argument. The identifier is optional for a function declaration.
- kevin_thibedeau 1mo agoThere is a distinction. Lisp machines do not allow function pointers to become data pointers. The C standard accounts for that. Function pointers also have special decay behavior unlike data pointers. You can chain an unlimited number of '*'s and still get a valid function pointer. int f(int x) { return x + 1; } int main(int argc, char *argv[]) { int (*fp)(int) = f; printf("Answer: %d\n", fp(39) + (****fp)(1)); return 0; }
- phire 1mo agoNot sure about later versions, but c89 doesn't seem to forbid casting a function pointer to a data pointer (it does say that casting pointers has implementation defined aspects, so lisp machines aren't out of spec if they fail). The reason you can chain unlimited ''s is that f isn't actually a function pointer, it's a function designator. Function designators are automatically converted to function pointers in almost every case, but '' is special cased so that indirecting a raw function designator results in another function designator. Edit: Actually on a closer reading, maybe it does. C89 doesn't explicitly point it out as forbidden or undefined, it simply defines two classes (objects and functions) and only defines a very limited set of valid operations on pointers to functions. Though one operation that is implementation defined is converting a pointer to an int, and an int to a pointer. So with two steps through an implementation defined path, you have a data pointer to a function.
- geocar 1mo agovoid* and void(*)() are just not guaranteed to be the same size. If the latter is larger then those "implementation defined aspects" are what kicks in. It's nothing to do with the int. In some systems functions are smaller, since they're gate addresses in a jump table (who has more than 64k functions?), so that's no problem. But in some systems functions are bigger, and PCDOS systems running in real mode this was very common, and in those cases you'd need to get the extra bits from someplace else (union trick), or just design things another way.
- inigyou 1mo agoIn 1989 compilers weren't adversarial yet, and language specs weren't expected to be complete formal descriptions. The complete formal description was "whatever the compiler does" and "whatever the machine does" which was usually something sensible, unlike today.
- geocar 1mo agoI don't think it's bad enough to think of them as an adversary: I think letting the compiler do a little of the boring stuff is ok, and for the most part I am rarely surprised by this stuff. I don't -O3 and -funroll-loops and just forget-about-it either - I choose each of the -f options individually based on a combination of my understanding of what kinds of transformations they do, testing, and whether I am prepared to continuously keep those things in mind when programming. What are you thinking of?