How do curly braces and scope wоrk in C? - c

I'm currently trying to learn some C in my spare time. I have some experience in Java, and I'm therefore used to limit scoping of i.e. variables with curly braces. But I'm a bit confused, as it seems like Brian W. Kernighan/Dennis M. Ritchie's book "The C Programming Language" doesn't use a lot of curly braces, where I would assume it's normal to use them (from a Java perspective). E.g. 1.6 in the book where the code is:
while((c = getchar())) != EOF)
if (c >= '0' && c <= '9')
++ndigit[c-'0'];
else if() /*and so forth...*/
++nwhite;
else
++nother;
From a Java perspective I'm used to that only the first statement would be run then, because of the lack of curly braces, but the indentation suggests that everything will run (if, else if and else).
So what I'm asking is: What would run here, and why would it run? Are if, else if and else all in the scope of the while loop? Are there any conventions to refer to, that I can read to try to understand it better? Thanks in advance.

The while, if, else if, and else are followed by a single statement. This statement can be an actual line of C, or a block statement (surrounded by braces). The if, else if, and else are considered as one block.
So using braces, this would be the equivalent:
while((c = getchar())) != EOF) {
if (c >= '0' && c <= '9') {
++ndigit[c-'0'];
}
else if() { /*and so forth...*/
++nwhite;
}
else {
++nother;
}
}

In C, as in Java, an ìf and a while has one statement as its body, and if can have an optional else clause with one statement as its body. And in most places where you can have one statement, you can have a statement block, which is a list of statements surrounded by curly braces. This is not different in C than in Java.
In order to avoid ambiguity, if there are two ifs and one else, the else is defined to refer to the last one.
I. e.
if(a) x if(b) y else z
is defined to mean
if(a) x { if(b) y else z }
and not
if(a) x { if(b) y } else z
Here, x, y, and z are statements - which can be statement blocks as well.
However, having said all this, it is error prone to leave off the braces, and hence many coding guidelines suggest to always use the curly braces.

You may just treat if/else if/else instructions as one block - they can't really be divided, else cannot exist on it's own.
On the side note, it's sometimes confusing when you have a situation like
if(something)
if (somethingOther)
....
else
.....
If your code is long enough, you might get confused where to attach this else, so it's good to always use braces. As stated in comment, else always attaches to the "nearest" if.

If an if statement does not contain a curly brace, you can inline up to one semi colon after, and it will automatically assume the curly braces as such:
while((c = getchar())) != EOF){
if (c >= '0' && c <= '9')
{
++ndigit[c-'0'];
}
else if() /*and so forth...*/
{
++nwhite;
}
else
{
++nother;
}
}
So this is valid:
while(true) i++;
And this should not be valid:
while(true) i++; break; printf("hello");
if(true) printf("Hello"); break;

Related

C Unexpected Identifier - For Loop Increment

Not sure what I've done here (pretty new to C, have done some Python, Arduino and Java before).
int main(void){
unsigned long int sum = 0;
unsigned int count = 0;
for(unsigned long int i = 1; i <= 100; i++)
if(i%3 != 0 && i%5 != 0)
printf("cond%d\n", i);
count++;
sum += i; // $$here$$
return 0;
When I compile the code, I get 'error undefined identifier: i' on line 10 (indicated with $$here$$
C does not use indentation to structure program control (such as to indicate that more-indented statements are controlled by a less-indented statement above them). To group statements under the control of an if or other statement, you must use braces:
if(i%3 != 0 && i%5 != 0)
{
printf("cond%d\n", i);
count++;
sum += i;
}
The compiler error occurs because, due to the lack of braces, the scope of the for statements ends after the printf statement. So no declaration for i is visible at the sum += i; line, and the compiler complains.
The compiler will ignore most indentation and white space. When authors use indentation in C source code, it is to aid humans, not the compiler.
(C grammar does not require any braces around the if statement to keep it within the for statement’s control, because the if statement and the following lines form a single statement, once braces are added as shown above. However, many people would add braces just around the if statement to illustrate explicitly that it forms the body of the for statement. I treat this contextually, judging what is clearer in each situation.)

How are if statements in C syntactically unambiguous?

I don't know a whole lot about C, but I understand the basics and as far as I can tell:
int main() {
if (1 == 1) printf("Hello World!\n");
return 0;
}
and
int main() {
if (1 == 1)
printf("Hello World!\n");
return 0;
}
and
int main() {
if (1 == 1) {
printf("Hello World!\n");
}
return 0;
}
are all precisely syntactically equivalent. The statement is true; the string is printed; the braces are (apparently) optional.
Sometimes, especially here on SO, I see something like the following:
int main() {
if (1 == 1)
printf("one is one\n");
printf("is this inside the if statement??/who kn0WS\n");
return 0;
}
By the power vested in CodeGolf, I have been led to believe that C is whitespace-agnostic; the lexical analyser breaks the tokens up into their component parts and strips whitespace outside strings.
(I mean, the whole reason for the semicolons-on-every-statement-thing is so the parser can strip \n, \t, literal spaces and still know where each statement ends, right??)
So how is it possible to unambiguously parse the previous bit of code (or perhaps someone can come up with a better example of what I mean), if whitespace is to be disregarded?
If C programmers want to write in whitespace-dependent Pythonic syntax, why do they write C, and why is it taught wherever C is taught that it's okay to write lexically ambiguous (both to me, a programmer, and the computer) statements like this?
if (1 == 1)
printf("one is one\n");
printf("is this inside the if statement??/who kn0WS\n");
The second printf() should never execute inside the if statement.
The reason being that the previous line ends with a semicolon, which indicates the end of the if-block to execute.
(I mean, the whole reason for the semicolons-on-every-statement-thing
is so the parser can strip \n, \t, literal spaces and still know where
each statement ends, right??)
So how is it possible to unambiguously parse the previous bit of code
(or perhaps someone can come up with a better example of what I mean),
if whitespace is to be disregarded?
Parsing example:
if (1 == 1) // if - ( and ) - statements (or block) follow, skip all whitespace
// no { found -> single statement, scan until ; (outside quotes / comments)
printf("one is one\n"); // ; encountered, end of if-block
Without braces, only one statement belongs to the if-block.
But, as said already, it's a good habit to use braces. If you later add a statement (a quick temporary printf() for example), it will always be inside the block.
Special case:
int i = 0;
while(i++ < 10);
printf("%d", i);
Here printf() will only execute once. Mark the ; at the end of while().
In case of an empty statement, it's better to use:
while(i++ < 10)
;
to make the intention clear (or, as an alternative, an empty block {} can be used as well).
In C, an if statement takes exactly statement after the truth expression, regardless of indentation. Normally this statement is indented for clarity, but C ignores indentation. In any case, there is no amiguity in any of your examples.
What is ambiguous, in C and in many other languages, is the "dangling else". For instance, suppose you have a nested if statement with a single else after the second one. It could group as:
if (expr)
if (expr)
statement
else
statement
Or it could group as:
if (expr)
if (expr)
statement
else
statement
The only difference between these two is how they're indented, which C ignores. In this case, the ambiguity is resolved by using the first interpretation, i.e., the else statement binds to the nearest preceding if statement. To achieve the second interpretation, curly braces are needed:
if (expr) {
if (expr)
statement
}
else
statement
However, even in the first case, it's a good idea to include the curly braces, even though they aren't required:
if (expr) {
if (expr)
statement
else
statement
}
tl;dr The only ambiguity is in how difficult it is for a human to read. From the compiler's perspective, the syntax is perfectly unambiguous.
There are only two (compilable and syntactically acceptable) possibilities after an if statement:
Braces, as in
if(x) {
DoFoo();
}
// or
if(x) { DoFoo(); }
In this case, whatever is in the {...} will execute if the condition is met.
No braces, as in
if(x)
DoFoo();
// or
if(x) DoFoo();
In this case, only the next statement will execute if the condition is met.
You are correct that C is whitespace-agnostic. As a result, omitting the braces can lead to some tricky bugs. For example, in this code, DoBar() will execute whether or not the condition is met:
if(x)
DoFoo();
DoBar();
Inconsistent use of braces can also easily result in invalid code. For example, this looks valid (at a glance) from a human perspective, but it's not:
if(x)
DoFoo();
DoBar();
else
DoBaz();
None of the examples you posted are ambiguous from the compiler's perspective, but the no-braces versions are confusing from a human perspective. Leaving out the braces frequently leads to hard-to-find bugs.
Without braces it is just the next statement after the if. The whitespace does not matter
It is good practice and makes life easier to always use the braces. Good indentation as well. Code is then easy to read and does not lead to errors when people add/remove statements after the if
Readability is the only ambiguity in the statement:
if (1 == 1)
printf("one is one\n");
printf("is this inside the if statement??/who kn0WS\n");
The only time the first statement following the if(...) statement should execute, is if it is evaluated TRUE.
Braces, {...} help to remove readability ambiguities,
if (1 == 1)
{
printf("one is one\n");
}
printf("is this inside the if statement??/who kn0WS\n");
but the syntax rules are still the same.
Opinions vary, but I always choose to use braces.
Not using them is fine at the time code is written. But down the road, you just know someone will come along and add another statement under the first and expect it to be executed.
In general, in a statement or a loop with a single instruction, the curly brackets are optionals; instead, if you have two or more instructions you have to add them.
For example :
for(i = 0; i < 2; i++)
for(j = 0; j < 4; j++)
If(...)
printf(..);
else
printf(..);
Is equivalent to :
for(i = 0; i < 2; i++)
{
for(j = 0; j < 4; j++)
{
If(...)
{
printf(..);
}
else
{
printf(..);
}
}
}
As you may note this is more something related to indentation of the code. Personally I don't use curly brackets if I have a single instruction as doing that will make your code shorter and cleaner.
Another reason to use braces is that a simple typo can bite you hard:
#include <stdio.h>
int main(void) {
if (0 == 1)
printf("zero is one\n"),
printf("is this inside the if statement?? /who kn0WS\n");
return 0;
}
Look carefully...

What is the difference between IF and ELSE IF clauses?

I'm on the path of learning C with K&R. Besides the exercises in the book, I'm also doing some by myself. I wrote the following code, what it does is, it counts your input, gives you feedback how many words are left to reach the "goal" of 10 words and congratulates you as soon as you reach the 10 words.
#include <stdio.h>
main()
{
/* This programm will read your input, check the number of words,
and will congratulate when you reach a value of 10 words*/
int c, nw, counter;
nw = 0;
counter = 0;
while (nw < 10)
{
c = getchar();
if (c == ' ' || c == '\t' || c == '\n')
{
counter = 0;
}
else if (c != ' ' || c != '\t' || c != '\n')
{
if (counter == 0)
{
counter = 1;
++nw;
}
printf("Only %d words left\n", 10-nw );
}
}
}
Ok, in this version the code will not count blanks as words, resulting in the correct output of words left.
At first I wrote the code with only "if" instead of "else if". What it did was it also counted the blanks as words.
The question I am asking is why ? Wheres the difference in using if or else if.
As I understand is, that compiler will check whether a condition is met or not met. This should also be the case when only using if. As everything is the same expect else if instead of if, I can't figure out what the problem is.
It looks like this might be a case of overthinking a problem. The logic you've ended up with, aside from being wrong, is overly complicated. Your question of the difference between if and else if is fair, and I promise I will address it in my answer.
First, let me restate what you are trying to do:
Read input, count number of words, and congratulate you when you reach 10 words
From your code, I believe your intention is to split words based on spaces, tabs, and newlines. There are many ways to split words, but for the purposes of this question, your intended method is fine.
The problem, of course, is that your logic doesn't work. You have a condition that can never be false:
else if (c != ' ' || c != '\t' || c != '\n')
Think about it (hint: the else doesn't change the condition itself). Say it out loud if that helps: You are looking for a condition where c isn't a space, or c isn't a tab, or c isn't a newline. Remember that logical or (||) is an inclusive or, in other words, the expression is true if any of the conditions are true. For example, let's suppose c is a space. The first condition (c != ' ') fails, but the 2nd one, c != '\t' is true because a space is not a tab. Thus, the entire expression is true. In fact, that expression will be true for any value of c.
But what about else?
As I said, the else part of the else if doesn't make a difference here. The only thing else if does differently is essentially tack itself on as a new condition to your if statement. Let's look at a simpler example:
if (a == 1) {
/* a is 1 */
}
if (a != 1 && b == 2) {
/* a isn't 1, but b == 2 */
}
That's an example of two completely independent if statements. It's a perfect example of where to use else, because as you probably noticed, the 2nd if statement tests for the inverse of the 1st. (a != 1). So, the above can be simplified as follows:
if (a == 1) {
/* a is 1 */
else if (b == 2) {
/* a isn't 1 and b is 2 */
}
In the else if block, we needn't test for a != 1, as that's implied because we only evaluate the else statement if the 1st if conditional was false.
Note also that else if is actually a combination of two separate keywords. It is equivalent to:
else {
if (b == 2) { ... }
}
However, by convention we omit the optional braces and write it as:
else if (b == 2) { ... }
In fact, in some cases we don't need that 2nd if at all:
if (a == 1) {
printf("a is 1!\n");
} else {
printf("a isn't 1. In fact, it's %d.\n", a);
}
Simplified Version
So, now there's no need to get caught up in else if. Focus on your logic, and do your best to simplify it. I will simplify it for you, however I encourage you to skip over this part and try it on your own first:
char c;
int in_word = 0;
while (nw < 10) {
c = getchar();
if (c == ' ' || c == '\t' || c == '\n') {
/* If we were in a word, then count that word! */
if (in_word) {
nw++;
printf("You only have %d words to go!", 10 - nw);
}
in_word = 0; /* We are not in a word now */
} else {
in_word = 1; /* Now we're in a word
}
}
Not sure if this is the answer you are looking for. But two separate if statements are both evaluated no matter what. With "if" and "else if", first if is evaluated if it is true, else if is skipped. When "if" is not true "else if" is evaluated.
else if (c != ' ' || c != '\t' || c != '\n')
This line of code is probably not doing what you intended. It works fine as an else if, but in fact it doesn't really check anything (you could actually replace it with a simple else)! For any conceivable character, it's will always be either not a space, or not a tab, or not a newline. What you really want is the conjunction of those statements, rather than the current disjunction:
else if (c != ' ' && c != '\t' && c != '\n')
This checks that the character is neither a space, a tab, nor a newline. It would work even with else removed, as a separate if-statement. However, you really should just go with a simple else.
In your if condition, if you use simple if .. else construct, it will be equivalent to
if (c == ' ' || c == '\t' || c == '\n')
{
...
}
else if (c != ' ' && c != '\t' && c != '\n') //Notice &&.
//Apply De-morgan's law over if conditio.
{
...
}
In the above case else if is not required, if else is suffice to use. Using if .. else would be more efficient than the given code.
However, logically it may have same effect as your given code.
As per logic, else if is not required.
The difference you will get in terms of number of comparison operation as explained below.
Current code:
For any non-space character such as 'a', it will perform 4 comparisons (|| and && are short-circuited).
For any space character, there will be max 3 comparisons.
In if-else code, max of 3-comparison will be performed.
This difference is not so significant. And can only be noticed when code is run billions of times.
if else has extra advantage of maintenance. In the above code in OP, if there is any change in if part, it should be reflected in else if part also.
Hence, to me it seems it will be beneficial to go with just if else rather if else if.

Is it possible for an IF statement to be in the for loop's third expression (C)?

While learning from K&R, I tried to modify the while loop in exercise 1.5.3 in to a for loop:
#include <stdio.h>
main()
{
int c, nl;
for (nl = 0; (c = getchar()) != EOF; if (c == '\n'){++nl}){}
printf("%d\n", nl);
}
SIDE NOTE: I apologize if this code looks abominable. For now, I am just trying to write valid C code, and when I feel that I have adapted to the language's general concepts, I will start writing code for "humans."
When I try to build and run the code, this message appears,
error: expected expression before 'if'
I tried to search this error, but I could only find answers that either referred to other languages or ternary operations, which I would like to avoid if possible. Thanks in advance for any insight into resolving this issue, if it even is one.
No, but if you really want to squeeze it in the loop (although I would not recommend it), you could do
nl += (c == '\n')? 1: 0
Or, as #LogicG8 suggested, just
nl += (c == '\n')
although it is a bit less clear and may violate some coding guidelines.
No, not possible. The third clause has to be an expression and if statements are statements, not expressions.
if is a statement, the header of a for loop can only contain expressions, not statements.
You can use a ternary operator there, it's the way to perform a conditional in an expression.
for (nl = 0; (c = getchar()) != EOF; (c == '\n') ? ++nl : 0) {}
In short: This is not possible, you could use some inline-ifs somehow, though.
But, even in case it would work, you shouldn't do it because....
...the simpler the for-loop, the better the optimization. Don't confuse the compiler.
...you might also confuse other developers if they have to read and unerstand that code.
Keep it simple whenever possible.

assignment works as a condition

Consider the following Code,
int i;
while(i=0)
printf("Hello");
Now Generally speaking i=0 is an assignment and not a condition for while to check.
But the GCC compiler lets it go with a warning and even evaluates it correctly (does not execute the print statement).
Why? I usually would do with parenthesis for the truth value but my juniors feel that I am wrong and there is no real reason for the parenthesis in this!
EDIT: Zeroing down on the 'actual' doubt, Please consider the following test case
int callme(){
return 0;
}
int main(int argc,char*argv[]){
int c;
while(c = callme()){
printf("Calling...\n");
}
return 0;
}
The expression i = 0 does 2 things:
Has the side effect of storing o in i
Yields the value 0
I usually would do with parenthesis for the truth value but my juniors
feel that i am wrong and there is no real reason for the parenthesis
in this
It's usually a hint to the compiler meaning "I actually want this, I didn't forget a =, shut up".
For your specific case there's no reason to write if (i = 0): you already know what if (0) does. But it's pretty useful when used as:
if ((i = some_function()))
...
i=0 is always an assignment (unless you have it as part of int i = 0; where it is an initialization). But any non-void expression may appear inside the condition of a while loop and if it evaluates to non-zero, the body of the loop will be executed, and if it is zero, the body of the loop will not be executed.
The notation:
while (i = 0)
printf("Hello\n");
is always equivalent to:
i = 0;
There is very little justification for writing the loop at all.
People do write other expressions:
while (c = getchar())
...process EOF or a non-null character...
But that's usually a bug. It is more likely that you should be writing:
while ((c = getchar()) != EOF)
...process a character - possibly null...
or even:
while ((c = getchar()) != EOF && c != '\0')
...process a non-null character...
The first getchar() loop gets a warning from GCC; the latter two do not because of the the explicit test of the value from the assignment.
The people who write a condition like this:
while ((c = getchar()))
really annoy me. It avoids the warning from GCC, but it is not (IMNSHO) a good way of coding.
When you use an assignment operator such as
a=0;
You assign the value to 'a', and still return the number 0.
To test your question, I tried these lines of codes:
int a;
printf("%d", a=0);
and these lines displayed 0.
Then, I tested another set of codes:
int b;
printf("%d", b=15);
Here, the lines displayed 15.
So, if you do:
while(a=0)
{
printf("zero");
}
The (a=0) statement would return false, thus not displaying anything.
But if you do:
while(a=15)
{
printf("fifteen");
}
The "fifteen" will be displayed endlessly, because the statement (a=15) will return a non zero value, or 15, which is not false, not zero, thus it is true. :)
As cnicutar has told above the assignment also yields the value zero.
Some additional info:
It is a common coding mistake for people to omit an extra '=' whereby the comparison becomes an assignment.
An easy way to avoid this is to write the comparison as below, in which case even if a '=' is missed compiler will give an error
while(0 == i)
{
prinf("Hello");
}

Resources