This just isn't true. C is not portable assembler. It was never intended to be. I hear it claimed, and it is wrong every time somebody calls C a low-level language close to assembler. You can make some roughly reasonable assumptions about what comes out of the compiler, but often it is not what you think it is.
Let's challenge this specific claim, that when you do "if (foo == bar)" -- I corrected the syntax error, which is a symptom of C's high-level syntax and not of the underlying assembly code -- you compare one value to another and then jump. For this challenge,I will write some trivial code that we should be able to make easy assumptions about, and I will compile it with debugging enabled so that I can dump the results with gdb.
$ gcc -g example.c
1 #include <stdio.h>
2
3 int main() {
4 int foo = 10;
5 int bar = 20;
6 if (foo == bar) {
7 printf("Fun\n");
8 }
9 return 0;
10 }
Dump of assembler code for function main:
0x0000000100000ef8 <main+0>: push rbp
0x0000000100000ef9 <main+1>: mov rbp,rsp
0x0000000100000efc <main+4>: sub rsp,0x10
0x0000000100000f00 <main+8>: mov DWORD PTR [rbp-0x4],0xa
0x0000000100000f07 <main+15>: mov DWORD PTR [rbp-0x8],0x14
0x0000000100000f0e <main+22>: mov eax,DWORD PTR [rbp-0x4]
0x0000000100000f11 <main+25>: cmp eax,DWORD PTR [rbp-0x8]
0x0000000100000f14 <main+28>: jne 0x100000f22 <main+42>
0x0000000100000f16 <main+30>: lea rdi,[rip+0x19] # 0x100000f36
0x0000000100000f1d <main+37>: call 0x100000f30 <dyld_stub_puts>
0x0000000100000f22 <main+42>: mov eax,0x0
0x0000000100000f27 <main+47>: leave
0x0000000100000f28 <main+48>: ret
We see that in the very basic version of this code with absolutely no optimizations and doing the silliest things that we can, we store our two values into some memory locations, perform a comparison (cmp), and jump if not equal. We can see that the jump leads us to the puts() call.
Now, let's get smarter. The variables foo and bar do not change value, and we only work with two variables in the routine. Therefore, we could optimize by storing those values in temporary registers instead of using expensive memory transfers. Further,since our two constants are being compared and will always return a false, we actually have a section of code -- the printf -- that is dead code, that can be completely removed from final compilation. Well, that's simple, and everyone who uses C in production at least turns on some minor optimization:
$ gcc -g -O1 example.c # the only difference is the -O1
Dump of assembler code for function main:
0x0000000100000f34 <main+0>: push rbp
0x0000000100000f35 <main+1>: mov rbp,rsp
0x0000000100000f38 <main+4>: mov eax,0x0
0x0000000100000f3d <main+9>: leave
0x0000000100000f3e <main+10>: ret
This does not look like our C code at all! And thankfully so! What a waste of space and CPU time it would have been had we treated C like an interpreted language! C is a high-level language with numerous compiler implementations that can intelligently convert the human-readable code into the binary code that represents the real situation behind the code.
The point here is that you are not properly guessing the assembler code that will be produced. The compiler is doing a better job of that; that is the compiler's job. As a programmer, you can just focus on the algorithm. C is not an assembler macro language. For that, you would use things like "gas".