I am preparing for our programming test and I read this long long A[(10,10)] declaration (it was in some previous test in our course), which I have no more information about. Only other thing I know about it, is that it is not possible to initialize variable declared this way by calling A[5][1]=something. Otherwise, I would assume it is some kind of 2D array.
It also could be comma operator but the gcc compiler actually doesn't recognise it.
abc.c:3:16: error: expected ']'
long long A[10,10];
^ abc.c:3:13: note: to match this '['
long long A[10,10];
Do you have any clue if it is a thing, or just some nonsense? (I was trying to Google it, but these things aren't that easy to find...)
Thank you.
In array declarations, a constant-expression is expected, which is a subset of expression. Specifically, the comma operator and assignment expressions are not part of the set.
Array declarators are a kind of direct-declarator:
direct-declarator: ... |
direct-declarator "[" constant-expression? "]";
constant-expression: conditional-expression;
expression: assignment-expression | expression "," assignment-expression;
assignment-expression: conditional-expression |
unary-expression assignment-operator assignment-expression;
So the grammar doesn't allow for a comma here.
To answer your question "Do you have any clue if it is a thing, or just some nonsense?": Any declaration which is that non-intuitive that experienced programmers have to consult cpp reference is IMHO clearly nonsense.
I tested expression long long A[(10,10)] with Apple LLVM 8.0 compiler and C99 language dialect, and it worked. When consulting the cpp reference concerning comma operator, one can find the following:
Top-level comma operator is also disallowed in array bounds
// int a[2,3]; // error
int a[(2,3)]; // OK, VLA array of size 3 (VLA because (2,3) is not a constant expression)
So long long A[(10,10)] seams to be equivalent to long long A[10], where the 10 is the second part of non top-level comma expression (10,10). Interesting thing may be that an array declared this way is treated as VLA (variable length array, which's size is determined at runtime).
Related
If I do:
int j = ({int x = 7; x+3;});
In i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5646) gcc it compiles just fine. The block in question ({int x = 7; x+3;}) returns the value of the last statement as the value of the block. If you remove the parenthesis it doesn't compile. Can I expect this to work in most C compilers?
Additionally, what is the name for this construct? I have had a lot of trouble searching for it because search engines don't index () or {} and C is a terrible search term. I also haven't been able to find anything about it in any of my books, probably because I don't know what to look for.
It's a GCC extension:
A compound statement enclosed in parentheses may appear as an expression in GNU C. This allows you to use loops, switches, and local variables within an expression.
Recall that a compound statement is a sequence of statements surrounded by braces; in this construct, parentheses go around the braces. For example:
({ int y = foo (); int z;
if (y > 0) z = y;
else z = - y;
z; })
is a valid (though slightly more complex than necessary) expression for the absolute value of foo ().
The last thing in the compound statement should be an expression followed by a semicolon; the value of this subexpression serves as the value of the entire construct. (If you use some other kind of statement last within the braces, the construct has type void, and thus effectively no value.)...
If you remove the parenthesis it doesn't compile.
Without the parentheses, the compiler will treat this as an aggregate initialization block and will fail when it sees the int keyword. You cannot have keywords in initializer blocks.
6.7.8 Initialization
11 The initializer for a scalar shall be a single expression, optionally enclosed in braces. The
initial value of the object is that of the expression (after conversion); the same type
constraints and conversions as for simple assignment apply, taking the type of the scalar
to be the unqualified version of its declared type.
6.2.5 Types
21 Arithmetic types and pointer types are collectively called scalar types. Array and
structure types are collectively called aggregate types.
Can I expect this to work in most c compilers?
No. Looks like a non-standard GNU extension.
Additionally, what is the name for this construct?
I wonder if there is any. Actually, this is similar to what macros typically do.
You can expect it to work in most versions of GCC.
You can expect it to work almost nowhere else - it is a GCC extension.
The section of the GCC manual that describes the feature is titled 'Statements and Declarations in Expressions':
A compound statement enclosed in parentheses may appear as an expression in GNU C.
Later it says:
Any temporaries created within a statement within a statement expression will be destroyed
at the statement’s end. This makes statement expressions inside macros slightly
different from function calls.
So, 'statement expression' seems to be the name used in the documentation.
I'm new to C programming and I'm struggling really hard to understand why this works
#include <stdio.h>
int main() {
int l = -5;
char arr[l];
printf("content: %s; sizeof: %d\n", arr, sizeof(arr));
}
with output:
content: ; sizeof: -5
and this doesn't:
#include <stdio.h>
int main() {
char arr[-5];
printf("content: %s; sizeof: %d\n", arr, sizeof(arr));
}
with output:
name.c:6:10: error: size of array ‘arr’ is negative
6 | char arr[-5];
| ^~~
I was expecting an error also from the first example, but I really don't know what's happening here.
Neither version of the program conforms to the C language specification (even after the format specifiers are corrected to properly match the size_t argument). But the two cases are semantically different, and they violate different provisions of the language specification.
Taking this one first:
char arr[-5];
The expression -5 is an integer constant expression, so this is a declaration of an ordinary array (not a variable-length array). It is subject to paragraph 6.7.6.2/1 of the C17 language spec, which says, in part:
In addition to optional type qualifiers and the keyword static, the [
and ] may delimit an expression or *. If they delimit an expression
(which specifies the size of an array), the expression shall have an
integer type. If the expression is a constant expression, it shall
have a value greater than zero.
(Emphasis added.)
That is part of a language constraint, which means that the compiler is obligated to emit a diagnostic message when it observes a violation. In principle, implementations are not required to reject code containing constraint violations, but if they accept such codes then the language does not define the results.
On the other hand, consider
int l = -5;
char arr[l];
Because l is not a constant expression (and would not be even if l were declared const), the provision discussed above does not apply, and, separately, arr is a variable-length array. This is subject to paragraph 6.7.6.2/5 of the spec, the relevant part requiring of the size expression that:
each time it is evaluated it shall have a value greater than zero
The program violates that provision, but it is a semantic rule, not a language constraint, so the compiler is not obligated to diagnose it, much less to reject the code. In the general case, the compiler cannot recognize or diagnose violations of this particular rule, though in principle, it could do so in this particular case. If it accepts the code then the runtime behavior is undefined.
Why this program emits -5 when you compile and run it with your particular C implementation on your particular hardware is not established by C. It might be specified by your implementation, or it might not. Small variations in the program or different versions of your C implementation might produce different results.
Overall, this is yet another example of C refusing to hold your hand. New coders and those used to interpreted languages and virtual machines seem often to have the expectation that some component of the system will inform them when they have written bad code. Sometimes it does, but other times it just does something with that bad code that might or might not resemble what the programmer had in mind. Effective programming in C requires attention to detail.
I have just finished reading through the language reference ( appendix A) of the 2nd edition of K&R's "The C Programming language" for a new job.
Please note, that I have worked out how to read C declarations by all the guidance here on SO ( so thanks ;) ). The explanation has always been a reference to the precedence of expressions and operators in C.
I can infer the same precedence table from the Reference Manual a.k.a. Appendix A, §A7 in said book, no problem.
However, §A7 deals with the precedence of expression operators, as is said at the beginning of the section - the square brackets postfix-expression [ expression ] are herein viewed as the subscript operator, the asterisk * is viewed as the indirection operator and parentheses ( argument-expression-list) or () pertain to function calls, see §A7.3.
So why do so many people refer to this precedence table, when the subject is C declarations, and even get a couple of upvotes on SO? The only precedence it claims to define is that of certain expression operators.
How declarations are to be parsed is written in §A8, especially §A8.6 goes into the basics of more complicated declarations.
But throughout the whole of Appendix A, there are no statements about in what precedence exactly parentheses, square brackets, asterisks, type names and so on are to be parsed.
On page 216 it says, that parentheses may change the binding of complex declarators, but now how ( although I have a hunch, see below in my example).
Let me give you an example in 'pseudo-code' where I am was at a loss, using §A8.8 ( the T's stand for the type specifiers, the D's for the declarators):
Reading a C declaration:
Original declaration:
char (*(*f())[])()
/* Have to use A8.6.3, because of how the decl. looks: */
Now let T=char , D=(*(*f())[])()
D=D1(), where D1=(*(*f())[])
The type of f, according to A8.6.3 is then
:L1 /* label 1 */
f is "type-modifiers of f in D1" function returning char /* note, in the book it says 'type-modifiers of f in T D1, but this wouldn't make any sense! */
Now look at T D1 = char (*(*f())[])
:ALT_A
The type of f in D1 = (*(*f())[]) is the same as that of f in
D2=*(*f())[]
/* At least, this is how parentheses are supposed to be understood, according to the beginning of A8.6,
where it says:
In a declaration T D where D has the form
( D1 )
then the type of the identifier in the declaration D1 is the same as that of D. */
/* note about that quote: the identifier in D has no type, did the authors mean to imply 'incomplete' types?
So, using incomplete types:*/
Looking at D2 = T1 D3[] ----> have to use A8.6.2, where T1 = * , D3 = (*f()),
so the type of f in T1 D3 is
f is "type-modifiers of f in D3" array of pointers ()
look at f in D3 = (*f()), or equivalently, f in D4 = *f(). Have to use A8.6.3. --->
f is a function returning a pointer to ()
going up a level: f is "type-modifiers of f in D3" array of pointers ()
translates to: f is a function returning a pointer to an array of pointers ()
going up another level: f is "type-modifiers of f in D1" function returning char
translates to: f is a function returning a pointer to an array of pointers to functions returning a char
This is the result, cdecl also shows.
Note that I haven't used any precedence table, just the reference manual and the sections directly pertaining declarations.
So how and why do so many people refer to operator precedence?
It seems to me like every reference to operator precedence when people ask about C declarations is a wrong answer giving a "parsing algorithm" that magically turns out to give the right result.
And secondly, how come that K&R seem to be so inexact with so many things ( see my /* */-remarks in the pseudo-code)? I would have expected more precision, I mean they obviously knew all the details and had to be able to think precise.
Sorry for the messed up formatting, btw.. I have spent most of this day trying to write down how I manually parse this and at the same time understanding what K&R might have meant with this and that formulation...
List of sources, where declarations and operator precedence are said to be connected:
http://users.ece.utexas.edu/~ryerraballi/CPrimer/CDeclPrimer.html
("All one has to understand any complex C declaration then, is to know that these declarations are based on the C operator precedence chart, the same one you use to evaluate expressions in C:")
http://binglongx.com/2009/01/25/how-to-read-a-cc-declaration/
How are (complex) declarations parsed in terms of precedence and associativity?
("In fact, the designers of C were wise enough to make declarations use the same "precedence rules" as expressions. This is the "declaration follows usage" rule. For example, in the expression", see first answer by Brian .)
Operator precedence in C Definitions
Expert C Programming: Deep C Secrets, p. 74, 28 5 star reviews on Google Books?
It's because declaration reflects use.
In the grammar for declarations, a declarator is separated into a pointer part followed by a direct-declarator, wherein the array-forming [] and function-forming () constructs are applied, so forming an array or function type has higher precedence than forming a pointer type.
And, reflecting this, in the grammar for expressions, the postfix operators [] and () have higher precedence than the prefix unary operator *.
This means that when we see int *f() we know that this is a function returning pointer to int, because *f() gives a value of type int; likewise we know that char (*g)() is a pointer to a function returning char, because (*g)() must give a value of type char.
This is why errors were allowed to creep into Appendix A; no-one is expected to read it, because applying the declaration-reflects-use rule gives the correct result and is a lot easier to apply.
Background
The last time I asked about whether parentheses were causing implicit cast (here), #pmg was nice enough to point out that "Nothing in C is done below int" But there, the discussion was about bitwise operators, and the parentheses turned out to be just a distraction.
Introduction
Below, the parentheses are the main attraction. Or, to be more boring but precise, the only operators I see are the parentheses and assignment operators.
At this reference about the C parentheses operator, I do not see anything about parentheses changing the type (outside of typecast syntax, which is not this case).
Meanwhile, here's a reference that reminds that there is automatic type conversion on assignment, but I don't think that will explain the static analysis tool behavior I will describe here.
As in my previous question, "OK" means that the static analysis tool did not warn about an implicit type conversion, and "NOT OK" means that it did.
int main(void)
{
unsigned int ui;
int i;
ui = (256U); // NOT OK (*) (1)
i = (256U); // NOT OK (*) (2)
i = 256; // OK
i = 256U; // NOT OK
ui = 256U; // OK (3)
ui = 256; // NOT OK
return(0);
}
I can understand them all except the first two - what do the parentheses do? If they do nothing in the way of implicit typecasting, then I would expect (1) to be OK and (2) to be NOT OK. If they do automatic type promotion of types smaller than int up to int, then I would expect (1) to be NOT OK and (2) to be OK. But this tool says that both are NOT OK.
Is this a static analysis tool error, or is the tool correct and there's something else I need to learn about implicit type conversions in C?
(BTW I hope that the value 256 is small enough not be causing overflow on my machine ...)
First, let's clear up some terminology. Nothing can cause an "implicit cast", because there is no such thing. A cast is a explicit operator, consisting of a type name in parentheses preceding an expression, such as (double)42; it specifies a conversion. Conversions can be either explicit (specified by a cast operator) or implicit, as in double x = 42;. So what you're really asking is whether parentheses can cause an implicit conversion.
And the answer, at least in the code you've shown us, is no.
Quoting the C99 standard (3.7 MB PDF), section 6.5.1p5:
A parenthesized expression is a primary expression. Its type and value
are identical to those of the unparenthesized expression. It is an
lvalue, a function designator, or a void expression if the
unparenthesized expression is, respectively, an lvalue, a function
designator, or a void expression.
And since 256U is already a primary expression, the parentheses make no difference at all; parentheses generally indicate precedence, but in this case there is no predecence to indicate.
What static analysis tool are you using? You should probably submit a bug report.
The tool is confused somehow. There's no casting here. Those parentheses just indicate precedence.
If I do:
int j = ({int x = 7; x+3;});
In i686-apple-darwin10-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5646) gcc it compiles just fine. The block in question ({int x = 7; x+3;}) returns the value of the last statement as the value of the block. If you remove the parenthesis it doesn't compile. Can I expect this to work in most C compilers?
Additionally, what is the name for this construct? I have had a lot of trouble searching for it because search engines don't index () or {} and C is a terrible search term. I also haven't been able to find anything about it in any of my books, probably because I don't know what to look for.
It's a GCC extension:
A compound statement enclosed in parentheses may appear as an expression in GNU C. This allows you to use loops, switches, and local variables within an expression.
Recall that a compound statement is a sequence of statements surrounded by braces; in this construct, parentheses go around the braces. For example:
({ int y = foo (); int z;
if (y > 0) z = y;
else z = - y;
z; })
is a valid (though slightly more complex than necessary) expression for the absolute value of foo ().
The last thing in the compound statement should be an expression followed by a semicolon; the value of this subexpression serves as the value of the entire construct. (If you use some other kind of statement last within the braces, the construct has type void, and thus effectively no value.)...
If you remove the parenthesis it doesn't compile.
Without the parentheses, the compiler will treat this as an aggregate initialization block and will fail when it sees the int keyword. You cannot have keywords in initializer blocks.
6.7.8 Initialization
11 The initializer for a scalar shall be a single expression, optionally enclosed in braces. The
initial value of the object is that of the expression (after conversion); the same type
constraints and conversions as for simple assignment apply, taking the type of the scalar
to be the unqualified version of its declared type.
6.2.5 Types
21 Arithmetic types and pointer types are collectively called scalar types. Array and
structure types are collectively called aggregate types.
Can I expect this to work in most c compilers?
No. Looks like a non-standard GNU extension.
Additionally, what is the name for this construct?
I wonder if there is any. Actually, this is similar to what macros typically do.
You can expect it to work in most versions of GCC.
You can expect it to work almost nowhere else - it is a GCC extension.
The section of the GCC manual that describes the feature is titled 'Statements and Declarations in Expressions':
A compound statement enclosed in parentheses may appear as an expression in GNU C.
Later it says:
Any temporaries created within a statement within a statement expression will be destroyed
at the statement’s end. This makes statement expressions inside macros slightly
different from function calls.
So, 'statement expression' seems to be the name used in the documentation.