4 ms·
A good exercise is to then guess why the spiral rule seems to work most of the time even though it is inherently wrong. The reason is that some types that are
by palotasb 8y ago
A good exercise is to then guess why the spiral rule seems to work most of the time even though it is inherently wrong.
The reason is that some types that are syntactically valid are forbidden by C/C++. You cannot have a function returning a function. A function returning an array. An array of functions. You can only have a pointer to these (function returning a pointer to function/array or an array of pointer to functions), and then they need to be correctly parenthesized. Then the spiral rule works because you only have two elements in a parenthesis and you can just go right and then go left... unless you have arrays of arrays which are legal, and then it doesn't work.
But the more correct rule would be to go right and parse all [] and () inside the current parenthesis level, then go parse the * -s (including const/volatile) on the left. Then repeat for outer parenthesis levels.
- aasasd 8y agoAha, so the final working algorithm is "the spiral rule but with a rightward detour on arrays of arrays."
- palotasb 8y agoForget the spiral rule. That's putting an if-statement into a buggy algorithm instead of using the correct algorithm. The simplest correct algorithm is to 1. From the identifier, go right one by one in the current parenthesis level and process every element. 2. From the identifier again, go left one by one in the current parenthesis level and process every element. 3. Repeat from step 1. except the "identifier" is the part you already processed. Within step 1, you will encounter arrays and function parameter lists. Within step 2, you will encounter pointers (in C++ also references), const and volatile modifiers, and named types. Neither algorithm covers it, but if there is no identifier to start with, then you start at the most nested level between the elements that may occur in step 1 and 2 and if you find a comma, you were just processing the type of a function parameter. Spiraling is unnecessary. Within step 1, you DON'T spiral because of arrays of arrays or you turn back because of a closing parenthesis so a spiral doesn't need to guide you. Within step 2, the elements on the right are all processed already, so a spiral going back right will hit nothing. For simple but common cases like "const int * ptr * const_ptr_to_const_int" you're spiraling around nothing on the right-hand side. Let's rework and simplify the examples from the http://c-faq.com/decl/spiral.anderson.html http://c-faq.com/decl/spiral.anderson.html page. Comments show what the result of parsing an element is. Process >> >> left to right and << << right to left. char * str [10]; // XXX >>>> "str is", "an array of 10..." // <<<< < XXX "pointer to", "char" char * ( * fp ) ( int, float * ); // XX "fp is" // < XX "a pointer to" // XXXXXXXX >>>>>>>>>>>>>>>> "a function taking (int, float*) and returning" // <<<< < XXXXXXXX "a pointer to", "char" void (* signal (int, void (* fp) (int))) (int); // < XX "fp is", "a pointer to" // XXXXXX >>>>> "a function taking (int) and returning" // <<<< XXXXXX "void", "and it is a function parameter to something else" // XXXXXX >>>>>>>>>>>>>>>>>>>>>>>> "signal is", "a function taking (int, void(*fp)(int)) and returning" // < XXXXXX "a pointer to" // XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX >>>>> "a function taking (int) and returning" // <<<< XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "void" const char * chptr; // <<<<< <<<< < XXXXX "chptr is", "a pointer to", "char", "that is const" // Notice how a spiral would make this worse and the page omits drawing it for a reason char * const chptr; // <<<< < <<<<< XXXXX "chptr is", "a constant", "pointer to", "char" volatile char * const chptr; // <<<<<<<< <<<< < <<<<< XXXXX "chptr is", "a constant", "pointer to", "char", "that is volatile" Notice that in the examples where Anderson drew any spirals, it only made sense because there was a single "<<"/">>" element to the left and to the right of the XXXX part. In all other cases, spirals don't make sense because you might have to avoid the element on the left or there is no element on the right to spiral through, you're just going right-to-left. Why fit a spiral when a straight-line arrow will do?
- kazinator 8y agoThe working algorithm is that the postfix operators in declarators (just like in expressions) have higher precedence than the unary ones (just like in expressions), but are overridable with parentheses (like in you know what). The declared identifier is, first and foremost, the clump of high precedence postfix things that are on it: a[3][4][5]; // a is an array of array of array b(int); // b is a function c(int)[3]; // c is an array of functions: nonexistent Then we consider the unaries: *** whatever; // whatever of pointer to pointer to pointer Then the declaration specifiers: int whatever; // whatever of/to int