Introduction to Linux C language architecture
You have learned the basic syntactic features of THE C language. This section takes you further.
- Preprocessor. Compilation instructions: preprocessing, macro definition,
- Create your own data types: structure, union, dynamic data structure
- C expression tool logic operators:
& | ^ ~ < < > >
- A recursive call to a function
What is preprocessing
vim helloworld.c
Copy the code
The helloworld. C:
#include <stdio.h>
int main(a)
{
printf("hello,world! \n");
return 0;
}
Copy the code
Purpose of compilation:
From c source files to executable files
gcc helloworld.c -o helloworld.out
./helloworld.out
Copy the code
Four steps to compile:
.c files ->.i files ->.s files ->.o files -> Executable files
-
- pretreatment
-
- compile
-
- assembly
-
- link
Mnemonic method: ISO three-step strategy.
Let’s look at the things to do in preprocessing:
gcc -o helloworld.i helloworld.c -E
Copy the code
-e indicates that only GCC performs preprocessing.
// Check the helloWorld.i file
cat helloworld.i
Copy the code
Vim jumps to the bottom of the document and commands: :$
You can see at the bottom of the code is our main function
Compare the difference between dot I files and dot c files
First of all: they are all syntaxes of C. And then the c file main function is #include
And this line of code in the dot I file is missing, it’s going to be this thing up here.
So the first thing preprocessing does is expand the header file
Expand stdio.h in #include
and write the uncommented content directly to the. I file.
In the preprocessing step, in addition to expanding the header file, a macro replacement is also performed.
What is the macro
C language constants are divided into direct constants and symbolic constants:
#defineIdentifier constant value (note: no semicolons)
Copy the code
Hellomacro.c source code:
#include <stdio.h>
#define R 10
int main(a)
{
int a =R;
printf("a=%d\n");
printf("hello,world! \n");
return 0;
}
Copy the code
gcc -o helloMacro.i helloMacro.c -E
Copy the code
Preprocessed code
# 4 "helloworld.c"
int main(a)
{
int a =10;
printf("a=%d\n");
printf("hello,world! \n");
return 0;
}
Copy the code
You can see that 10 is directly treated as a string to replace the original macro definition R.
The essence of macros is pure string substitution (macro substitution) that occurs in the preprocessing phase. In the preprocessing phase, macros do not consider syntax;
Example code 2: Vim Hellomacro2.c
#include <stdio.h>
#define R 10
#define M int main(
M){
printf("hello,world! \n");
return 0;
}
Copy the code
gcc helloMacro2.c -o helloMacro2.out
./helloMacro2.out
Copy the code
Preprocessing is fine and can compile and execute successfully. Macros do not consider the syntax of THE C language. It’s very simple, string substitution.
- Macros are used to define the size of a large number of repeatedly used constants and arrays of buffers for easy modification.
To define an array, we write:
int a[10];
int b[10];
Copy the code
Define two arrays of the same size. Here we can change to the following code.
#define R 10
int a[R];
int b[R];
Copy the code
You can modify two copies at a time.
Macros can also pass arguments and do things that functions can do
The macro function
Vim Hellomacrofunction.c source code:
#include <stdio.h>
#define R 10
#define M int main(
#define N(n) n*10
M){
int a = R;
int b = N(a);
printf("b = %d\n",b);
printf("a =%d\n",a);
printf("hello,world! \n");
return 0;
}
Copy the code
gcc helloMacroFunction.c -o helloMacroFunction.out
./helloMacroFunction.out
Copy the code
Here’s how to do it: first replace the argument a with the macro above, which becomes N(a) a*10, and then replace the following N(a) with a*10.
int b = N(a); Int b =a*10;Copy the code
gcc -o helloMacroFunction.i helloMacroFunction.c -E
Copy the code
After pretreatment:
# 8 "hello.c"
int main(a){
int a = 10;
int b =a*10;
printf("b = %d\n",b);
printf("a =%d\n",a);
printf("hello,world! \n");
return 0;
}
Copy the code
Macro implementation aside, let’s write a normal summation function.
vim helloAdd.c
Copy the code
#include <stdio.h>
#define R 20
#define M int main(
#define N(n) n*10
int add(int a,int b){
return a+b;
}
M){
int a = R;
printf("a =%d\n",a);
printf("hello,world! \n");
int b =N(a);
printf("b = %d\n",b);
int c =add(a,b);
printf("c =%d\n",c);
return 0;
}
Copy the code
gcc helloAdd.c -o helloAdd.out
./helloAdd.out
Copy the code
Use macro functions for summation.
vim helloAddMacro.c
Copy the code
#include <stdio.h>
#define R 20
#define M int main(
#define N(n) n*10
#define ADD(a,b) a+b
int add(int a,int b){
return a+b;
}
M){
int a = R;
printf("a =%d\n",a);
printf("hello,world! \n");
int b =N(a);
printf("b = %d\n",b);
int c =add(a,b);
printf("c =%d\n",c);
int d =ADD(a,b);
printf("d =%d\n",d);
return 0;
}
Copy the code
gcc helloAddMacro.c -o helloAddMacro.out
./helloAddMacro.out
Copy the code
You can see that using macro functions gives the same summation effect as using normal functions. The result is consistent with a simple string substitution.
ADD(a,b) is replaced by a plus b so it becomes int d is equal to a plus b;
gcc -o helloAddMacro.i helloAddMacro.c -E
vim helloAddMacro.i
Copy the code
Version 3, priority issues in macro definitions.
#include <stdio.h>
#define R 20
#define M int main(
#define N(n) n*10
#define ADD(a,b) a+b
int add(int a,int b){
return a+b;
}
M){
int a = R;
printf("a =%d\n",a);
printf("hello,world! \n");
int b =N(a);
printf("b = %d\n",b);
int c =add(a,b);
printf("c =%d\n",c);
int d =ADD(a,b);
printf("d =%d\n",d);
int e =ADD(a,b) * ADD(a,b);
printf("e =%d\n",e);
return 0;
}
Copy the code
A +b*a+b multiply ab, a=20, b=200, ab=4000, then add a, b: to get the result (4220)
gcc helloAddMacroPrecedence.c -o helloAddMacroPrecedence.out
./helloAddMacroPrecedence.out
Copy the code
The operation is going to run when we compile it and execute it. No operations are performed in the preprocessing phase.
- Macros are defined because they are essentially string replacements
When the actual operation is done, it is done according to the precedence of the operation symbol
Solution:
#define ADD(a,b) (a+b)
Copy the code
gcc helloAddMacroPrecedence.c -o helloAddMacroPrecedence2.out
./helloAddMacroPrecedence2.out
Copy the code
I’ll put a parenthesis around it to make sure it’s higher priority.
What are the advantages of macro and normal functions?
Normal add functions need to return value types, and the parameters that need to be passed in have type requirements.
Change the incoming a and B types, such as two floating point numbers, and the program will automatically convert.
Macro functions, however, have no such requirement that they ignore the type of the input value, unlike ordinary function definitions.
int c =add(10.5.20.4);
printf("c =%d\n",c);
float d =ADD(10.5.20.4);
printf("d =%f\n",d);
Copy the code
gcc helloAddMacroPrecedenceCompare.c -o helloAddMacroPrecedenceCompare.out
./helloAddMacroPrecedenceCompare.out
Copy the code
Common functions such as int add(int a,int b) declare the type of the value at the beginning and also set the return value, so the process of defining and calling is relatively complicated. Macros are preferred in cases where they can be implemented.
Macros don’t care about data types, don’t care about C syntax. It’s just a simple string manipulation.
The pre-processing phase, in addition to macros, also provides a feature called Mtianyan: conditional compilation.
Different parts of the program can be compiled under different conditions, resulting in different object code files. It is very useful for program transplantation and debugging.
A similar function to macros is typedef
Typedefs for Linux C preprocessing
Strictly speaking, typedef has little to do with preprocessing, but it’s there because it can be confusing.
typedef
Alias is used to alias a variable type.
Shorthand: alias; But it’s C syntax, it ends with a semicolon, unlike #define, it doesn’t need a semicolon.
typedef int tni;
Copy the code
Int (tni); int (tni);
tni a;
Copy the code
We defined Pointers earlier:
int *p;
// If we add a typedef
typedef int *p;
Copy the code
typedef int *p; P is the alias p for the data type int *
pq=null
// According to the typedef above, it is equivalent to
int* q=null
Copy the code
Replace int with tni:
typedef int tni;
M){
tni a = R;
Copy the code
gcc helloAddMacroPrecedenceCompareTypedef.c -o helloAddMacroPrecedenceCompareTypedef.out
./helloAddMacroPrecedenceCompareTypedef.out
Copy the code
You can see that the program runs successfully. Note that real names can coexist with aliases.
We have already tested that macros are replaced in the preprocessing phase, while tnI is not replaced in the.i file.
gcc -o helloAddMacroPrecedenceCompareTypedef.i helloAddMacroPrecedenceCompareTypedef.c -E
vim helloAddMacroPrecedenceCompareTypedef.i
Copy the code
When we use typedefs, we usually alias our own custom data types.
For example, size_t is the alias of a type that the system defines for us.
typedef unsigned long size_t;
// Define a structure
struct stu{
};
// When using a structure
struct stu xxx;
// Alias the structure
typedef struct stu{
} stu_t;
// Use the structure after the alias
stu_t xxx;
Copy the code
Difference 2: macro definition declaration at the beginning, global any scope can be used directly.
Note that a typedef, if written inside a method body, can only be used within curly braces of that scope.
Next class: Structures
Structure declaration and definition
In previous studies, the variable types used were mostly simple variable types.
Such as:
int a = 0;
float b =0.0;
Copy the code
There is no connection between these variables, however many times the data we store is too complex to be described by simple types.
For example, we need to store information about a weapon:
It will contain a lot of information about the weapon. All of the previous data types were insufficient to describe multiple information about a weapon.
Arrays don’t work either, because arrays can only hold the same type of data, with different weapon names and price types.
Structs are collections of variables of different types & arrays are collections of variables of the same type
vim Cstructweapon.c
Copy the code
#include <stdio.h>
struct weapon{
// Weapon name
char name[20];
/ / damage
int atk;
/ / price
int price;
};
int main(a)
{
int a =0;
float b =0.0;
struct weapon weapon_1;
return 0;
}
Copy the code
Struct struct type name {}, just creates a struct type.
But we can use this struct Weapon type to declare variables
struct weapon weapon_1;
Copy the code
This definition method is a form of separation of declaration and definition.
The second is defined directly at declaration time.
struct weapon{
// Weapon name
char name[20];
/ / damage
int atk;
/ / price
int price;
}weapon_1;
Copy the code
It defines a global variable, named weapon_1, of type struct weapon. Simple programs can be used
The third method:
struct{
// Weapon name
char name[20];
/ / damage
int atk;
/ / price
int price;
}weapon_1;
Copy the code
This method cannot use the structure type to define other variables. Anonymous structure type.
Next lesson: How to initialize and reference a structure
Struct initialization and reference.
Once we have defined the structure: do we need to know how to initialize, and how to access the structure members
When initializing a structure, can be equal to an initializer list. Constants are enclosed in curly braces and assigned to structure members in turn.
struct weapon weapon_1 = {"weapon_name".100.200};
// Access the member in the structure, using point
printf("%s\n,%d\n",weapon_1.name,++weapon_1.price);
Copy the code
#include <stdio.h>
struct weapon{
// Weapon name
char name[20];
/ / damage
int atk;
/ / price
int price;
};
int main(a)
{
int a =0;
float b =0.0;
struct weapon weapon_1 = {"weapon_name".100.200};
// Access the member in the structure, using point
printf("%s\n%d\n",weapon_1.name,++weapon_1.price);
}
Copy the code
gcc Cstructweapon.c -o Cstructweapon.out
./Cstructweapon.out
Copy the code
Structure member variables can operate just like normal variables. Name should be taken as a whole
a++;
++weapon_1.price // It can be seen that it operates like a normal variable
Copy the code
Struct array
If we need data for ten weapons, we need to use arrays, struct arrays.
.
The dot operator is a member operator,Has the highest level of operation of all operatorsThat can be used.
The operator accesses internal members of a structure.
Struct arrays are not very different from normal arrays.
int a[2]; // Contains two elements of type int
The struct array contains two struct type elements, each with three struct members
struct weapon weapon_2[2]. // Each element is a structure type of data
Copy the code
Vim cstructweaponarray. c
struct weapon weapon_2[2] = {{"weapon_name1".50.100}, {"weapon_name2".99.200}};
printf("%s\n%d\n",weapon_2[0].name,weapon_2[1].atk);
Copy the code
gcc CstructweaponArray.c -o CstructweaponArray.out
./CstructweaponArray.out
Copy the code
Two struct arrays can be initialized:
/ / intuition
struct weapon weapon_2[2] = {{"weapon_name1".50.100}, {"weapon_name2".99.200}};
/ / the general formula
struct weapon weapon_2[2] = {"weapon_name1".50.100."weapon_name2".99.200};
Copy the code
Both methods can be successfully initialized.
Structure pointer
Learn: what is a pointer to a struct variable and how to use it.
struct weapon *w ; // Define a pointer to the W structure for weapon
w=&weapon_1; // points to memory address for weapon_1
(*w).name // The left and right parentheses cannot be omitted because. Operators take precedence over stars
w->name // Arrows are called pointing operators
weapon_1.name // All three access types have the same effect
Copy the code
vim CstructweaponArrayPointer.c
Copy the code
struct weapon * w;
w = &weapon_1;
printf("---------------------------------\n");
printf("name=%s\nname=%s\nname=%s\n",(*w).name,w->name,weapon_1.name);
Copy the code
gcc CstructweaponArrayPointer.c -o CstructweaponArrayPointer.out
./CstructweaponArrayPointer.out
Copy the code
Structure array pointer
cp CstructweaponArrayPointer.c CstructweaponArrayPointer2.c
vim CstructweaponArrayPointer2.c
Copy the code
Pointer to a structure array without taking the address character&
The name of the array represents the memory header of the array, the length in parentheses represents the number of cells in the array, multiplied by the number of cells if the data type is int (4 bytes on 32-bit systems), or by the length of the structure if the data type is structure.
P++, instead of moving the memory location right by one byte, moves the weapon memory length right by one unit length. So it’s not hard to see why we moved right to the start of the second struct instance.
struct weapon weapon_2[2] = {{"weapon_name1".50.100}, {"weapon_name2".99.200}};
struct weapon *p;
p = weapon_2; // The array name corresponds to the starting address of the first element of the array
P ->name equals weapon_2[0]. Name
printf("%s\n",p->name);
printf("---------------------------------\n");
p++; // 等价于 weapon_2+1 让其指向weapon_2[1]
printf("%s\n",p->name);
Copy the code
gcc CstructweaponArrayPointer2.c -o CstructweaponArrayPointer2.out
./CstructweaponArrayPointer2.out
Copy the code
Linux C utility
Common types are also known as unions, and can be created using the keyword union
Simple enough to understand, let several different types of variables share the same memory address
vim union.c
Copy the code
#include <stdio.h>
union data{
int a;
char b;
int c;
}; // indicates that a, B, and C are in the same memory space.
int main(a)
{
union data data_1;
data_1.b ='C';
data_1.a =10; // The assigned member is the member that matters
return 0;
}
Copy the code
The advantage is to save some overhead. Disadvantages: Only one member can be stored at a time.
Let’s say I assign to b, I assign to A. The last assignment takes effect, because all members share a block of memory. (The last one overwrites the previous one)
The length of the common body type is the length of the longest member of all members. For example, in the above example int has four bytes and char has only one byte. Int is the longest, so the common type takes up four bytes.
union data data_1 = {10};
Copy the code
So when you assign to a common entity or a union, you can only assign to one variable.
For structures, this is different
struct data{
int a;
char b;
int c;
};
Copy the code
It is tempting to think that the size of the struct is the sum of its member variables. 4 + 1 + 4 = 9
The memory footprint of a structure, involving byte alignment, is a way for computers to trade space for time.
Size = the offset of the last member + size of the last member +(optional) number of padding bytes at the end.Copy the code
The offset is the distance between the actual address of a member and the first address of the structure. (Offset, the distance between the actual address and the first address of the structure)
- For member A, the address of a is the first address of the structure, offset 0;
- The offset of B is 4, which itself is 1;
- Structure byte alignment rule: Each structure must have an offset relative to the head address that is an integer multiple of the memory size occupied by the current member, otherwise padding bytes are added.
- The offset of c is 5, and c itself is 4. Since 5 is not a multiple of 4, byte alignment is completed to 8. And then plus 4 of C itself. So the whole thing takes 12.
- Then determine whether the size 12 is an integer multiple of the widest primitive type member in the structure.
The widest one in this example is int, 4 is divisible by 12. If not, the number of bytes is also padded after c.
A union pool is a collection of different data types, and the number of bytes is determined by the type that occupies the largest amount of memory in the pool.
For example, there are char(1 byte) and int(4 bytes), which take up four bytes. In addition, the address of all data in the community is the same, which determines that when it has multiple data types, it can store only one of these data types. The last assignment takes effect.
A struct structure is also a collection of different data types, and the number of bytes is determined by the length of all the data types in it.
For example, char(1 byte) and int(4 bytes), structs take up 8 bytes of space, not 5 bytes, because byte alignment is involved, and byte alignment is to facilitate the computer to read data quickly.
vim struct_size.c
Copy the code
#include <stdio.h>
struct data{
int a;
char b;
int c;
};
int main(a){
// union data data_1;
//data_1.b ='C';
// data_1.a =10;
printf("%lu\n".sizeof(struct data));
return 0;
}
Copy the code
gcc struct_size.c -o struct_size.out
Copy the code
A is the first member with an offset of 0. B has an offset of 4, b itself has size 1, divisible without the complement, c has an offset of 5, itself has size 4, byte alignment is 8, plus itself has size 4, 12.
Then determine whether 12 is an integer multiple of the widest primitive type member in the structure.
%p
Represents output of this pointer%d
Indicates that the following output type is a signed decimal integer,%u
Represents an unsigned decimal integer type,%lu
Output long unsigned integer (long unsigned)
vim union_address.c
Copy the code
#include <stdio.h>
union data{
int a;
char b;
int c;
}; // indicates that a, B, and C are in the same memory space.
int main(a)
{
union data data_1;
data_1.b ='C';
data_1.a =10;
printf("%p\n%p\n%p\n",&data_1.a,&data_1.b,&data_1.c);
return 0;
}
Copy the code
gcc union_address.c -o union_address.out
./union_address.out
Copy the code
All member variables in the Commons have the same address.
Linux C Dynamic Data Structures – static linked lists
Static data structures:
- Such as: integer, floating point, array.
- Features: The system allocates a fixed amount of storage space
After the program runs, the location and capacity of the space do not change.
For example, when using an array, the length of the array must be defined in advance. But a lot of times we don’t know.
You need a data structure that can store allocations dynamically. Variable-size data structures.
List:
- We have a header pointer variable head, which holds the address, and the address points to the first element. The linked list cannot be accessed without a header pointer
- Each element in a linked list is a node.
- Each node contains two parts, including the data required by the user and the address of the next node. The addresses of each element are not necessarily contiguous (unlike arrays).
End of the linked list when pointer is null. To access an element in a list, you must use the address of the next element provided by the previous element.
To access element B, you need to find the address of element B stored in element A.
Static linked lists:
(All nodes are defined in the program, not created temporarily)
[Consists of three nodes for weapon information, so use structure type as node element]
vim struct_weapon_link.c
Copy the code
#include <stdio.h>
struct weapon{
int price;
int atk; // The data part of the node
struct weapon * next; // Address of the next node
};
int main(a)
{
struct weapon a.b.c, *head; // Define a header pointer in addition to the three weapons
a.price =100;
a.atk = 100;
b.price =200;
b.atk =200;
c.price =300;
c.atk =300;
// join to a linked list
head = &a;
a.next =&b;
b.next =&c;
c.next = NULL;
struct weapon *p;
p =head;
while(p! =NULL) {printf("%d,%d\n",p->atk,p->price); p=p->next; }}Copy the code
gcc struct_weapon_link.c -o struct_weapon_link.out
./struct_weapon_link.out
Copy the code
Static lists, all the nodes are defined in the program, not created temporarily.
A linked list has a header pointer and a tail pointer, each pointing to the address of the next item on the list.
The pointer structure consists of two parts, one is the information field (data field), the other is the pointer field.
Linux C Dynamic Data Structures – dynamic linked lists
Linked lists: You can use malloc to dynamically allocate the required memory, and you need to use free to manually free the memory requested in the heap.
In the process of the execution of the program from scratch to establish a linked list, that is to say, the need to open up a new node, input the data of the new node, and then establish a connected relationship.
Build a one-way dynamic linked list of weapon information:
vim dynamic_linked_list.c
Copy the code
#include <stdio.h>
#include <malloc.h>
struct weapon{
int price;
int atk;
struct weapon * next;
};
Struct weapon * // We need a function to create a list. The return value is the head pointer of the list. The return type is struct weapon *
struct weapon * create(a){
struct weapon *head;
struct weapon *p1, *p2; // All three Pointers are used to point to struct Weapon type data,p1 and p2 to the newly created node and the previous node respectively.
int n=0;// A temporary variable to record the number of nodes in the current list
// malloc a function that allocates memory blocks. Sizeof determines the length of data types
// (int)malloc(16) Allocates 16 int blocks
p1=p2=(struct weapon*)malloc(sizeof(struct weapon));
// Input data from the keyboard to the first node.
scanf("%d,%d",&p1->price,&p1->atk);
head = NULL;// The list does not exist at first
while(p1->price! =0) {// Stop input when price is 0
n++;
if(n==1) head=p1;
else p2->next=p1;
p2=p1; // Keep the current address of p1, keep the last node.
// we need to create a new dynamic storage area, load this address to p1
p1=(struct weapon*)malloc(sizeof(struct weapon));
scanf("%d,%d",&p1->price,&p1->atk);// Enter data after opening
}
p2->next=NULL;
return (head);
}//p1,p2 one points to the newly created node in the list, one points to the next node
int main(a)
{
struct weapon *p;
p=create(); // p becomes the head pointer to the list
printf("%d,%d\n",p->price,p->atk);// Prints information about the first node
return 0;
}
Copy the code
gcc dynamic_linked_list.c -o dynamic_linked_list.out
./dynamic_linked_list.out
Copy the code
while(p! =NULL) {printf("%d,%d\n",p->atk,p->price);
p=p->next;
}
Copy the code
C language bit operators: bitwise and, bitwise or, bitwise xor, left and right shift
Bitwise and bitwise for LInux C bit operations
- Bit: indicates the binary digit 0 false 1 true
- The old microprocessor speed: faster than addition and subtraction, much faster than multiplication and division in today’s framework: usually as fast as addition and subtraction, much faster than multiplication and division
Six bitwise operators
- & click with
- | or by location
- ^ Bitwise xOR
- ~ is reversed by bit
- < < shift to the left
-
Moves to the right
vim and_operation.c
Copy the code
- Bitwise operation is the logical and operation of the two data bits according to the corresponding binary number.
- The two numbers involved in the operation must be integers or characters.
- And the numbers involved must appear in the form of a complement.
#include <stdio.h>
int main(a)
{
/ / & | ^ ~ < < > >
int a = 4; //0100 int occupies 4 bytes, 32 bits complement.
int b = 7; / / 0111
int c = a&b; //0100 logic and operations
printf("%d\n",c);
return 0;
}
Copy the code
gcc and_operation.c -o and_operation.out
./and_operation.out
Copy the code
Application of bitwise and
- Quick zeroing (for a 1 bit in one number, the corresponding bit in the other number is 0);
Any number and 0 do the bitwise sum and the result must be 0
- Reserve the specified position (corresponding position 1 for another number); Take the lower eight bits of A and set all the lower eight bits of B to one.
- [a&1 // get 1 to be odd and 0 to be even]
vim odd_even.c
Copy the code
#include <stdio.h>
int main(a)
{
/ / & | ^ ~ < < > >
int a =4;/ / 0100
int b =7;/ / 0111
int c =a&1;/ / 0100
int d =b&1;/ / 0100
printf("%d\n",c);
printf("%d\n",d);
return 0;
}
Copy the code
The output of 0, 1; An output of 0 is even, and an output of 1 is odd.
Because everything before the last digit must be a multiple of 2. The last digit is 1, which means it’s odd. The last digit is 0, which means it’s even.
gcc odd_even.c -o odd_even.out
./odd_even.out
Copy the code
To operate by bit or.
Perform a logical or (if there is a 1, the result is 1) operation on the two data bits involved in the operation
vim bit_or.c
Copy the code
#include <stdio.h>
int main(a)
{
/ / & | ^ ~ < < > >
int a =9; / / 1001
int b =5; / / 0101
int c =a|b;/ / 1101
printf("%d\n",c);
return 0;
}
Copy the code
gcc bit_or.c -o bit_or.out
./bit_or.out
Copy the code
The output is 1101 (13)
The function of bitwise or:
Sets the specified bit of data, with 255(0xFF) doing bitwise or
Let’s say we want 9 to be 8 bits lower than 9 to be 1.
a = a | 0xFF; Sets position 8 after the specified binary number of data a to 1
vim bit_or_8.c
Copy the code
#include <stdio.h>
int main(a)
{
/ / & | ^ ~ < < > >
int a =9; / / 1001
int b =5; / / 0101
int c =a|b;/ / 1101
printf("%d\n",c);
int a =a|0xff;
printf("%d\n",a);
return 0;
}
Copy the code
gcc bit_or_8.c -o bit_or_8.out
./bit_or_8.out
Copy the code
The bitwise exclusive or^
The logical XOR operation is performed bit by bit on the two data participating in the operation according to the corresponding binary number. The result is true only if the corresponding bit results are mutually exclusive.
vim bitwise_xor.c
Copy the code
#include <stdio.h>
int main(a)
{
/ / & | ^ ~ < < > >
int a =9; / / 1001
int b =5; / / 0101
int c =a^b;/ / 1100
printf("%d\n",c);
return 0;
}
Copy the code
gcc bitwise_xor.c -o bitwise_xor.out
./bitwise_xor.out
Copy the code
- Location inversion makes all the zeros in A become 1s and 1s become zeros.
a^0xff;
Copy the code
- Numerical exchange
a= a^b;
b= b^a;
a= a^b;
Copy the code
vim bitwise_xor_change.c
Copy the code
#include <stdio.h>
int main(a)
{
/ / & | ^ ~ < < > >
int a =9; / / 1001
int b =5; / / 0101
a= a^b;
b= b^a;
a= a^b;
printf("%d\n%d\n",a,b);
return 0;
}
Copy the code
gcc bitwise_xor_change.c -o bitwise_xor_change.out
./bitwise_xor_change.out
Copy the code
We successfully switched a and B
The operation principle involved can be specified as follows :(the following = means equal rather than assignment)
P0. x^x=0
P1. a^0=a
P2. c=a^x --> a=c^x (a=a^x^x=a^0=a)
Copy the code
Exchange explanation:
a^=b; A is assigned a^b, b^=a; B is assigned the original value of A, and A remains the same as in the previous step: A ^=b; At this point, A is assigned the original value of B, and B remains the same as the result of the previous step
According to the not
The only unary operator, right associativity. I’m going to replace 0 with 1 and 1 with 0
~ 1000 = 0111Copy the code
Left shift: high speed multiplied by 2; Right shift: High speed divided by 2
Left shift: The corresponding binary value of the data is moved several bits to the left.
The high will be discarded and the low will be zeroed out; That’s the same thing as multiplying by 2 to the n
Note that an int is a signed type and will overflow if it is moved to the left
Right shift: Moves the binary value of data to the right by several bits
Mtianyan: low value discarding, unsigned number filling zero, signed number filling 0 or 1(according to the sign bit judgment, that is, positive number filling 0, negative number filling 1); That’s the same thing as dividing by 2 to the n.
vim left_right_shift.c
Copy the code
#include <stdio.h>
int main(a)
{
/ / & | ^ ~ < < > >
int a =3; / / 0000 0011
a = a<<4; / / 0011 0000 32 + 16 = 48
printf("a=%d\n",a);
int i =1; / / 0000 0001
i = i<<33; // I <<1
printf("i=%d\n",i);
// When the number of shift bits exceeds the maximum number of bits of the numeric type, the compiler modulates the number of shift bits with the remainder.
int b = 4;
b = b>>1;
printf("b=%d\n",b);
return 0;
}
Copy the code
gcc left_right_shift.c -o left_right_shift.out
./left_right_shift.out
Copy the code
As you can see, warning is displayed
Recursive calls to recursive functions
A recursive call is a process in which the called function calls itself during a function call.
Recursive calls sometimes sacrifice efficiency
vim recursiv_call.c
Copy the code
#include <stdio.h>
void func(a){
printf("1\n");
func();
}
int main(a)
{
func();
return 0;
}
Copy the code
gcc recursiv_call.c -o recursiv_call.out
./recursiv_call.out
Copy the code
Outputs 1 endlessly, then bursts a segment error, and the core has been dumped
So our recursion is conditional. Choose recursion when appropriate, because recursion sometimes sacrifices efficiency.
Use recursion to find a factorial
vim recursiv_call_factorial.c
Copy the code
#include <stdio.h>
int func(int n)
{
int r =0;
if(n<0) {printf("error");
}else if(n==0 || n==1) {return 1;
}else{
r =n *func(n- 1);
returnr; }}int main(a)
{
int n =0;
printf("please input the num:");
scanf("%d",&n);
// use func to find the factorial
int result = func(n);
printf("the result is %d\n",r);
return 0;
}
Copy the code
gcc recursiv_call_factorial.c -o recursiv_call_factorial.out
./recursiv_call_factorial.out
Copy the code
Notice the recursive integer overflow here
Recursion principle of recursive function
Recursion is a programming technique in which the body of a function calls itself during a call
The process of a function call:
Function A calls function B, A is called from main, and B is called from A. B has two arguments,A and B are called parameters.
When the function is not called, the parameter is not allocated memory. When B is called from A, A is called the calling function.
When a function is called in a calling function, the arguments in the function are called arguments.
The first thing a function call needs to do is:
Allocate a temporary memory unit for the parameters of the called function (B) before passing in the values of the two corresponding arguments.
You also need to pass the return address of the calling function (A). (Commonly known as protection site)
The reason for saving the return address of A is that the called function (B) needs to continue executing the code after the calling function (A).
A return statement is used to bring the evaluated value of the function back to the calling function.
The return address, parameter, and return value at the end of the function call passed when protecting the scene. This data is stored on the stack.
Recursion is a call to itself, but it’s also a function call, essentially a function call.
The factorial function, since recursion also conforms to the principle of function calling, so all called functions create a copy, serving their respective callers, independent of other functions. The recursive function creates as many copies of itself as it is called.
This copy is as if it were a new function. The system uses stacks to manage its memory. It’s a separate entity, and it’s totally understandable that this is a new FUNC. It is allocated a separate memory unit.
Recursion: large – > simplify – > small until the problem is solvable. A recursive function must have both a recursive condition and a recursive expression, otherwise it will enter an infinite loop. Recursion (for) : the solution of a small problem is gradually substituted into a large problem and solved.
conclusion
-
Compile preprocessing: expand header file, macro replacement
-
Custom data types: structure, union, linked list implementation, static and dynamic linked list. The structure involves byte alignment, all members of the community share a common address, and the structure body has the calculation method of occupancy.
-
Logical operator bit operations
- Recursive calls, recursive ideas and recursion