C Language Notes#
Thanks to Frank for opening the door to modern C language for a wide range of students
[TOC]
The blog posts written in June were too superficial. Starting from this note, the content of the blog articles will be elevated to a higher level!
Content comes from the free course by instructor Micro_Frank, and the notes are specifically derived from the content taught. If there is any infringement, please contact the blogger!
Chapter One#
First Program: C Language Execution Process#
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
This program is a basic example of C language, demonstrating how to output a line of text. The #include <stdio.h>
statement includes the standard input-output library, int main()
is the entry point of the program, printf("Hello World!\n");
is used to print the string, and return 0;
indicates that the program has ended successfully.
C Language Story · 1#
Selected Textbook: C Primer Plus#
It is recommended to read with English and Chinese side by side.
C Language Story · 2#
Declaration, Definition, Assignment#
#include <stdio.h>
int main()
{ // Declaration
int number = 123;
// Definition
printf("%d\n", number);
// Assignment
return 0;
} // Instructor Frank_PPT also talked about the use of the TAB key and tab characters in this section.
The code is mainly a simple C program that includes the concepts of declaration, definition, and assignment. Here is a detailed analysis: c #include // Include standard input-output library
- #include
: This is a preprocessor directive that includes the standard input-output library, allowing the program to use functions like printf
for input and output operations. c int main() { // Main function, entry point of the program
- int main()
: Defines the main function, where the execution of the C program begins. The return type is int
, indicating that this function will return an integer value. c int number = 123; // Define an integer variable and initialize
- int number = 123;
: Here we declare and define an integer variable named number
, initializing its value to 123
. In C language, variable declaration and definition are usually done in a single statement. c printf("%d\n", number); // Print the value of variable number
- printf("%d\n", number);
: This line of code intends to output the value of the variable number
, but it contains an error. The correct usage should be printf("%d\n", number);
. In this statement, %d
is a format specifier used to output integer type data, and \n
is a newline character. In the erroneous version, the string "number"
is treated as plain text output rather than the value of the variable. c return 0; // End the main function, returning 0 indicates the program executed successfully }
- return 0;
: This line of code indicates the end of the main function and returns 0
to the operating system, typically representing successful execution of the program. Corrected Code Combining the above analysis, the correct code should be: c #include int main() { int number = 123; // Define and initialize variable printf("%d\n", number); // Correctly print the value of the variable return 0; // Return 0, indicating the program was successful }
### Summary This code demonstrates the basic structure of C language, including variable declaration and definition, use of standard libraries, output format usage, and the return value of the main program. The only thing to note is the correct usage of the format string in printf
.
Identifiers#
identifier:
nondigit
identifier
digit
nondigit:
_ a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
digit:
0 1 2 3 4 5 6 7 8 9
The compiler treats uppercase and lowercase letters as different characters.
add
ADD
Add
aDD
Microsoft Specific
Names that start with two underscores or start with a single underscore followed by an uppercase letter.
Microsoft uses underscores and uppercase letters as prefixes for macro names and uses double underscores as prefixes for Microsoft-specific keyword names.
Valid Identifiers
j
count
temp1
top_of_page
skip12
LastNum
Keywords#
Chapter Two Data Types#
Comments#
// Line
Comment
/*
Block
Comment
Comment
*/
Number Systems#
Variables#
// int variable
#include <stdio.h>
int main(){
// integer assignment
int sun_flower = 9673;
printf("The current value of sunshine: %d\n", sun_flower);
return 0;
}
Integer Types#
Syntax: Defined Type Identifier = Value
int type storage in memory#
Computer storage units:
1G = 1024MB
1MB = 1024KiB
1KiB = 1024Bytes
1024Bytes = 8Bits
00000000 | 00000000 | 00000000 | 00000000 | int |
---|
0 is used to display positive and negative, and can represent a total of $ 2^{32} - 1 $ values.
#include <stdio.h>
int main()
{
int number = 100;
// Represented in decimal
printf("Decimal: %d\n", number);
printf("Octal: %o\n", number);
printf("Hexadecimal (lowercase): %x\n", number);
printf("Hexadecimal (uppercase): %X\n", number);
return 0;
}
Floating Point Numbers#
Floating point numbers include: 2.75, 3.16E7, 2e-8, etc.
How does the computer store floating point numbers?#
Floating point number: Sign (1) + Fraction (23) + Exponent (8) int type 32 bits
One Step : 3.14159=> $ 314159×10^{-5} $ First convert to int type.
Two Step : How to store the exponent? $ 2^{8} $ = 256 From 0 to 255, the number to the left of 127 is negative exponent, and the offset is the exponent value.
The storage of floating point numbers in computers typically follows the IEEE 754 standard, which defines a method for representing floating point numbers. A floating point number consists of three main parts: the sign bit, the exponent part, and the mantissa (or significant digits). Here is the general storage format for floating point numbers: 1. Sign bit: Occupies 1 bit, indicating the sign of the value. 0 indicates a positive number, and 1 indicates a negative number. 2. Exponent part: Occupies a certain number of bits (for example, 32-bit floating point uses 8 bits, and 64-bit floating point uses 11 bits), indicating the range of the value. The exponent is generally biased (i.e., a constant is added, called the bias), allowing for the representation of negative exponent values. 3. Mantissa: Occupies the remaining bits. For standard floating point representation, the mantissa is typically stored in the form of 1.XXXX, implicitly having a 1 in front (called the implicit leading bit). In the storage structure of 32-bit floating point (single precision) and 64-bit floating point (double precision), the specific bit allocation is as follows: - Single precision (32 bits): - Sign bit: 1 bit - Exponent: 8 bits - Mantissa: 23 bits - Double precision (64 bits): - Sign bit: 1 bit - Exponent: 11 bits - Mantissa: 52 bits Using this format, computers can represent a very large range of values and precision for the fractional part, but floating point representation also has rounding errors and range limitations. In computers, floating point numbers are typically stored in binary form, meaning they are saved and operated on based on binary values rather than decimal. This storage method makes operations on floating point numbers more efficient.
float and double types#
The range of float type is approximately between 3.4E-38 and 3.4E+38.
Decimal numbers are generally >1, so normalized floating point numbers are often greater than 1, mantissa (occupying the remaining bits). For standard floating point representation, the mantissa is typically stored in the form of 1.XXXX, implicitly having a 1 in front (called the implicit leading bit).
Therefore, the implicit length of the mantissa for float is 24 bits, and for double it is 53 bits.
Single precision (32 bits) - Double precision (64 bits)
Storage > Precision choose double, Storage < Precision choose float
Printing floating point numbers#
#include <stdio.h>
int main() {
float temperature = 36.5f;
float humidity = 48.3f;
float speed_of_sound = 343.5e2f;
float length = 12.3f, width = 23.45f, height = 34.56f;
printf("Temperature: %f\n", temperature);
printf("Humidity: %f\n", humidity);
printf("Speed of Sound: %f\n", speed_of_sound);
printf("Length: %f x %f x %f\n", length, width, height);
// Loss of precision
// double %lf, float %f
return 0;
}
C99 regulations on floating point numbers#
#include <stdio.h>
int main () {
float num = 123.456;
printf("Using %%f: %f\n", num);
// %e %E scientific notation formatted output
printf("Using %%e: %e\n", num);
printf("Using %%E: %E\n", num);
// %a %A hexadecimal floating point representation
printf("Using %%a: %a\n", num);
printf("Using %%A: %A\n", num);
return 0;
}
Overflow of floating point numbers#
#include <stdio.h>
#include <float.h>// Open to see
int main() {
float max_float = FLT_MAX;
float overflow = max_float * 1000.0f;
// OverFlow
float min_float = FLT_MIN;
float underflow = min_float / 1000.0f;
// UnderFlow
printf("Maximum Float: %e\n", max_float);
printf("OverFloat: %e\n", overflow);
printf("Minimum Float: %e\n", min_float);
printf("UnderFloat: %e\n", underflow);
return 0;
}
Nan & Infinity#
#include <stdio.h>
#include <float.h>
#include <math.h>
int main() {
// Positive infinity
float positive_infinity = INFINITY;
printf("Positive Infinity: %f\n", positive_infinity);
// Negative infinity
float negative_infinity = -INFINITY;
printf("Negative Infinity: %f\n", negative_infinity);
// Division by 0 produces infinity
float num = 1.0f;
float infinity = num / 0.0f;
printf("0.0 / 0.0 = %f\n", nan);
// Nan 0/0
// float nan = 0.0f /0.0f;
// printf("0.0f / 0.0f =%f\n", nan)
// Square root of a negative number
float negative_sqrt = sqrt(-1.0f);
printf("sqrt(-1.0f) = %f\n", negative_sqrt);
return 0;
}
Round to nearest even (banker's rounding)#
#include <stdio.h>
int main() {
// Round half away from zero
// IEEE 754
// Round to nearest, ties to even
// Banker's rounding
// 3.14159
float number = 3.14159f;
printf("%.4f\n", number);
// 3.15
// 3.25
return 0;
}
Difference between double and long double in research and enterprise#
#include <stdio.h>
int main() {
// double
// float loses precision
// 3D rendering
// Interest NASA
// Floating point constant 3.14
// 3.14 defaults to double type, specifically noted as 3.14f
return 0;
}
In computer science and programming, double
and long double
are two floating point data types used to represent numbers with decimals. Their usage in research and enterprise may sometimes have different focuses, mainly reflected in the following aspects:
- Precision Requirements:
- Research: Scientific research often involves highly precise calculations, such as physical and chemical simulations. In such cases,
long double
may be prioritized to improve calculation accuracy and avoid errors in floating point operations. - Enterprise: In commercial applications, the precision requirements for floating point numbers are usually not as strict, and
double
can meet most needs, especially in financial calculations and statistical analyses.
- Performance Considerations:
- Research: Scientific calculations may focus more on the accuracy of calculations rather than speed, but in some high-performance computing scenarios, it is still necessary to balance precision algorithms with computational performance.
- Enterprise: Enterprise applications often pursue performance, response time, and resource utilization efficiency, so they may prefer to use
double
to improve calculation speed while ensuring a certain level of precision.
- Platform Dependency:
- Research: In the research field, researchers consider differences between different hardware platforms and take cross-platform compatibility into account when choosing floating point types.
- Enterprise: Enterprises may focus more on software maintainability and development efficiency, typically choosing more commonly used and standardized
double
types to gain better development support and community resources.
- Language and Tool Support:
- Research: Researchers may use specialized scientific computing libraries (such as NumPy, SciPy, etc.) that consider support for various floating point types in their design.
- Enterprise: In enterprise environments, development teams may prefer to use generic data types, relying on standard libraries and storage processes, so
double
types are more common.
In summary, although double
and long double
are both floating point types, their specific usage in research and enterprise may vary due to differences in requirements, performance, and platforms.
Comparison of effective precision between float and double, principles and calculations#
#include <stdio.h>
int main() {
float float_num = 1.0 / 3.0;
double double_num = 1.0 / 3.0;
printf("Float precision: %20f\n", float_num);
printf("Double precision: %.20lf\n", double_num);
// Comparison of effective precision between float and double
printf("Defined max precision for double: %d\n", FLT_DIG);
printf("Defined max precision for float: %d\n", DBL_DIG);
// 53 divided by log₂10 24 divided by log₂10 is the precision
}
float (single precision floating point): Typically occupies 4 bytes (32 bits) with 1 bit for the sign, 8 bits for the exponent, and 23 bits for the mantissa (significant digits). Typically has an effective precision of about 7 decimal digits.
double (double precision floating point): Typically occupies 8 bytes (64 bits). 1 bit for the sign, 11 bits for the exponent, and 52 bits for the mantissa. Typically has an effective precision of about 15 to 16 decimal digits.
The representation of floating point numbers follows the IEEE 754 standard. Its value can be represented by the following formula: [ \text = (-1)^{sign} \times (1 + fraction) \times 2^{exponent} ]
sign: Sign bit, determines positive or negative. fraction: Mantissa, determines the precision of the value. exponent: Exponent, determines the size of the value. bias: Bias, for float
it is 127, for double
it is 1023.
In banking, fixed-point numbers (database MySQL) are used.
char & ASCII#
#include <stdio.h>
int main() {
char mych = 'a'; // Actually stored using int type, converting A to int type int(97)
printf("mych: %c\n");
// ASCII American Standard Code for Information Interchange generally uses 7 bits including 128 characters
}
Escape Sequences Backslash \#
Escape Sequence | Represents |
---|---|
\a | Bell (alert) |
\b | Backspace |
\f | Form feed |
\n | New line |
\r | Carriage return |
\t | Horizontal tab |
\v | Vertical tab |
\' | Single quote |
\" | Double quote |
\\ | Backslash |
\? | Question mark |
\ ooo | ASCII character in octal representation |
\x hh | ASCII character in hexadecimal representation |
\x hhhh | Unicode character in hexadecimal representation (if this escape sequence is used for wide character constants or Unicode string text). For example, WCHAR f = L'\x4e00' or WCHAR b[] = L"The Chinese character for one is \x4e00" . |
// Clear screen
printf("\033[2J");
// Move cursor
printf("\033[%d;%dH", 3, 3);
Boolean Type bool#
#include <stdio.h>
#include <stdbool.h>
int main() {
// true or false
// Convert to 1 or 0
bool is_game_win = true;
bool is_game_over = false;
return 0;
}
Constants const and #define macros#
#include <stdio.h>
#define PI 3.14
int main() {
// Constants
const double MAX_USER = 100;
printf("PI: %lf\n", PI);
return 0;
}
Conclusion of Chapter Two#
Chapter Three Operators#
Introduction to Operators#
1. Arithmetic Operators
- Unary
++
(increment) ,--
(decrement),+
(addition) and-
(subtraction) operators - Binary
*
(multiplication) ,/
(division) ,%
(modulus) ,+
(addition) and-
(subtraction)
2. Relational Operators
Operator | Relationship Tested |
---|---|
< | The first operand is less than the second operand |
> | The first operand is greater than the second operand |
<= | The first operand is less than or equal to the second operand |
>= | The first operand is greater than or equal to the second operand |
== | The first operand is equal to the second operand |
!= | The first operand is not equal to the second operand |
Data Objects Lvalue and Rvalue#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main() {
// Data objects
// Lvalue
// Rvalue
// Operators
uint32_t apple_box = 5;
uint32_t orange_box = 5;
printf("There are %" PRIu32 " apples in the apple box\n", apple_box);
printf("There are %" PRIu32 " oranges in the orange box\n", orange_box);
uint32_t total_fruit = apple_box + orange_box;
printf("There are %" PRIu32 " fruits in the box\n", total_fruit);
return 0;
}
Multiple Assignments#
Prefix and Postfix Increment and Decrement#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main() {
int32_t value = 5;
int32_t result_1;
int32_t result_2;
// Postfix increment, assign first, then increment
result_1 = value++;
// Prefix decrement, decrement first, then assign
result_2 = --value;
printf("After postfix increment, result_1 = %" PRIu32 ", result_2 = %" PRIu32 ", value = %" PRIu32 "\n", result_1, result_2, value);
return 0;
}
Bitwise Shift Operators#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint8_t num = 22;
num >> 2; // High bits are filled with zeros, low bits are pushed out
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint8_t num = 22;
// num >> 2; High bits are filled with zeros, low bits are pushed out
uint8_t num = 22;
printf("Original number: %" PRIu8 " (binary: 00010110)\n", num);
uint8_t left_shifted = num << 2;
printf("Left shifted by 2: %" PRIu8 " (binary: 01011000)\n", left_shifted);
uint8_t right_shifted = num >> 2;
printf("Right shifted by 2: %" PRIu8 " (binary: 00000101)\n", right_shifted);
return 0;
}
Why is multiplication using shift operators faster than direct multiplication?
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint8_t num = 25;
// uint32_t result = num * 1024;
printf("Result: %" PRIu32 "\n", result);
// ALU is responsible for basic operations
return 0;
}
The reason shift operators are faster than direct multiplication operators (*
) is mainly due to differences in their underlying implementations.
Here are some specific reasons:
Underlying Operations: Multiplication operations are typically more complex than addition and bitwise operations at the computer's lower levels. Multiplication requires more hardware resources and time, as it involves multiple additions and possibly storing intermediate results. In contrast, shift operations only require simple adjustments to the positions of binary bits, which is usually a very fast operation.
CPU Instruction Set Optimization: Many modern CPUs provide specialized instructions for performing shift operations, which are highly optimized at the hardware level. In contrast, multiplication instructions, while also optimized, generally have higher latency due to their complexity.
Specific Scenarios: For multiplication by powers of two, such as multiplying by 2, 4, 8, etc., shift operations can replace multiplication. For example, multiplying by 2 can be done by shifting left by 1 bit, and multiplying by 4 can be done by shifting left by 2 bits. This substitution can significantly improve performance in specific cases.
Compiler Optimization: Many programming language compilers automatically replace certain multiplication operations (especially those by powers of two) with corresponding shift operations during the optimization phase, thus improving code execution efficiency.
In summary, while we typically do not need to worry about these details at a high level, understanding the efficiency differences of these operations can be helpful in performance-sensitive applications.
Note that left shifting an int type may lead to overflow.
Logical Truth and Falsehood, C Relational Operators#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int a = 10;
int b = 20;
// xxx ? xxx : xxx determines true or false
bool greater = a > b;
printf("a > b: %d\n", greater);
printf("a > b: %s\n", greater ? "true" : "false");
bool less = a < b;
printf("a < b: %d\n", less);
printf("a < b: %s\n", less ? "true" : "false");
bool not_equal = a != b;
printf("a != b: %d\n", not_equal);
printf("a != b: %s\n", not_equal ? "true" : "false");
bool equal = a == b;
printf("a == b: %d\n", equal);
printf("a == b: %s\n", equal ? "true" : "false");
bool greater_or_equal = a >= b;
printf("a >= b: %d\n", greater_or_equal);
printf("a >= b: %s\n", greater_or_equal ? "true" : "false");
bool less_or_equal = a <= b;
printf("a <= b: %d\n", less_or_equal);
printf("a <= b: %s\n", less_or_equal ? "true" : "false");
return 0;
}
Conditional Expression Operator#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int score = 89;
printf("Your score level: %s\n", score >= 60 ? "Pass" : "Fail");
return 0;
}
#include <stdio.h>
int main() {
int score = 85;
// You can modify this score as needed
const char* result = (score >= 90) ? "Excellent" : (score >= 85) ? "Good" : (score >= 60) ? "Pass" : "Fail";
printf("%s\n", result);
return 0;
}
Bitwise Operators & ^ |#
Operator | Description |
---|---|
& | The bitwise "AND" operator compares each bit of its first operand with the corresponding bit of its second operand. If both bits are 1, the corresponding result bit is set to 1. Otherwise, it is set to 0. |
^ | The bitwise "XOR" operator compares each bit of its first operand with the corresponding bit of its second operand. If one bit is 0 and the other is 1, the corresponding result bit is set to 1. Otherwise, it is set to 0. |
**` | `** |
& Bitwise AND
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int a = 12;
int b = 25;
// & Binary bit comparison, output 1 only if both are 1, for example
printf("%d\n", 12 & 25);
// Clear specific bits, check if a certain bit is 1
return 0;
}
| Bitwise OR
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int a = 2;
int b = 5;
// | Binary bit comparison, output 1 if there is a 1, for example 0111
printf("%d\n", a | b);
// Set specific bits, combine flag bits
return 0;
}
^ Bitwise XOR
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int a = 2;
int b = 10;
// ^ Binary bit comparison, output 1 only if one is 1 and the other is 0, for example 1000
printf("%d\n", a ^ b);
// Logical XOR operation
// Flip specific bits, swap two variable values, check for differences
return 0;
}
Bitwise NOT ~#
Masks and Circuit Remote Control LED Light Exercise#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
void print_binary(uint8_t num);
int main() {
uint8_t status = 0b00001100; // Initial status
printf("Initial status: 0b");
print_binary(status);
printf("\t(Binary)\n");
status = status & 0b11111011; // Mask control
printf("Final status: 0b");
print_binary(status);
printf("\t(Binary)\n");
return 0;
}
void print_binary(uint8_t num) {
for (int index = 7; index >= 0; index--) {
printf("%d", (num >> index) & 1);
}
}
Logical Operators && ||#
Operator | Description |
---|---|
&& | The logical "AND" operator produces a value of 1 if both operands are non-zero. If either operand is equal to 0, the result is 0. If the first operand of the logical "AND" operation is equal to 0, the second operand will not be evaluated. |
**` |
Compound Assignment Operators#
The operands of compound assignment operators can only be of integer and floating point types.
#include <stdio.h>
/*
struct BigStruct {
//..
//..
};
void update(BigStruct& bs) {
BigStruct temp = someExp();
bs = bs + temp;
bs += temp;
}
*/
int main() {
int base_number = 8;
int add_number = 2;
int sub_number = 3;
int mul_number = 2;
int div_number = 5;
int mod_number = 4;
base_number += add_number;
// In-place Modification
base_number -=
base_number *=
base_number /=
base_number %=
base_number <<=
base_number >>=
base_number &=
base_number |=
base_number ^=
return 0;
}
Comma Operator#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint32_t a = 1, b = 2, c = 4;
uint32_t result = (a += 1, b -= 1, c += 3);
printf("a = %d, b = %d, c = %d, result = %d", a, b, c, result);
return 0;
}
Microsoft Learn
// cpp_comma_operator.cpp
#include <stdio.h>
int main () {
int i = 10, b = 20, c= 30;
i = b, c;
printf("%i\n", i);
i = (b, c);
printf("%i\n", i);
}
Output:
20
30
[collapse status="false" title="Microsoft Learn Explanation"]
The comma operator has left-to-right associativity. Two expressions separated by a comma will be evaluated from left to right. The left operand is always evaluated, and all side effects will be completed before the right operand is evaluated.
In some contexts (such as function argument lists), the comma can be used as a separator. Do not confuse this use of the comma as a separator with its use as an operator; the two usages are entirely different.
Consider the expression e1, e2
. The type and value of this expression are those of e2; the result of evaluating e1 will be discarded. If the right operand is an lvalue, the result is an lvalue.
In the usual cases where the comma is used as a separator (for example, in function or aggregate initializer argument lists), the comma operator and its operands must be enclosed in parentheses. For example:
C++ Copy
func_one( x, y + 2, z );
func_two( (x--, y + 2), z );
In the function call to func_one
, three arguments are passed, separated by commas: x
, y + 2
, and z
. In the function call to func_two
, the parentheses force the compiler to interpret the first comma as a sequencing operator. This function call will pass two arguments to func_two
. The first argument is the result of the sequencing computation (x--, y + 2)
, which has the value and type of the expression y + 2
; the second argument is z
.
[/collapse]
Order of Operations and Precedence#
Symbol ^1^ | Operation Type | Associativity |
---|---|---|
[ ] ( ) . -> ++ -- (postfix) | Expression | Left to Right |
sizeof & * + - ~ ! ++ -- (prefix) | Unary | Right to Left |
typecasts | Unary | Right to Left |
* / % | Multiplication | Left to Right |
+ - | Addition | Left to Right |
<< >> | Bitwise Shift | Left to Right |
< > <= >= | Relational | Left to Right |
== != | Equality | Left to Right |
& | Bitwise "AND" | Left to Right |
^ | Bitwise "XOR" | Left to Right |
` | ` | Bitwise "OR" |
&& | Logical "AND" | Left to Right |
` | ` | |
? : | Conditional Expression | Right to Left |
= *= /= %= += -= <<= >>= &= ^= ` | =` | Simple and Compound Assignment ^2^ |
, | Sequence | Left to Right |
^1^ Operators are listed in descending order of precedence. If multiple operators appear on the same line or in a group, they have the same precedence.
^2^ All simple and compound assignment operators have the same precedence.
Expressions can contain multiple operators of the same precedence. When multiple such operators of the same level appear in an expression, the evaluation will be performed according to the associativity of that operator, either from right to left or from left to right. The direction of evaluation does not affect the result of expressions that include multiple multiplications (*
), additions (+
), or binary bitwise (&
, |
, or ^
) operators at the same level. The order of operations is not defined by the language. The compiler may evaluate such expressions in any order as long as it can guarantee consistent results.
Only the sequence operator (,
), logical "AND" (&&
), logical "OR" (||
), conditional expression (? :
), and function call operators constitute sequence points, thus ensuring that a specific order of evaluation is followed for their operands. The function call operator is a set of parentheses immediately following the function identifier. The sequence point operator (,
) ensures that its operands are evaluated in left-to-right order. (The comma operator in function calls is different from the sequence point operator and does not provide any such guarantees.) For more information, see Sequence Points.
Logical operators also ensure that their operands are evaluated in left-to-right order. However, they will evaluate the minimum number of operands necessary to determine the result of the expression. This is called "short-circuit" evaluation. Therefore, some operands of the expression may not be evaluated. For example, in the expression
x && y++
the second operand (y++
) is only evaluated if x
is true (non-zero). Therefore, if y
is false (0), x
will not be incremented.
Example
The following list shows how the compiler automatically binds multiple example expressions:
Expand Table
Expression | Automatic Binding |
---|---|
`a & b | |
`a = b | |
`q && r |
In the first expression, the bitwise "AND" operator (&
) has a higher precedence than the logical "OR" operator (||
), so a & b
forms the first operand of the logical "OR" operation.
In the second expression, the logical "OR" operator (||
) has a higher precedence than the simple assignment operator (=
), so b || c
is grouped as the right operand in the assignment. Note that the value assigned to a
will be either 0 or 1.
The third expression shows a correctly formatted expression that may generate unexpected results. The logical "AND" operator (&&
) has a higher precedence than the logical "OR" operator (||
), so q && r
is grouped as an operand. Since logical operators ensure that their operands are evaluated in left-to-right order, q && r
is evaluated before s--
. However, if the result of q && r
is non-zero, s--
will not be evaluated, and s
will not decrease. If not decreasing s
causes problems in the program, s--
should be shown as the first operand of the expression, or s
should be decremented in a separate operation.
The following expression is illegal and will generate diagnostic messages at compile time:
Illegal Expression | Default Grouping |
---|---|
p == 0 ? p += 1: p += 2 | ( p == 0 ? p += 1 : p ) += 2 |
In this expression, the equality operator (==
) has the highest precedence, so p == 0
is grouped as an operand. The conditional expression operator (? :
) has the next highest level of precedence. Its first operand is p == 0
, and the second operand is p += 1
. However, the last operand of the conditional expression operator is treated as p
rather than p += 2
, because the match for p
will bind more tightly to the conditional expression operator than the compound assignment operator. Since += 2
has no left operand, a syntax error occurs. You should use parentheses to prevent such errors and produce more readable code. For example, you can use parentheses to correct and clarify the previous example as follows:
( p == 0 ) ? ( p += 1 ) : ( p += 2 )
See Also
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
int32_t result;
result = a * b + c << d > e ? b : c * sizeof(++e) / sizeof(int32_t);
printf("Result: %" PRId32 ", result");
return 0;
}
Among unary operators, & - + etc., the prefix * is the dereference operator.
& Bitwise AND
is different from prefix &
, the latter has higher precedence!
Chapter Three Branching and Control#
Decision Control#
If the weather is sunny — choose to go out;
Logic && || ;
Humans have decision-making abilities, and logical judgments allow programs to choose!
The premise is: let the program determine whether the "weather status" is true or false.
For example, a weather application:
Temperature < 30° Temperature > 30°
Programming languages:
- Must be applied to real environments
- Flexibility, randomness
- It is impossible to be comprehensive; if there is perfection, it must be a lie.
if Statement and if-else#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint32_t number = 100;
// if statement
if (number > 10) {
printf("This number is greater than 10!\n");
printf("This number is greater than 10!\n");
}
if (number >= 100) {
printf("This number is greater than or equal to 100!\n");
}
else {
printf("This number is less than 100!\n");
}
return 0;
}
Short-circuit Behavior of Logical AND and OR#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
bool is_weather_sunny = false;
bool is_venue_available = true;
if (is_weather_sunny && is_venue_available) {
printf("The event will proceed as scheduled!\n");
}
else {
printf("The event cannot proceed as scheduled!\n");
if (!is_weather_sunny) {
printf("Reason: The weather is not sunny!\n");
}
if (!is_venue_available) {
printf("Reason: No venue available!\n");
}
}
return 0;
}
// Vending machine
// Only supports coins
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void) {
const uint32_t PRICE = 3; // drink's price
uint32_t balance = 0; // now total_coin
uint32_t coin; // every coin in it
puts("The drink's price is $5, please!");
while (balance < PRICE) // Before enough amount is inserted
{
puts("Don't!"); // Refuse transaction
scanf_s("%" PRIu32, &coin);
if (coin == 1 || coin == 2 || coin == 5) // Determine the amount type
{
balance += coin; // Assign and add
printf("You have inserted $%" PRIu32 "\n", balance);
}
else {
printf("Sorry! We do not accept $%" PRIu32 " coins\n", coin);
}
}
if (balance > PRICE) {
printf("Change: %" PRIu32 "\n", balance - PRICE);
}
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void) {
// Player enters different conditions of the room
uint32_t coins = 15;
bool is_vip = true;
bool have_special_tool = false;
if (is_vip) {
puts("VIP Enter!\n");
}
else {
puts("Not VIP Exit!\n");
}
if (coins >= 10 || have_special_tool) {
puts("Enter!\n");
}
else {
puts("Exit!\n");
}
return 0;
}
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void) {
const uint32_t all_laps = 100;
uint32_t current_laps = 0;
puts("Start!");
while (current_laps < all_laps) {
current_laps++;
printf("Completed lap %" PRIu32 ".\n", current_laps);
}
return 0;
}
// Vending machine
// Only supports coins
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void) {
const uint32_t price = 10; // drink's price
uint32_t balance = 0; // now total_coin
uint32_t coin; // every coin in it
puts("The drink's price is $5, please!");
while (balance < price) // Only start looping when enough amount is inserted
{
puts("Don't!"); // Refuse transaction
// Simulate coin insertion
scanf_s("%" PRIu32, &coin);
if (coin == 1 || coin == 2 || coin == 5) // Determine the amount type
{
balance += coin; // Assign and add
printf("You have inserted $%" PRIu32 "\n", balance);
}
else {
printf("Sorry! We do not accept $%" PRIu32 " coins\n", coin);
}
}
if (balance > price) {
printf("Change: %" PRIu32 "\n", balance - price);
}
return 0;
}
// Be sure to write printf to see the effect.
// When encountering loop issues
// Avoid using multiple times in the loop to prevent unnecessary errors
// Write a program to calculate the sum
#include <stdio.h>
#include <inttypes.h>
int main(void) {
uint32_t sum = 0; // Set initial sum to 0
uint32_t number = 1; // The value of number for the first time
while (number != 0) // Set 0 as the method to exit the loop
{
scanf_s("%" PRIu32, &number); // scanf reassigns number, the first value of number is not actually used.
sum += number;
}
printf("The sum is %" PRIu32 "\n", sum);
return 0;
}
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
int main() {
uint32_t sum = 0; // Set initial sum to 0
char input[50]; // We will convert the user's input character to a number
char* end;
puts("Please enter a series of numbers, separated by Enter, we will calculate their sum, output q to end");
while (true) // Set 0 as the method to exit the loop
{
errno = 0;
puts("Enter a number:");
scanf_s(" %49s", &input, 50);
if (input[0] == 'q' && input[1] == '\0') {
break;
}
long number = strtol(input, &end, 10);
// Convert the input character to a number and add it to the total sum
// The ASCII of 0 is 48
if (end == input || *end != '\0') {
printf("Invalid input, please enter a number or the character q\n");
}
else if (errno == ERANGE || number < 0 || number > UINT32_MAX) {
printf("Number out of range! Please enter a positive integer less than or equal to %u\n", UINT32_MAX);
}
else {
sum += (uint32_t)number;
}
}
printf("The sum is %" PRIu32 "\n", sum);
return 0;
}
#include <stdio.h>
#include <inttypes.h>
#include <stdbool.h>
int main() {
uint32_t sum = 0; // Set initial sum to 0
uint32_t number; // The value of number for the first time
puts("Please enter a series of numbers, separated by Enter, we will calculate their sum, output 0 to end");
while (true) // Set 0 as the method to exit the loop
{
scanf_s(" %" PRIu32, &number);
if (number == 0) {
break;
}
sum += number;
}
printf("The sum is %" PRIu32 "\n", sum);
return 0;
}
// Use of guard clauses: car rental case
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
void check_car_rent(uint8_t age, uint8_t driving_exp_years);
int main(void)
{
check_car_rent(22, 3);
return 0;
}
void check_car_rent(uint8_t age, uint8_t driving_exp_years) {
// Guard clauses
if (age < 21) {
puts("Not eligible, age is insufficient!");
return;
}
if (driving_exp_years < 1) {
puts("Not eligible, driving experience is insufficient!");
return;
}
}
// Simplifying logical expressions
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
// Logical expressions should be simplified!
bool is_weekend = true;
bool has_enter = true;
if (!has_enter) {
return 0;
}
return 0;
}
State Machine: Managing Complex State Transitions#
#include <stdbool.h>
#include <inttypes.h>
int main(void)
{
// State machine: managing complex state transitions using switch case
uint8_t traffic_state = 0;
switch (traffic_state)
{
case 0:
puts("red");
traffic_state = 1;
break;
case 1:
puts("yellow");
traffic_state = 2;
break;
case 2:
puts("green");
traffic_state = 0;
break;
default:
puts("???");
break;
}
return 0;
}
Difference Between switch-case and if-else#
// Difference Between switch-case and if-else
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void) {
return 0;
}
The Role of Loops in Life#
// The Role of Loops in Life
#include <stdio.h>
#include <stdbool.h>
#include <inttypes.h>
int main(void) {
int32_t number;
scanf_s("Please enter: %d %d %d", &number, &number, &number); // Do not use scanf in actual development
// not write \n in it
printf("The numbers you entered are: %d %d %d\n", number, number, number);
return 0;
}
The Difference Between do-while and while#
// The Difference Between do-while and while
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main(void) {
uint32_t total_laps = 10;
uint32_t current_laps = 0;
puts("Penalty running starts!");
do {
current_laps++;
printf("The runner completed lap %" PRIu32 "\n", current_laps);
} while (current_laps < total_laps);
return 0;
}
The Practical Use of do-while#
// The Practical Use of do-while
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
int main(void) {
uint32_t choice;
do {
puts("**** Main Menu ****");
puts("1. New Game");
puts("2. Load Game");
puts("3. Exit");
scanf_s("%" PRIu32, &choice);
switch (choice) {
case 1:
puts("**** Creating a new game!");
break;
case 2:
puts("**** Loading save!");
break;
case 3:
puts("**** Exiting the game!");
break;
}
} while (choice != 3);
return 0;
}
Random Number Guessing Game Example#
// Random Number Guessing Game Example
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <time.h>
#include <stdlib.h>
int main(void) {
uint32_t secret_num, guess, status;
char buffer[50];
srand(time(NULL));
// Generate a random number between 1-100
secret_num = rand() % 100 + 1;
puts("Take a guess!");
do {
puts("Please enter your guess:");
fgets(buffer, sizeof(buffer), stdin);
status = sscanf_s(buffer, "%d", &guess);
// Guard clause
if (status != 1) {
puts("Invalid input!");
continue;
}
if (guess < secret_num) {
puts("Too low!");
}
else if (guess > secret_num) {
puts("Too high!");
}
} while (guess != secret_num);
printf("Congratulations, you guessed it right!");
return 0;
}
continue Statement#
// continue statement
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
uint32_t sum = 0;
int32_t number;
puts("Please enter a series of positive integers, you can enter a negative number, enter 0 to end");
while (1) {
puts("Please enter a number");
scanf_s("%d", &number);
if (number == 0) {
break;
}
if (number < 0) {
continue; // continue skips the rest of this loop
}
sum += number; // Count
}
printf("The result is: %" PRIu32 "\n", sum);
return 0;
}
Using continue and break with Conditional Judgments#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main() {
uint32_t number;
puts("Please enter a number (0-100), enter -1 to end the program");
while (1) {
puts("Please enter a number:");
scanf_s("%d", &number);
// Guard clause! Check condition to end
if (number == -1) {
break;
}
if (number < 0 || number > 100) {
continue; // Skip the remaining part of this loop.
}
sum += number;
}
return 0;
}
Introduction to for Loop#
// Introduction to for loop
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
// for loop
const uint32_t total_laps = 10; // Target laps
// uint32_t current_lap = 0; Initialize current lap
puts("The runner starts running");
for (uint32_t current_lap = 0; current_lap <= total_laps; current_lap++) {
printf("The runner completed lap %" PRIu32 " \n", current_lap);
}
return 0;
}
Sum of Squares of Integers#
// Sum of squares of integers
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
/*
To calculate the sum of squares of all integers from 1 to n (n entered by the player)
*/
uint32_t number;
uint32_t sum_of_squares = 0;
puts("Enter an integer n");
scanf_s("%u", &number);
for (uint32_t index = 1; index <= number; index++) {
sum_of_squares += index * index;
}
printf("%" PRIu32 "\n", sum_of_squares);
return 0;
}
Countdown from Five#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void) {
/*
Star Avenue
Countdown from five
*/
uint32_t start_number;
puts("Please enter a positive integer");
scanf_s("%" SCNu32, &start_number);
puts("The countdown begins");
for (uint32_t index_number = start_number; index_number > 0; index_number--) {
printf("%" SCNu32 "\n", index_number);
}
puts("The countdown stops");
return 0;
}
// Extended sleep
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <windows.h>
int main(void)
{
/*
Star Avenue
Countdown from five
*/
uint32_t start_number;
puts("Please enter a positive integer");
scanf_s("%" SCNu32, &start_number);
puts("The countdown begins");
for (uint32_t index_number = start_number; index_number > 0; index_number--) {
printf("%" SCNu32 "\n", index_number);
Sleep(1000);
}
puts("The countdown stops");
return 0;
}
Factorial#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <windows.h>
// Factorial
int main(void) {
uint32_t number;
uint32_t factorial = 1;
puts("Please enter a positive integer:");
scanf_s("%" SCNu32, &number);
for (uint32_t index = 1; index <= number; index++) {
factorial *= index;
}
printf("%" PRIu32 "! = %" PRIu32 "\n", number, factorial);
return 0;
}
Square Root#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
int main(void) {
double number = 4.00;
printf("%lf\n", sqrt(121));
return 0;
}
Prime Numbers#
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
int main(void) {
uint32_t num;
bool is_prime = true;
puts("Please enter a positive integer, except for 1, we will check if it is prime:");
scanf_s("%" SCNu32, &num);
if (num <= 1) {
is_prime = false;
}
else {
// for, check factors other than 1 and itself
for (uint32_t i = 2; i * i <= num; i++) {
if (num % i == 0) {
is_prime = false;
break;
}
}
}
return 0;
}
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <math.h>
#include <stdbool.h>
int main(void) {
uint32_t size;
puts("Please enter the size of the pattern: ");
scanf_s("%" SCNu32, &size);
// Characteristics: length and width are the same
puts("Printing square pattern");
for (uint32_t i = 0; i < size; i++) {
for (uint32_t j = 0; j < size; j++) {
printf("* ");
}
printf("\n");
}
return 0;
}
This article is synchronized and updated to xLog by Mix Space. The original link is https://hansblog.top/posts/study-book/c-study