当前位置:网站首页>Notes on in-depth analysis of C language 1
Notes on in-depth analysis of C language 1
2022-07-25 09:20:00 【halazi100】
The secret of keywords
data type
What is data type ?
A data type can be understood as an alias for a fixed memory size ;
A data type is a model for creating variables ( Flower shaped , circular , Star and so on );
char1byteshort2byteint4byte
Memory space
+----------+
| char c |
+----------+
| short s |
| |
+----------+
| |
| int i |
| |
| |
+----------+
The nature of variables
Variables are also aliases ; Such as int num Tell the compiler to apply int Size of memory and named num;
A variable is an alias for an actual contiguous storage space ;
The program uses variables to apply for and name storage space , The storage space can be used through the name of the variable ;
The memory space corresponding to each variable has a number, that is, address ;
Pointer is also a variable , It stores the address of a common variable ;
The relationship between types and variables
That is, the relationship between architectural drawings and actual buildings
#include <stdio.h>
int main() {
char c = 0;
short s = 0;
int i = 0;
printf("%ld, %ld\n", sizeof(char), sizeof(c)); //1,1
// The size of the variable is the same as the size of the type that defines it
// Deeply understand the relationship between data types and variables
printf("%ld, %ld\n", sizeof(short), sizeof(s)); //2,2
printf("%ld, %ld\n", sizeof(int), sizeof(i)); //4,4
return 0;
}
Prove the essence of variables by printing statements
#include <stdio.h>
typedef int INT32;
typedef unsigned char BYTE;
typedef struct _demo {
short s; // 2 //a=2, @+0
INT32 i; // 4 //a=4, @+4
BYTE b1; // 1 //a=1, @+8
BYTE b2; // 1 //a=1, @+9
} DEMO;
typedef struct _test {
DEMO de; // 12
char c; // 1
} Test; //16
int main() {
INT32 i32;
BYTE byte;
DEMO d;
printf("%ld, %ld\n", sizeof(INT32), sizeof(i32)); // 4, 4
printf("%ld, %ld\n", sizeof(BYTE), sizeof(byte)); // 1, 1
printf("%ld, %ld\n", sizeof(DEMO), sizeof(d)); // 12, 12
printf("%ld\n", sizeof(Test)); // 16
return 0;
}
Variable properties
C Variables in a language can have their own properties ,
Attribute keywords can be added when defining variables , Attribute keywords indicate the unique meaning of variables ;
Variable attribute keyword
autoyes C Default properties of local variables in language ;
- The function is to assign a local variable to the stack ;
- The compiler defaults that all local variables are auto, Indicates that variables are allocated on the stack ;
staticKeyword indicates the static attribute of a variable ;
staticKeywords also have the meaning of scope qualifiers ,staticModified local variables are stored in the program static area ;staticAnother meaning of is the file scope identifier ;staticThe modified global variable scope is only in the declared file , Other files are not accessible ;staticThe scope of the modified function is only in the declared file , Other files cannot be called ;
staticModifying a global variable is to limit the scope , Decorated local variables are stored in static areas ;staticThe meaning of is not to put variables in static areas , It is limited to the current file ;
registerKeyword indicates that a variable is stored in a register ;
registerJust request register variables , But not necessarily successful ;registerThe variable must be CPU The value that the register can accept ;- Can't use
&obtainregisterThe address of the variable , Because variables are not in memory ; registerCan't modify global variables ;- Getting a variable from a register is faster ,
registerOften used to decorate loop variables ;
Summary
autoVariables are stored in the program stack , The default attribute ;staticLocal variables are stored in the static area of the program ,static Global variables and function qualification action file ;registerVariable requests are stored in CPU In the register , Not necessarily successful ;
///test2.c//
static int test2_g = 1;
/*
* static int test2_g = 1;
* Variable test2_g Will not be referenced in other files
* static The meaning of is not to put variables in static areas , It is limited to the current file
*/
int test2_func(){
return test2_g;
}
///test.c///
#include <stdio.h>
int g = 0;
int m = 0;
//auto int g = 0; // Global variables are stored in the global area , Cannot be allocated to a stack area that can be changed at any time ;
// register int m = 0; // Global variables exist during program operation ;
// If global variables are allowed to be stored in registers , So many register variables have been occupied ,CPU The register of will not work properly ;
//extern int test2_g; /* static2.c */
//test2_g Variable in static2.c Declared as static, Therefore, you cannot quote
extern int test2_func(); /* static2.c */
void f1() {
int i = 0; // Local variables are released at the end of the function call
i++;
printf("%d\t", i);
}
void f2() {
static int i = 0;
// static Decorated variables will only be initialized once ;
// static When modifying a local variable , Store local variables in static storage instead of stack , It will not be released because the function runs out ;
// static The scope of the modified local variable does not change , The life cycle is extended ;
i++;
printf("%d\t", i);
}
int main() {
auto int i = 0; /* Clearly indicate i Variables are stored in the program stack */
register int j = 0; /* Requests are stored in CPU in , The compiler said I try my best */
static int k = 0; /* Tell the compiler not to use default attribute assignment k, It is stored in the static area */
for (i = 0; i < 5; i++) {
f1();
} // Print 5 individual 1
printf("\n");
for (i = 0; i < 5; i++) {
f2();
} // Print 12345
printf("\n");
// printf("test2_g = %d\n", test2_g);
printf("test2_g = %d\n", test2_func());
return 0;
}
/test.sh
#! /bin/bash
gcc -o test test.c test2.c
Branch statement
if Branch
ifStatement is used to select and execute statements according to conditions ;elseIt cannot exist independently and is always unpaired with its nearest if Match ;elseStatements can be followed by other statements if sentence ;
if (condition) {
//statement1;
} else {
//statement2;
}
if (cond1) {
//statement1;
} else if (cond2) {
//statement2;
} else {
//statement3
}
It is in the following form
if (cond1) {
//statement1;
} else {
if (cond2) {
//statement2;
} else {
//statement3
}
}
if In the sentence 0 Note on value comparison
boolType variables should appear directly in the condition , There is no need to compare true and false , Not 0 It's true ;
bool b = TRUE;
if (b) {
//statement1;
} else {
//statement2;
}
- Common variables and 0 When comparing values ,0 The value should preferably appear to the left of the comparison symbol ;
int i = 1;
if (0 == i) {
//statement1;
} else {
//statement2;
}
floatType variables cannot be used directly 0 Value comparison , Precision needs to be defined ;
#define EPSINON 0.00000001
float f = 0.0;
if ((-EPSINON <= f) && (f <= +EPSINON)) {
//statement1;
} else {
//statement2;
}
switch Branch
- switch Statement corresponds to the case of multiple scores for a single condition ;
- Every case Statement branches should have break, Otherwise, the branches will overlap ;
- default The statement must be added , To deal with special situations ;
switch ( expression ) {
case Constant :
Code block
case Constant :
Code block
default:
Code block
}
- switch The value in the statement can only be integer or character ;
case Statement sort order analysis
- Arrange the sentences in alphabetical or numerical order ;
- Normally, put it in front , Exceptions are left behind ;
- default Statements can only be used in real default situations ;
if and switch Comparative use examples
#include <stdio.h>
void f1(int i) {
if (i < 60) {
printf("Failed!\n");
} else if ((60 <= i) && (i <= 80)) {
printf("Good!\n");
} else {
printf("Perfect!\n");
}
}
void f2(char i) {
switch (i) {
case 'c':
printf("Compile\n");
break;
case 'd':
printf("Debug\n");
break;
case 'o':
printf("Object\n");
break;
case 'r':
printf("Run\n");
break;
default:
printf("Unknown\n");
break;
}
}
int main() {
f1(50);
f1(90);
f2('o');
f2('d');
f2('e');
return 0;
}
Summary
- if The statement is applicable to situations that need to be judged by slice ;
- switch The statement is applicable to the situation that each discrete value needs to be judged separately ;
- if Statements can be completely replaced functionally switch sentence , but switch Statements cannot replace if sentence ;
- switch The statement is more concise for the case of multi branch judgment ;
Loop statement
The basic working mode of circular statements :
By conditional expression ( The conditional expression follows if The principle of statement expression ) Determine whether to execute the loop body ;
do,while,for The difference between
- do Statements are executed first and then judged , The loop body will be executed at least once ;
- while The statement is judged before execution , The loop body may not be executed ;
- for Word order is judged before execution , comparison while More concise ;
Comparison of three kinds of circular statements :
// Accumulate natural numbers
#include <stdio.h>
int f1(int n){
int ret = 0;
int i = 0;
// Most intuitive
for(i = 1; i <= n; i++) {
ret += i;
}
return ret;
}
int f2(int n){
int ret = 0;
// The implementation method is more abstract
while((n > 0) && (ret += n--)); // without n>0 Your judgment will enter a dead cycle
// while(n && (ret += n--)); // Dead cycle
return ret;
}
int f3(int n){
int ret = 0;
if (n > 0) {
// without n>0 Your judgment will enter a dead cycle
do {
ret += n--;
} while(n);
// as long as n!=0 It's true ,while It's going to continue to cycle
}
// You can set the condition as while(n > 0); Avoid the dead cycle ;
// use while(n>0) do{} It can replace the above branches and loops
return ret;
}
int main(){
printf("%d\n", f1(10));
printf("%d\n", f2(10));
printf("%d\n", f3(10));
return 0;
}
break and continue The difference between
- break Indicates that the execution of the loop is terminated ( Jump out of block statement ( loop ,switch)), It can be used in loops and switch;
- continue Indicates the termination of this cycle , Enter the next cycle to execute , Only for loop body ;
switch Can you use continue keyword ?
continue Is loop dependent , Cannot be used for switch Branch ;
do…while(0) and break The magic effect of
#include <stdio.h>
#include <malloc.h>
int func(int n)
{
// 1. Unified resource allocation
int i = 0;
int ret = 0;
int *p = (int*)malloc(sizeof(int) * n);
// 2. Code execution
do {
if (NULL == p) break; // break Jump out of the statement block
if (n < 0) break; // If you will break Replace with return, The code will be cumbersome
for (i = 0; i < n; i++) {
p[i] = i;
printf("%d\n", p[i]);
}
ret = 1;
} while(0);
// The outer do...while The statement block must be executed once
// 3. Unified resource recycling
free(p);
return ret;
}
int main() {
if (0 != func(10)) {
printf("OK\n");
} else {
printf("ERROR\n");
}
return 0;
}
Abandoned goto
Master hidden rules : Ban goto, Program quality and goto Is inversely proportional to the number of occurrences ;
Generally, the entry function of the kernel module will be widely used goto sentence , Used to handle exceptions ;
- goto It often destroys the sequential execution of structured programs ;
- goto Statements are also called unconditional jump statements , The general format is
goto Statement tags ;
The statement label is a symbol written according to the identifier , Put in front of a sentence line , Add a colon after the label
:;
Statement labels are used to identify statements , And goto Statement with ;
C There is no limit in the language to the number of times labels are used in the program , However, each label shall not have the same name ;
goto Statement is to change the flow direction of the program , Go to execute the statement identified by the statement label ;
- goto Statements are usually used with branch statements , Can be used to achieve conditional transfer , Form a cycle , Jump out of circulation and other functions ;
However, it is not recommended to use goto sentence , So as not to cause program confusion , Make it difficult to understand and debug the program ;
Example
#include <stdio.h>
int main(){
int n = 0;
printf("input a string\n");
loop:
if (getchar() != '\n') {
n++;
goto loop;
}
printf("%d\n", n);
return 0;
}
For example, the input : hello world
Press enter to print : 11
goto Side effect analysis
goto It may cause skipping some statements that should have been executed , Rules that break the sequential execution of structured programming ;
// goto Side effect analysis
#include <stdio.h>
void func(int n) {
int* p = NULL;
if (n < 0) { // When n>=0 The program will perform well
goto STATUS; // Skip heap memory allocation , Crash the program
}
// Destroy the sequential execution of structured programs
p = malloc(sizeof(int) * n);
STATUS:
p[0] = n;
}
int main() {
f(1);
f(-1);
return 0;
}
When compiling, it is compiled , But the execution was goto skip , Cause the uncertainty of the result ;
#include <stdio.h>
int x = 5;
int main () {
printf("%p\n", &x);
goto a;
{
// When it comes to execution goto Will skip , But it will still be compiled normally
int x = 3; // Reapply a local variable with the same name x
printf("%p\n", &x);
pritnf("int x = 3\n");
a:
// there x All local variables
printf("x = %d\n", x);
printf("%p\n", &x);
}
// After the x Global variable
printf("x = %d\n", x);
printf("%p\n", &x);
return 0;
}
void keyword
void Modify function return values and parameters
- If the function does not return a value , Then it should be declared as void type ;
- If the function does not accept parameters , The parameter should be declared as void type ;
void The return value and parameters of the decorated function are only used to indicate none ;
non-existent void Variable ,
void v;Compiler error ;No, void Ruler of ;
- c Type names in languages are aliases for fixed size memory , But there is no definition void What is the alias of how much memory ;
- void Does not correspond to the size of any variable ;
void Pointers to types exist ;
void The meaning of the pointer
c The language stipulates that only pointers of the same type can assign values to each other ;
void*The pointer is used as an lvalue to receive any type of pointer ;void*When a pointer is assigned to another pointer as an R-value, it needs to be cast , Can be assigned to other types of pointers ;
malloc()returnvoid*type ;
int *pI = (int *)malloc(sizeof(int));
char *pC = (char *)malloc(sizeof(char));
void *p = NULL;
int *pni = NULL;
char *pnc = NULL;
p = pI; //ok
pni = p; //oops!
p = pC; //ok
pnc = p; //oops!
void* Use of the pointer , Realization my_memset() function ;
#include <stdio.h>
void* my_memset(void *p, char c, int size) {
void *ret = p;
char *dest = (char *)p;
int i = 0;
for (i = 0; i < size; i++) {
dest[i] = c;
}
return ret;
}
int main(){
int arr[5] = {1, 2, 3, 4, 5};
long num = 9999;
char str[10] = "hello";
int i = 0;
for (i = 0; i < 5; i++) {
printf("%d\t", arr[i]);
}
printf("%ld\t", num);
printf("%s", str);
printf("\n");
my_memset(arr, 0, sizeof(arr));
my_memset(&num, 0, sizeof(num));
my_memset(str, 65, sizeof(str) - 1);
for (i = 0; i < 5; i++) {
printf("%d\t", arr[i]);
}
printf("%ld\t", num);
printf("%s", str);
printf("\n");
return 0;
}
extern keyword
- extern Used to declare externally defined variables or functions ;
extern "C" {}Used to tell the compiler to use c How to compile ;
C++ Compiler and some variants C By default, the compiler compiles functions and variables in its own way , adopt extern Keyword can command the compiler with standard c How to compile ;
// g++ test.c
#include <stdio.h>
extern "C" {
int add(int a, int b){
return a + b;
}
}
int main(){
printf("res = %d\n", add(2, 3));
return 0;
}
extern Used to declare externally defined variables or functions ;
// test2.c
int g = 100;
int get_min(int a, int b) {
return (a < b) ? a : b;
}
//gcc test1.c test2.c
#include <stdio.h>
extern int g; // Declare references to externally defined variables
extern int get_min(int a, int b); // Declaration references externally defined functions
int main() {
printf("g = %d\n", g);
printf("get_min(3, 5) res %d\n", get_min(3, 5));
return 0;
}
sizeof keyword
sizeofIs the built-in indicator of the compiler , It's not a function ;sizeofUsed to calculate the memory size occupied by the corresponding entity , You can know without running , Compile time determines ;sizeofThe value of is determined at compile time ;sizeofIt's not a function ;
#include <stdio.h>
int main() {
int a;
printf("%ld\n", sizeof(a));
printf("%ld\n", sizeof a); //sizeof It's not a function
printf("%ld\n", sizeof(int));
// printf("%ld\n", sizeof int ); //error: expected expression before ‘int’
C In language int There can be no unsigned/signed/const In addition to the ;
It can't be sizeof
Type cannot be written like this ;
return 0;
}
const keyword
- const Decorate a read-only variable ;
- stay c In language const Decorated variables are read-only , Its essence is variable , Take up space in memory ;
- Essentially const Only useful for compilers , Useless at runtime ; At run time, its value can be changed by a pointer ;
use
const int cc = 1;After defining variables
- When doing lvalue , The compilation will report an error ;
- When doing the right value :
- Direct access
int cb = cc;Take the content directly from the variable table and replace ;- Indirect access to
int *p = (int *)&cc;Assign a value after accessing the memory at runtime ;
#include <stdio.h>
int main(){
const int cc = 1;
int *p = (int *)&cc;
printf("%d\n", cc);
// cc = 3; // Compiler error
*p = 3; // Can indirectly change cc Value
printf("%d\n", cc);
return 0;
}
stay c In language const Decorated arrays are read-only ;
const Decorated array space cannot be changed ( Right now c Compiler )
const int arr[5] = {1, 2, 3, 4, 5}; int *p = (int*)arr; int i = 0; for (i = 0; i < 5; i++) { p[i] = 5 - i; //oops! }
const Modify a pointer
int const * p;//p variable ,p The content of the ordinary variable pointed to is immutable ;const int * p;//p variable ,p The content of the ordinary variable pointed to is immutable , And int const *p Equivalent ;int * const p;//p Pointer immutable ,p The content of the ordinary variable pointed to is variable ;int const * const p;//p and p The contents of ordinary variables pointed to are immutable ;const int * const p://p and p The contents of ordinary variables pointed to are immutable , Equivalent to the previous sentence ;const * int p;// illegal
const In fact, it is to decorate the thing on the left ,const The and type identifier can be replaced, but cannot be crossed
*Number ;
formula
constbe relative to*Number ,(const stay Of ) Left number ,( stay The right side of ) The right finger is read-only ;
- When const Appear in the * Left side of No , The data pointed to by the pointer is read-only ;
const char *p = "hello world";- When const Appear in the * On the right side of No , The pointer itself is read-only ;
const Modify function parameters and return values
- const The modified function parameter indicates that it does not want to be changed in the function body ;
- const Decorated function return value means that the return value cannot be changed ( You can't be left-handed ), It is often used to change the pointer ;
const int * func() {
static int count = 0;
count++;
return &count;
}
int const *p = func();
//*p = 3;// Report errors
//p = NULL;// That's all right.
olatile keyword
- volatile Can be understood as " Compiler warning indicator ";
- volatile Used to tell the compiler that it must get the value of a variable from memory every time , Don't optimize ;
- volatile It mainly modifies variables that may be accessed by multiple threads ;
- volatile It can also modify variables that may be changed by undetermined factors ;
int obj = 10;
int a = 0;
int b = 0;
a = obj;
sleep(100);
b = obj;
When compiling, the compiler finds obj Not used as an lvalue , So it will " smart " The direct will obj Replace with 10, But the a and b All assigned to 10;volatile int obj = 10;// The compiler will directly access each time obj Storage location
const and volatile Whether a variable can be modified at the same time ?
Sure
const volatile int i = 0; This is the time i What properties does it have , How does the compiler handle this variable ?
const Tell us that we should not try to modify through the program i Value ;
volatile Tell compiler i The value of may change at any time , Read from memory every time you reference this variable , To get the latest resultsThis method is often used in drivers ;
struct How much memory does the empty structure occupy
#include <stdio.h>
struct D {
};
int main() {
struct D d1;
struct D d2;
printf("%d\n", sizeof(struct D));
printf("%d, %0x\n", sizeof(d1), &d1);
printf("%d, %0x\n", sizeof(d2), &d2);
return 0;
}
gcc The compiler defines the empty structure size as 0, Two different variables have the same address (gcc v7.5 Different addresses in );
g++ The compiler defines the empty structure size as 1, There will not be two variables with the same address ;
The flexible array is generated by the structure
A flexible array is an array whose size is undetermined ;
C The last element of a structure can be an array of unknown size ;
C Flexible arrays can be generated from structures in language ;
struct soft_array{
int len;
int array[];
}
union and struct The difference between
- struct Each field in the allocates space independently in memory
- union Allocate only the space of the largest domain , All domains share this space
struct A{
int a;
int b;
int c;
};
union B{
int a;
int b;
int c;
};
int main(){
printf("%d\n", sizeof(struct A)); //12
printf("%d\n", sizeof(union B)); //4
return 0;
}
union The use of is affected by the size of the system
+----------------------+
| Big end format |
| int i = 1; |
| 0x0 0x0 0x0 0x1 |
|----------------------|
| Low address High address |
+----------------------+
+----------------------+
| Small end format : |
| Put the low order data in the low address |
| int i = 1; |
| 0x1 0x0 0x0 0x0 |
|----------------------|
| Low address High address |
+----------------------+
union U {
int i;
char c;
};
union U u;
u.i = 1;
printf("0x%x\n", u.c); //0x1 or 0x01000000??
If it is a small end format ,1 Will be stored in the low address , The result returned to 1
If it is in big end format ,1 Will be stored in the high address , The result returned to 0
char arr[10] = {1,2,3,4,5,6,7,8,9,10};
// Store from low address to high address 01 02 03 04 05 06 07 08 09 10 ...
int *p = (int *)arr;
printf("0x%x\n", *p); // 0x4030201 // This is the small end format
int isLittleEndian(void) {
union {
int i;
char c;
} u;
u.i = 1;
printf("union int:1, char:0x%08x\n", u.c);
printf("%s endian\n", (u.c == 1) ? "little" : "big");
return (u.c == 1);
}
Unix And the byte order of the network is high byte order ;linux Is low byte order ;
High byte order is also called big end format ,Big endian: Store the high order byte in the starting address
Low byte order is also called small end format ,Little endian: Store the low order byte in the starting address
Example : Integer in memory 0x01020304 Storage mode
Memory address
&4000 &4001 &4002 &4003
LE 04 03 02 01
BE 01 02 03 04
Example :
If we were to 0x1234abcd Write to 0x0000 In memory at the beginning , The result is
BE LE
0x0000 0x12 0xcd
0x0001 0x34 0xab
0x0002 0xab 0x34
0x0003 0xcd 0x12
x86 series CPU All are little-endian Byte order of
enum enumeration
- enum Is a custom type ;
- enum The default constants are based on the previous straight +1;
- enum Variables of type can only take discrete values at the time of definition ;
enum color{
green, //0
red = 2,
blue //3
};
enum color c = green;
printf("%d\n", c); //0
- enum What is defined is the real constant
#defineConstants defined by macros are simply value substitutions , Enumeration constants are constants in the real sense ;#defineMacro constants cannot be debugged , Enumeration constants can ;#defineMacro constants have no type information , Enumeration constants are constants of a particular type `It is recommended to use
enumInstead of using#definemacro ;
typedef
- typedef Used to alias an existing data type , There is no redefined function ;
- typedef There is no new type ;
- typedef Redefined types cannot be unsigned and signed Expand ;
- typedef Alias an existing type ;
#defineReplace with a simple string , The concept of no alias ;
typedef char* PCHAR;
PCHAR p1, p2; //p1,p2 All are char Pointer to type ;
#define PCHAR char*
PCHAR p3, p4; // Namely char *p3, p4; p3 Is a pointer ,p4 It's ordinary. char Variable
int *p1, *p2; //p1,p2 It's all pointers
int *p1, p2; //p1 Is a pointer ,p2 Is a common variable
notes
C Symbols in language ,.;:?'"()[]{}%^&~-<>!|/#*=+
Masters have no moves, but they have moves ,akari.c,C The best exhibition award of the international language chaos competition
Which of the following comments are correct
1 int/*...*/i;
2 char *s="adcdefgh //hijklmn";
3 //Is it a \
valid comment?
4 in/*...*/t i;
/*,*/The parts between will be replaced by spaces ;- Comment symbols that appear between double quotation marks will be ignored , Processed as part of a string
- The continuation character can regard the following line as the continuation of the line
- Compiler error , After the comment is replaced, it is
in t i;
Annotation rules :
- The compiler will delete comments during compilation , But not simply delete , Instead, replace with spaces
- The compiler thinks that the contents between double quotes are strings , Double slashes are no exception
/*,*/Type annotations cannot be nested ;
You feel y=x/*p What does that mean?
compiler :
take
/*As the beginning of a comment , hold/*The following contents are regarded as comments , until*/Until it appears ;
In the compiler's view , Comments and other program elements are equal , therefore , As a programmer, you can't despise comments .
Note:
Writing notes is not talking to people , Be sure to be accurate and useful , Avoid obscurity and bloated .
Annotation principle
- Notes should be accurate and easy to understand , To prevent ambiguity , Wrong comments are harmful and unprofitable
- Comments are hints to the code , Avoid being bloated and distracting the guest from the host
- Avoid commenting at a glance
- Don't use abbreviations to annotate code , This may lead to misunderstandings
- Notes are used to explain the reason, not to describe the running process of the program
Line continuation operator
C Line continuation in language \ It is a sharp tool to indicate the behavior of the compiler
#include/*hello world */<stdio.h>
#def\
ine MAX \
255
//#define MAX 255
int main()
{
//\
This is a \
\
notes
i\
n\
t\
*\
p\
= \
NULL;
//int * p= NULL;
printf("%p\n", p);
return 0;
}
Use of continuation
- The compiler will automatically connect the character after the backslash to the previous line
- When connecting words , No space after backslash , There must be no spaces before the next line of the backslash
- The connector is suitable for defining macro code blocks
Definition of macro code block
#include <stdio.h>
#define SWAP(a,b) \
{ \
int temp = a; \
a = b; \
b = temp; \
}
Escape character
C Escape characters in languages \ It is mainly used to represent non echo characters , It can also represent regular characters \n, \t, \v, \b, \r, \f, \\, \', \a, \ddd, \xhh
- C The backslash in language has the functions of both a continuation character and an escape character
- When the backslash is used as a continuation, it can appear directly in the program
- When the backslash is used as an escape character, it continues to appear in the character live string
Single and double quotation marks
#include <stdio.h>
int main()
{
char *p1 = (char *) 1 ; //p1 The address is 0x1 The place of
char *p2 = (char *)'1'; //p2 Point to '1' The memory address represented 49
char *p3 = "1"; //p3 Point to string constant "1"
// printf("p1:%s\n", p1); //Segmentation fault
// printf("p2:%s\n", p2); //Segmentation fault
printf("p3:%s\n", p3); //1
// printf('\n'); //fmt='\n', '\n'=10, Segmentation fault
printf("\n"); //ok
return 0;
}
- C Single quotation marks in languages are used to represent character constants
‘a’ Represents a character constant , In memory 1 Bytes ,‘a’+1 Express ’a’ Of ASCII code +1, The result is ’b’;
- C Double quotation marks in the language are used to represent string constants
"a" Represents a string constant , In memory 2 Bytes ,“a”+1 Indicates pointer operation , Result pointing "a" Terminator ’\0’
#include <stdio.h>
int main()
{
char c = " ";
while (c=="\t" || c==" " || c=="\n") {
scanf("%c", &c);
} // One cycle won't
return 0;
}
Assign a string to a character variable char c == " "; What will happen ?
" "There is a space and a ’\0’ form , Suppose the address of the space is 0xaabbccdd, Because the character type has only one byte , Therefore, it will truncate dd Assign a value to c;c == 0xdd;"\t"," ","\n"There is also a specific address in memory 0x********, And c Comparison cannot be equal ;In the above procedure d Replacing all double quotation marks with single quotation marks can realize the author's original intention ;
- Essentially, a character enclosed in single quotation marks represents an integer ;
- Characters enclosed in double quotation marks represent a pointer ;
- C The compiler accepts string comparisons , But the meaning is wrong ( Actually, it is the comparison of the first address of the string );
- C The compiler allows strings to assign values to character variables , Its meaning is ridiculous ( In fact, the first address of the string is truncated and assigned to the character variable );
- C The compiler does not allow character variables to be assigned to strings , It is also not allowed to assign a string directly to a string , Because the string actually represents this string of characters ( Read only area ) The first address ;
Clear basic concepts , Stay away from low-level mistakes
Logical operators use
Logical operators &&,|| and !
#include <stdio.h>
int main() {
int i = 0;
int j = 0;
if (++i > 0 || ++j > 0) {
printf("%d\n", i); //1
printf("%d\n", j); //0
}
return 0;
}
#include <stdio.h>
int main() {
int i = 0;
int j = 0;
if (++i > 0 && ++j > 0) {
printf("%d\n", i); //1
printf("%d\n", j); //1
}
return 0;
}
Short circuit characteristics of logical operators
||,&& Start from left to right , The result of the current expression can determine the result of the entire expression , Then the following expression will not evaluate or call ;
#include <stdio.h>
int g = 0; // Global variables
int f() {
return g++; // Use first, then add
}
int main() {
if (f() && f()) { // Program short circuit , first f() Be called and get 0, then g++;
printf("%d\n", g); // Not to be carried out
}
printf("%d\n", g); //1
return 0;
}
Only one of the above code results is printed 1
! What is it
#include <stdio.h>
int main() {
printf("%d\n", !0); //1
printf("%d\n", !1); //0
printf("%d\n", !100); //0
printf("%d\n", !-1000); //0
return 0;
}
C Logical symbols in language ! I only know him 0, All I know is that I saw 0 Just go back to 1; Non zero is regarded as true , After the action, they all return 0;
Ternary operator a?b:c
It can be used as the carrier of logical operators
The rules : When a When the value of is true , return b Value , Otherwise return to c Value
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
int c = 0;
// int *p = NULL;
c = a < b ? a : b; //c = 1;
//(a < b ? a : b) = 3; // You can't be left-handed ;
*(a < b ? &a : &b) = 3; // legal
// p = (a < b ? &a : &b);
// *p = 3;
printf("%d\n", a);
printf("%d\n", b);
printf("%d\n", c);
return 0;
}
An operator
stay C Bit operators in languages
&Bitwise AND|Press bit or^Bitwise OR<<Move left>>Move right~According to the not ( Monocular operator )
Associative law a&b&c <=> (a&b)&c <=> a&(b&c)
Commutative law a&b <=> b&a
Move left and right. Pay attention
- Shift left operator
<<Shift the binary bit of the operand to the left , High level discard , Low complement 0; - Shift right operator
>>Shift the binary bit of the operand to the right , High complement sign bit , To discard in low order ;
0x1 << 2 + 3What would be the value of ?32, actually+,-The priority of the operation is higher than that of the shift operation
Error proofing criteria :
- Avoid bitwise operators , Both logical and mathematical operators appear in an expression ;
- Dangle operator , When logical operators and mathematical operators participate in the operation at the same time , Use as much as possible
()To express the order of calculation ;
How to exchange the values of two variables
#include <stdio.h>
#define SWAP1(a,b) \
{ \
int temp = a; \
a = b; \
b = temp; \
} // Additional variables are required to complete
#define SWAP2(a,b) \
{ \
a = a + b; \
b = a - b; \
a = a - b; \
} //a and b Very big time a+b It will overflow
#define SWAP(a,b) \
{ \
a ^= b; \
b ^= b; \
a ^= b; \
}
// a ^= b; => a=(a^b)
// b ^= a; => b=b^(a^b) = a^(b^b) = a^0 => a
// a ^= b; => a=(a^b)^a = (a^a)^b = 0^b => b
// This method does not use other variables , It won't overflow , And the operation efficiency is higher than ordinary mathematical operation
int main() {
int a = 1;
int b = 2;
SWAP1(a,b);
SWAP2(a,b);
SWAP(a,b);
printf("a = %d, b = %d\n", a, b);
return 0;
}
reflection 1
Suppose there is a sequence of numbers A, Among them, natural numbers occur even times , Only one natural number occurs an odd number of times , Program to find this natural number ;
Method 1
- Sort
- Traversal search
Sorting is too time consuming
Method 2
- Traverse , Find the maximum max;
- apply
array[max]; - Traverse , take
array[A[i]]++; - Determine which subscript corresponds to an odd number ;
Too much space complexity
Method 3
Considering or ^ Bit operation and exchange law , The natural number can be obtained by all elements or operations ;
#include <stdio.h>
int main() {
int a[] = {2, 3, 3, 5, 7, 2, 2, 2, 5, 7, 1, 1, 9};
int find = 0;
int i = sizeof(a)/sizeof(a[0]);
while (0 <= --i) {
find ^= a[i];
}
printf("find = %d\n", find);
return 0;
}
reflection 2
&&,||,! And &,|,~ Whether the meaning of is the same ? Can they be used interchangeably in conditional expressions ?
Different , One is logical operation , One is bit operation ;
1<<32What is the result ?
0;1<<-1What is the result of ?
0;
Auto increment and auto decrement operator
int i = 3;
(++i) + (++i) + (++i);
Is it necessary for you to write like this ?
stay C This is a gray area in language ,C The language specification only defines ++ operation , But there is no stipulation about how such an expression is calculated ; Each compiler has its own way of processing ;
int x = 3;
int k = (++x, x++, x+10);
Evaluate from left to right , Then take the value of the last expression as the result of the comma expression ;
front ++ First calculate and then , after ++ First , When the expression ends, it will increase by itself ;
So the result is k==14;
In the written interview ++i+++i+++i illegal
The law of greed :
++,-- Reading skills of expressions ;
- Each symbol processed by the compiler should contain as many characters as possible ;
- The compiler reads as many characters as possible one by one from left to right ;
- When the characters to be read cannot be combined with the characters already read into legal symbols ;
Compiler is greedy ;
Spaces can end the greed of the compiler ;
#include <stdio.h>
int main() {
int i = 0;
int j = ++i+++i+++i;
// According to the greedy method
// ++i+++i+++i
// ++i++ => 2++ => ERROR;
// ++i Do left value , No more ++ Self increasing ;
int a = 1;
int b = 2;
int c = a+++b; // a++ +b
int *p = &a;
b = b/*p; // When the compiler reads / I guess that users may want to do Division , Then continue to read ;
// Read that the next one is *, Just take the following as comments ;
// b = b / *p; // It's legal. , Spaces can end the greed of the compiler ;
// When writing code, you can use parentheses and spaces appropriately ; It can make the code more beautiful , It can also be properly error proofed ;
return 0;
}
Operator priority
- Elementary operators :
() [] -> . - Monocular operator :
! ~( Bit inversion ) ++ -- * & ( type ) sizeof; - Arithmetic operator : Multiply and divide first to get the remainder , Add or subtract after , Shift again ;
- Relational operator : First size , Retrial, etc (
== !=) - Bit operators :
& ^ | - Logical operators :
&& || - Ternary operator :
?: - The assignment operation ;
- Comma operation ;
#include <stdio.h>
#include <malloc.h>
typedef struct Demo {
int *pInt;
float f;
} Demo;
int func(int v, int m) {
return ((v & m) != 0);
}
int main() {
Demo *pD = (Demo*)malloc(sizeof(Demo));
int *p[5]; //int* p[5]
int i = 0;
i = 1, 2; //i == 1
printf("i:%d\n", i); //i:1
i = (1, 2);//i == 2
printf("i:%d\n", i); //i:2
//*pD.f = 0; //error, The member operator has higher priority
(*pD).f = 0;
free(pD);
return 0;
}
Fallible priorities
*p.num; the truth is that*(p.num)
.Has a higher priority than*, Actually, it's true p Take offset , As a pointer , Then take the member ;
We often use(*p).num( Equivalent top->num);->Operators can eliminate this problem ;
int *ap[];the truth is thatint* (ap[]);
[]Has a higher priority than*, actually ap Is an element calledint*An array of pointers ,
We sometimes useint (*ap)[], It's an array pointer ;ap Point to an array of integersint a[];
int *fp();the truth is thatint* (fp())
function
()Priority over*,fp Is a function , returnint*;
We sometimes useint (*fp)();Represents a function pointer , Used to point to a function ;
(val & mask != 0)the truth is thatval & (mask != 0)
==and!=Priority higher than bit operation ,
We often use(val & mask) != 0;
c = getchar() != EOF;the truth is thatc = (getchar() != EOF)
==and!=Higher than assignment , Special attention , Mistakes in this place are the hardest to find ;
We often use((c = getchar()) != EOF)Type of , Pay special attention to priority issues ;
msb << 4 + lsbthe truth is thatmsb << (4 + lsb)
Arithmetic operator is higher than displacement operator ,
We often use(msb << 4) + lsb;
i = 1, 2;the truth is that(i = 1), 2;
The comma operator has the lowest priority among all operators ,
We may often usei = (1, 2);, take 2 The result of that is to i,1,2Represents two expressions ;
Be careful
(),[]The highest priority , And then there was.and->Take member operator , Then there is post++after--; Then there are others ;
Be carefulafter ++,after --The priority of combination is high , But the value of the variable will not take effect until the end of the entire statement ;
*p1++ = *p2++namely*(p1++) = *(p2++);
++With the first p combination , And then with*combination ;while ((*p1++ = *p2++) != '\0');
Equivalent to
do {
*p1 = *p2;
p1++;
p2++;
} while (*(p1-1) != '\0');
Type conversion
C Language implicit type conversion
- Arithmetic expression , Low type to high type ;
- In the assignment expression , The value of the expression is converted to the type of the variable on the left ;
- When a function is called , The argument is converted to the type of the formal parameter ;
- Function return value ,return Expression converted to return value type ;
char -> unsigned char ->
short -> unsigned short ->
int -> unsigned int ->
long -> unsigned long -> double
float -> double
#include <stdio.h>
int main() {
char c = -2;
unsigned char uc = 1;
printf("c+uc=%hhX\n", c + uc);//FF
printf("c+uc=%hX\n", c + uc); //FFFF
printf("c+uc=%X\n", c + uc); //FFFFFFFF
short s = -2;
unsigned short us = 1;
printf("s+us=%hX\n", s + us); //FFFF
printf("s+us=%X\n", s + us); //FFFFFFFF
int i = -2;
unsigned int j = 1;
if ((i + j) >= 0) {
printf("i+j >= 0\n"); //v
} else {
printf("i+j < 0\n");
}
printf("i+j=%X\n", i + j); // FFFFFFFF
printf("i+j=%d\n", i + j); // -1
printf("i+j=%u\n", i + j); // 4294967295
// The representation of signed and unsigned types in memory is the same ;
// The key depends on how our computer interprets ;
return 0;
}
-2
+2: 00000000 00000000 00000000 00000010
-2: 11111111 11111111 11111111 11111101 + 1
-2: 11111111 11111111 11111111 11111110
-2: 0xFFFFFFFE
+1: 00000000 00000000 00000000 00000001
-2+1:
11111111 11111111 11111111 11111111
0xFFFFFFFF
-1
+1 00000000 00000000 00000000 00000001
-1: 11111111 11111111 11111111 11111110 + 1
-1: 11111111 11111111 11111111 11111111
-1: 0xFFFFFFFF
printf("%d", i + j);,%d: With int Type print 0xFFFFFFFF, Is interpreted as -1printf("%u", i + j);,%u: With int Type print 0xFFFFFFFF, A big positive number ;
The implementation of forced type conversion is to temporarily generate a new data , Assign values to new data using old data ;
Type conversion does not change the original data ;
int a = 1;
(unsigned char)a == 1;
(short)a == 1;
(int)a == 1;
It will not read more because of forced type conversion a Storage space , Just use the original data to temporarily generate a new type of data for later interpretation ;
The forced type conversion of pointer will affect the way the program reads and parses data ;
#include <stdio.h>
int main() {
char arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
void *p = NULL;
p = arr;
/* The forced type conversion of pointer will affect the way the program reads and parses data */
int inta = *(int *)p;
short shorta = *(short *)p;
char chara = *(char *)p;
// The data that is cast does not change
printf("%p, %p, %p\n", (int *)p, (short *)p, (char *)p);
// 0x7fff52f3b040, 0x7fff52f3b040, 0x7fff52f3b040
// The type of pointer changes , The way of parsing it will be different , The way to read the corresponding address is also different
printf("%x, %x, %x\n", inta, shorta, chara);
// 4030201, 201, 1
return 0;
}
#include <stdio.h>
int main() {
char arr[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
void *p = NULL;
p = arr;
/* The forced type conversion of pointer will affect the way the program reads and parses data */
printf("%x, %x, %x\n", *(char *)p, *(short *)p, *(int *)p);
/* 1, 201, 4030201 */
/* The implementation of forced type conversion is to temporarily generate a new data , */
/* Assign values to new books using old data ; Later operations will use new data ; */
printf("%x, %x, %x\n", (char)arr[0], (short)arr[0], (int)arr[0]);
/* 1, 1, 1 */
return 0;
}
Compile preprocessing
The process hidden by the compiler :
file.c + file.hAfter preprocessing cpp( Delete Note , Expand macros, etc ) obtainfile.i;- compiler gcc compile
file.iobtainfile.S( Assembly code ); - Assembler as assembly
file.SGet the target filefile.o; - The connector linker Connect the target file ( Connect
libc.a,libc.soetc. ) Get the executablefile.out;
precompile
- Deal with all the comments , Replace... With a space ;
- Will all
#defineDelete , And expand and replace all macro definitions ; - Processing conditional compilation instructions
#if,#ifdef,#elif,#else,#endif; - Handle
#include, Expand the included files ; - Keep what the compiler needs to use
#pragmaInstructions ;
Preprocessing instruction :
gcc -E file.c -o hello.i
compile :
- Perform a series of lexical analysis on the preprocessed files , Grammatical analysis and semantic analysis ;
- Lexical analysis mainly analyzes keywords , Identifier , Whether the immediate number is legal ;
- Syntax analysis mainly analyzes whether expressions follow syntax rules ;
- Semantic analysis further analyzes whether the expression is legal on the basis of grammatical analysis ;
- After the analysis, optimize the code and generate the corresponding assembly code file ;
Compile instructions :
gcc -S file.c -o hello.S
assembly :
The assembler converts assembly code into instructions that the machine can execute ,
Almost every assembly statement corresponds to a machine instruction ;
Assembly instruction :
gcc -C file.s -o hello.o
The linker
The main function of linker is to handle the parts referenced by each module , So that each module can be correctly connected ;
Static links
file1.o file2.o libc.a -- The linker linker-- obtain a.out;
a.outFile containsfile1.o,file2.o,libc.aAll files in ;The disadvantage is that the target file is large ,
The advantage is that it runs faster , Can operate independently ;
Dynamic links
file1.o lib1.so lib2.so -- The linker linker-- obtain a.out;
a.out inIt doesn't containlib1.soandlib2.soThe content of the document , Just link to when loading so file ;Advantage is so Documents can be maintained separately , The compilation target file is smaller ;
The disadvantage is that the operation requires loading , The running speed is slightly slow ;
- The compiler mainly divides the compilation work into preprocessing , Compile and assemble three parts ;
- The job of the linker is to connect each independent module into an executable program ;
- Static links are completed at compile time , Dynamic linking is completed during runtime ;
macro
Define macro constants
#defineDefine that macro constants can appear anywhere in the code ;#defineStart with this bank , Later code can use this macro constant ;
#define ERROR -1
#define PI 3.14
#define PATH_0 "D:\cpp\c.ppt"
#define PATH_1 D:\cpp\c.ppt
#define PATH_3 D:\cpp\
c.ppt
The above macro definitions are free of syntax errors ;PATH_3 Equivalent to D:\cpp\c.ppt
Define macro expressions
Macros can also name a calculation formula ;
Macros can use parameters to represent unknown content in the calculation formula , There is no limit to the number of parameters ;
Macro parameters can represent anything , So macro parameters have no type ;
Macros with parameters are processed in the way of secondary replacement ;
A macro used to name a calculation formula cannot define its own variables ;
#defineExpressions give the illusion of function calls , But not a function#defineExpressions can be more powerful than functions#defineExpressions are more error prone than functions
#define SUM(a, b) (a)+(b)
#define sum(a, b) ((a)+(b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define DIM(array) {sizeof(array) / sizeof(*array)}
// Macros are simply expanded and replaced
printf("%d\n", SUM(1,2) * SUM(1,3)); //1 + 2 * 1 + 3 ==5
printf("%d\n", sum(1,2) * sum(1,3)); //(1 + 2) * (1 + 3) == 12
int i = 1, j = 5;
printf("%d\n", MIN(i++, j)); // return 1, instead of 2
int i = 1, j = 5;
printf("%d\n", MIN(++i, j)); // return 3, instead of 2
int a[] = {1, 2, 3};
printf("%d\n", DIM(a));
The first three macros can find alternative functions , The last macro cannot find the corresponding function ;
/*
* macro macro demonstration
*/
#include <stdio.h>
// Define a macro , That is to 3.14 Give a name PI
#define PI (3.14f)
#define CIRCLE(r) (2 * PI * (r))
#define AREAR(r) (PI * (r) * (r))
int main() {
int radius = 0;
printf(" Please enter the radius : ");
scanf("%d", &radius);
printf(" The circumference is :%g\n", 2 * PI * radius);
printf(" The circumference is :%g\n", CIRCLE(radius));
printf(" The area is :%g\n", AREAR(radius));
return 0;
}
A macro is simply a text expansion replacement
- All macros used to represent the calculation formula should add a pair of parentheses outside the calculation formula , This ensures the priority of the macro replacement part ;
- All macro parameters representing numbers should be enclosed in parentheses , This can prevent the priority from being inconsistent with the expected priority after expansion and replacement ;
/*
* macro display
*/
#include <stdio.h>
#define SUB1(x, y) (x)- (y)
#define SUB2(x, y) ((x)- (y))
int main() {
printf("SUB1(8, 3) is %d\n", SUB1(8, 3)); //5
printf("21 - SUB1(5, 3) is %d\n", 21 - SUB1(5, 2)); //14, After Hongzhan, it violates the expected priority and leads to wrong results
printf("21 - SUB2(5, 3) is %d\n", 21 - SUB2(5, 2)); //18
printf("SUB2(10, 5-2) is %d\n", SUB2(10, 5 - 2)); //7
return 0;
}
/*
* macro
*/
#include <stdio.h>
#define MUL(x,y) ((x) * (y))
int main() {
printf("MUL(8-2, 9+1) = %d\n", MUL(8 - 2, 9 + 1)); //60
printf("60 / MUL(8-2, 9+1) = %d\n", 60 / MUL(8 - 2, 9 + 1)); //1
return 0;
}
Macros are simply replaced during the preprocessing phase of compilation ;
If a macro needs complex processing to get a result number , Then this macro must be written as an expression ;
Do not use the calculation result of self increment or self decrement as the parameter of macro ;
/*
* macro display
*/
#include <stdio.h>
#define SQUARE(n) ((n) * (n))
int main() {
int num = 4;
printf("SQUARE(num+1) = %d\n", SQUARE(num + 1)); //25
num = 4;
printf("SQUARE(++num) = %d\n", SQUARE(++num)); //36?
num = 4;
printf("SQUARE(num++) = %d\n", SQUARE(num++)); //16?//20
return 0;
}
Comparison between macro expression and function
- Macro expressions are processed during precompiling , The compiler does not know the existence of macro expressions ;
- Macro expressions use " Actual parameters " Completely replace formal parameters , No operation ;
- Macro expressions don't have any " call " expenses
- Recursive definitions... Cannot appear in macro expressions
Built in macro
__FILE__The name of the compiled file__LINE__The current line number__DATE__Compile time date__TIME__Compile time__STDC__Whether the compiler complies with the standard C language norm
Example
Define log macros
#include <stdio.h>
#include <time.h>
// #defien LOG(s) printf("%s:%d %s ...\n", __FILE__, __LINE__, s)
#define LOG(s) do { \
time_t t; \
time(&t); \
struct tm *my_tm = localtime(&t);\
printf("%s[%s:%d] %s\n", asctime(my_tm), __FILE__, __LINE__, s); \
} while(0)
void f() {
printf("Enter f() ...\n");
printf("End f() ...\n");
}
int main() {
LOG("Enter main() ...");
f();
LOG("Exit main() ...");
return 0;
}
#define f (x) ((x) - 1)
What does the macro above mean ?
The compiler thinks this is defining macros
frepresentative(x) ((x) - 1), Errors will be reported during actual compilation ;
If it is multiplication, you need to add*Number((x)*((x) - 1));
If you want to define a macro function, it should be written as#define f(x) ((x)-1);
Are macro definitions space sensitive ?
Macro definitions will put
#defineThe first field after ( End with the first space ) As macro , The latter part acts as a macro entity ;
So macros and entities are separated by the first space between them , From this point of view, macro definitions are sensitive to spaces , But the following entity part can contain spaces , That is, it is insensitive to spaces ;
macro " call " Are you sensitive to spaces ?
Macro just ( Use the physical part ) Expand replacement , The entity part is not space sensitive , The spaces used by macros are also insensitive ;# Conditional compilation
Conditional compilation can compile only some statements and ignore others at compile time ;
- Conditional compilation is a precompiled instruction command , Used to control whether to compile a piece of code ;
- Optional compilation at conditional compilation , and
if...else...Is selective execution ;
Conditional compilation
Conditional compilation can compile only some statements and ignore others at compile time ;
Conditional compilation form 1
#ifdef Macro name
...
#else
...
#endif
#ifndef Macro name
...
#else
...
#endif
The above statements can be compiled from two groups of statements according to whether a macro has been defined ;
/*
* Conditional compilation
*/
#include <stdio.h>
int main() {
//#ifdef ONE
#ifndef TWO
printf("1\n");
#else
printf("2\n");
#endif
return 0;
}
Conditional compilation form 2
#if Logical expression
...
#elif Logical expression ( Multiple )
...
#else
...
#endif
The above structure can select a group of compilation from multiple groups of statements according to any logical expression ;
#include <stdio.h>
// #define C 1
int main() {
#if (C == 1)
printf("This is 1st printf ...\n");
#else
printf("This is 2nd printf ...\n");
#endif
return 0;
}
It can also be passed during precompiling -D Options Specify compilation options
gcc -DC=1 -E test.c -o test.i
#include Perplexity of
#includeThe essence of is to embed the existing file content into the current file ;#includeThe indirect inclusion of will also produce the action of embedding the content of the file ;
Whether indirectly including the same header file will produce compilation errors ?
Meeting ;
Use conditional compilation to add header macro guard , Head pieces can be used at will ;
// global.h
int global = 10;
// test.h
#include <stdio.h>
#include "global.h"
const char* NAME = "Hello world!";
void f() {
printf("Hello world!\n");
}
// test.c
#include <stdio.h>
#include "test.h"
#include "global.h"
int main() {
f();
printf("%s\n", NAME);
return 0;
}
Compiler error :
glocal.h:1: error: redefinition of 'global'
glocal.h:1: error: previous definition of 'global' was here
Use single step compilation , View the preprocessed file ; You can find
global.hIncluded twice ;
Add conditional compilation macros for header files , Prevent duplicate inclusion of :
#ifndef _TEST_H_
#define _TEST_H_
code ;
#endif
Example :
// global.h
#ifndef _GLOBAL_H_
#define _GLOBAL_H_
int global = 10;
#endif
// test.h
#ifndef _TEST_H_
#define _TEST_H_
#include <stdio.h>
#include "global.h"
const char* NAME = "Hello world!";
void f() {
printf("Hello world!\n");
}
#endif
// test.c
#include <stdio.h>
#include "test.h"
#include "global.h"
int main() {
f();
printf("%s\n", NAME);
return 0;
}
The meaning of conditional compilation
Conditional compilation allows us to compile different code segments according to different conditions , Thus different object codes are generated ;#if ... #else ... #endif Processed by precompiler ; and if...else... Statements are processed by the compiler , Must be compiled into the object code ;
In practical engineering, conditional compilation is mainly used in the following two cases :
- Different product lines share one code ;
- Distinguish between debug and release versions of compiled products ;
Example :
Product line differentiation and debugging code application
#include <stdio.h>
// Distinguish between debug and release
#ifdef DEBUG
#define LOG(s) printf("[%s:%d] %s\n", __FILE__, __LINE__, s)
#else
#define LOG(s) NULL
#endif
void f() {
// Distinguish between beggar edition and Advanced Edition
#ifdef HIGH
printf("This is the high level product!\n");
#else
printf("This is the normal product!\n");
#endif
}
int main() {
LOG("Enter main() ...");
f();
// With low The beggar version
printf("1. Query Information.\n");
printf("2. Record Information.\n");
printf("3. Delete Information.\n");
// Top configuration premium
#ifdef HIGH
printf("4. High Level Query.\n");
printf("5. Mannul Service.\n");
printf("6. Exit.\n");
#else
printf("4. Exit.\n");
#endif
LOG("Exit main() ...");
return 0;
}
Skill summary
- From the compiler command line
-DOption to define the macros used by the preprocessor ; - Conditional compilation can avoid repeated inclusion of the same header file ;
- Conditional compilation can distinguish the codes of different product lines in engineering development ;
- Conditional compilation can define the release version and debug version of the product ;
Custom compilation error messages
#error Usage of
#error Used to generate a compilation error message , And stop compiling ;
usage :
#error message
Be careful :message There is no need to enclose with double quotation marks
#error The compilation indicator is used to customize the compilation error messages specially used by programmers ;#warning Used to generate compilation warnings , But it won't stop compiling ;
#include <stdio.h>
#define CONST_NAME1 "CONST_NAME1"
#define CONST_NAME2 "CONST_NAME2"
int main() {
#ifndef COMMAND
# warning Compilation will be stoped ...
# error undefined Constant Symbol COMMAND
#endif
printf("%d\n", COMMAND);
printf("%s\n", CONST_NAME1);
printf("%s\n", CONST_NAME2);
return 0;
}
gcc -DCOMMAND test.c
#line Usage of
#line Used to force new line numbers and compiled file names to be specified , And renumber the source code ;
usage :
#line number filename
notes : filename It can be omitted
#line The essence of compilation directive is to redefine __LINE__ and __FILE__
Generally used for debugging of early development
#include <stdio.h>
void func();
int main() {
printf("__func__:%s, %s, %d\n", __func__, __FILE__, __LINE__); //test.c 4
func();
printf("__func__:%s, %s, %d\n", __func__, __FILE__, __LINE__); //test.c 6
return 0;
}
// The following code has long To write
// The following code has long To write
// The following code has long To write
#line 4 "long.c"
// #line 4 "long.c" The next line of is defined as 4 That's ok , The name is defined as long.c
void func() {
printf("__func__:%s, %s, %d\n", __func__, __FILE__, __LINE__);//long.c 6
}
#pragma Preprocessing
#pragmaIs a compiler directive , Used to instruct the compiler to complete some specific actions ;#pragmaMany of the designators defined are unique to compilers and operating systems ;#pragmaIt is not portable between different compilers ;- The preprocessor will ignore those it does not know
#pragmaInstructions ; - Two different compilers may interpret the same in two different ways
#pragmaInstructions ;
- The preprocessor will ignore those it does not know
General usage :
#pragma parameter
notes : Different parameter The syntax and meaning of parameters vary
#pragma message
messageParameters have similar implementations in most compilers ;messageParameter outputs a message to the compile output window at compile time ;messageIt can be used for code version control ;
Be careful :
messageyesVCUnique compiler directive ,GCCIgnore it ;
#include <stdio.h>
/* #define ANDROID23 1 */
#if defined(ANDROID20)
#pragma message("Compile Android SDK 2.0...")
#define VERSION "Android 2.0"
#elif defined(ANDROID23)
#pragma message("Compile Android SDK 2.3...")
#define VERSION "Android 2.3"
#elif defined(ANDROID40)
#pragma message("Compile Android SDK 4.0...")
#define VERSION "Android 4.0"
#else
#error Compile Version is not provided!
#endif
int main() {
printf("%s\n", VERSION);
return 0;
}
#pragma pack Memory alignment
- Data alignment
Alignment
- Different types of data in the structure are arranged in memory according to certain rules , Instead of always arranging one by one ( Easy to address );
- The address of any member variable must be its alignment parameter A Integer multiple , This rule is called data alignment ;
Data alignment will cause gaps between different member variables inside the structure ;
- Alignment parameters of each member variable A Value rules :
a=min(n, max(self))The common type is#pragma pack(n)、 Self size , The minimum value of both ;a=min(n, max(sizeof element...))The structure type is#pragma pack(n)、 The largest child variable , The minimum value of both ;
n Align parameters for the system , The general default is 4, Can pass
#pragma pack()Class macro customization
- Data complement
Completion
- The size of a structure variable must be C Integer multiple , This rule is called data completion ;
- This kind of complement may cause the structure to occupy more wasted bytes at the end ;
- Structure complement parameters C The value rules of :
c=max(A)That is, the alignment parameters of all members A The maximum of ( Certainly not more than pack value , Try to prove );
Analysis examples
// x *
// y y
// z *
typedef struct {
char c1; //s=1, a=min(4,1), @+0; Alignment parameters a=1;
short s; //s=2, a=min(4,2), @+2; Alignment parameters a=2;
char c2; //s=1, a=min(4,1), @+4; Alignment parameters a=1;
} ST1; //6 = n*2, c=max(a)==2; Supplement parameters c=2
// x x y y
// y y y y
// z z z z
// m * * *
typedef struct {
short s; //s=2, a=min(4,2), @+0; Alignment parameters a=2;
ST1 st1; //s=6, a=min(4,max(1,2,1)), @+2; Alignment parameters a=2;
int i; //s=4, a=min(4,4), @+8; Alignment parameters a=4;
char c; //s=1, a=min(4,1), @+12; Alignment parameters a=1;
} ST7; //16 = n*4, c=max(a)==4; Supplement parameters c=4;
struct Member alignment , Overall complement
- Common type members , Alignment parameters A by ( Own type size and specified alignment parameters n) The minimum value of both , namely
a=min(n, max(self)); - Members of structure types , Alignment parameters A Align the maximum value in the parameter for all its members , namely
a=min(n, max(sizeof element...)); - The total length of the structure is the complement parameter C Integer multiple , Supplement parameters C Align the maximum value of the parameter for all members , namely
c=max(A);
The order of the sub variables in the structure will affect the size of the structure , The memory space can be saved by writing the subvariables in front of them with small space ;
struct test1 {
char c1; // 1
short s; // 2
char c2; // 1
int i; // 4
}; //12
struct test2 {
char c1; // 1
char c2; // 1
short s; // 2
int i; // 4
}; //8
test1 and test2 Whether the memory space occupied by the two types is the same ?
#include <stdio.h>
struct test1 {
char c1;
short s;
char c2;
int i;
}; //12
struct test2 {
char c1;
char c2;
short s;
int i;
}; //8
int main() {
printf("%d, %d\n", sizeof(struct test1), sizeof(struct test2)); //12, 8
return 0;
}
Why memory alignment is needed ?
- CPU The reading of memory is discontinuous , But read in blocks , The size value of the block can be 1,2,4,8,16 byte ;
- When the data of the read operation is not aligned , It takes two bus cycles to access memory , Therefore, the performance will be greatly reduced ;
- Some hardware platforms can only get certain types of data from the specified address , Otherwise, a hardware exception will be thrown ;
The system default alignment parameters
#pragma pack Can change the default alignment of the compiler
#pragma pack(n) // Set the compiler to follow n Byte alignment ,n Can take 1,2,4,8,16
#pragma pack() // Default 4 Byte alignment
#pragma pack(push) // Press the current number of aligned bytes into the top of the stack , Do not change the number of aligned bytes
#pragma pack(push,n) // Press the current number of aligned bytes into the top of the stack , And in accordance with the n Byte alignment
#pragma pack(pop) // The number of aligned bytes at the top of the pop-up stack , Do not change the number of aligned bytes
#pragma pack(pop,n) // Pop up the top of the stack and discard it directly , according to n Byte alignment
#pragma pack(push,1) // You can specify the number of bytes to align and complement the structure #pragma pack(pop) // recovery push The value of the former
#include <stdio.h>
#pragma pack(push,2)
struct test1 {
char c1;
short s;
char c2;
int i;
}; // 10
struct test2 {
char c1;
char c2;
short s;
int i;
}; // 8
#pragma pack(pop)
int main() {
printf("%d, %d\n", sizeof(struct test1), sizeof(struct test2)); // 10, 8
return 0;
}
Structure size analysis
#include <stdio.h>
// Assume that the storage locations all start from 0
#define OFFSET_OF(type, member) ((size_t)(&((type *)0)->member))
#define OO1(t, m1) #m1,OFFSET_OF(t, m1)
#define OO2(t, m1, m2) OO1(t, m1), OO1(t, m2)
#define OO3(t, m1, m2, m3) OO2(t, m1, m2), OO1(t, m3)
#define OO4(t, m1, m2, m3, m4) OO3(t, m1, m2, m3), OO1(t, m4)
#define OO5(t, m1, m2, m3, m4, m5) OO4(t, m1, m2, m3, m4), OO1(t, m5)
#define SHOW_OO1(t, m1) \
printf("\n%s offset: %s:%ld\n", #t,OO1(t,m1))
#define SHOW_OO2(t, m1, m2) \
printf("\n%s offset: %s:%ld, %s:%ld\n", #t,OO2(t,m1,m2))
#define SHOW_OO3(t, m1, m2, m3) \
printf("\n%s offset: %s:%ld, %s:%ld, %s:%ld\n", #t,OO3(t,m1,m2,m3))
#define SHOW_OO4(t, m1, m2, m3, m4) \
printf("\n%s offset: %s:%ld, %s:%ld, %s:%ld, %s:%ld\n", #t,OO4(t,m1,m2,m3,m4))
#define SHOW_OO5(t, m1, m2, m3, m4, m5) \
printf("\n%s offset: %s:%ld, %s:%ld, %s:%ld, %s:%ld, %s:%ld\n", #t,OO5(t,m1,m2,m3,m4,m5))
#pragma pack(8)
//#pragma pack(4)
//#pragma pack(2)
typedef struct S1 {
short a; // 2, a=min(pack, 2)
long b; // 8, a=min(pack, 8)
} S1;
typedef struct S2 {
char c; // 1, a=min(pack, 1)
S1 d; // 2+8,a=min(pack, max(2,8))
double e; // 8, a=min(pack,8)
} S2;
#pragma pack()
int main() {
printf("%ld, %ld\n", sizeof(S1), sizeof(S2));
SHOW_OO2(S1, a, b);
SHOW_OO3(S2, c, d, e);
return 0;
}
Running results
#pragma pack(8)16,32; 0,8; 0,8,24;#pragma pack(4)12,24; 0,4; 0,4,16;#pragma pack(2)10,20; 0,2; 0,2,12;
#
# Operator is used to convert macro parameters to strings during precompiling
#include <stdio.h>
#define CONVERS(x) #x
int main(){
printf("%s\n", CONVERS(Hello world!));
pritnf("%s\n", CONVERS(100));
printf("%s\n", CONVERS(while));
printf("%s\n", CONVERS(return));
return 0;
}
# The wonderful use of operators in macros
#include <stdio.h>
#define CALL(f, p) (printf("Call function %s\n", #f), f(p))
// Print a sentence call function, Then call the function
int square(int n) {
return n * n;
}
int f(int x) {
return x;
}
int main() {
printf("1. %d\n", CALL(square, 4));
printf("2. %d\n", CALL(f, 10));
return 0;
}
##
## Operator is used to glue two symbols during precompiling
#include <stdio.h>
#define STR(n) #n
#define LOCAL(n) woshiqianzui_##n
int main(){
printf("STR(abc) is %s\n",STR(abc));
int woshiqianzui_num = 10;
int LOCAL(num1) = 20;// Equivalent to the previous sentence , Easy to write
printf("%d\n",woshiqianzui_num);
printf("%d\n",LOCAL(num1));
return 0;
}
utilize ## Define the structure type
#include <stdio.h>
#define STRUCT(type) typedef struct _tag_##type type;\
struct _tag_##type
STRUCT(Student) {
char* name;
int id;
};
int main() {
Student s1;
Student s2;
s1.name = "s1";
s1.id = 0;
s2.name = "s2";
s2.id = 1;
printf("%s\n", s1.name);
printf("%d\n", s1.id);
printf("%s\n", s2.name);
printf("%d\n", s2.id);
return 0;
}
边栏推荐
- ActiveMQ -- persistent mechanism
- Guangzhou has carried out in-depth "100 day action" to check the safety of self built commercial houses, and more than 2 million houses have been checked in two months
- Six storage types in C language: Auto register static extern const volatile
- 『每日一问』简单聊聊JMM/说说对JMM的了解
- ActiveMQ -- kahadb of persistent mechanism
- Redis sentry, master-slave deployment details
- registration status: 204
- Programmers can't SQL? Ashes Engineer: all waiting to be eliminated! This is a must skill!
- sql注入
- Difference between redis and mongodb (useful for interview)
猜你喜欢

LabVIEW experiment - temperature detection system (experimental learning version)

PL/SQL工具导出sql文件所使用的命令是什么?

Common tool classes under JUC package

OpenCV实现简单的人脸追踪

Live broadcast preview | how to build an enterprise cloud management platform in the cloudy era?

Table table expansion internal row switching effect

The operation cannot be completed because a folder or file in it is already open in another program

Feiling ok1028a core board adapts to rtl8192cu WiFi module

CentOS changes MySQL database directory

对称式加密与非对称式加密的对比
随机推荐
Learning Weekly - total issue 63 - an open source local code snippet management tool
学习周刊-总第 63 期-一款开源的本地代码片段管理工具
Guangzhou has carried out in-depth "100 day action" to check the safety of self built commercial houses, and more than 2 million houses have been checked in two months
Nacos搭建配置中心出现client error: invalid param. endpoint is blank
Oracle10g单实例数据库升级到哪个版本好,求建议
Learn about spark project on Nebula graph
registration status: 204
Comments on specific applications of camera
c语言中的六个存储类型:auto register static extern const volatile
activemq--死信队列
idea中将lib目录下的jar包加入到项目中
『每日一问』简单聊聊JMM/说说对JMM的了解
360 degree drag panorama plug-in tpanorama.js
OmniPeek packet capturing tool
Probe into Druid query timeout configuration → who is the querytimeout of datasource and jdbctemplate effective?
[stl]list Simulation Implementation
This ten-year content industry infrastructure company is actually an invisible Web3 pioneer
Table table expansion internal row switching effect
Uniapp intercepts route jumps through addinterceptor to control whether the page needs to log in
mysql中的数据结果排名