In-C + + depth analysis

Source: Internet
Author: User
Array

The array in development is a common data structure, but of course we know that arrays are equivalent to a container, but it can not only store values and characters, but it also holds the entry address of the function and the data of the struct.

typedef  struct _value{       int val_1;       int val_2;} Value;typedef  struct _table{       char  index;       Char  data[100];int (*unisetfunc) (value*);} TABLE; int  Add (VALUE  *val) {       int  temp = 0;       temp = Val->val_1 + val->val_2;       return  temp;} TABLE  page[10]{       {"First section", "ABCDEFGHIJKLMN", add},       {"Second section", "Opqrstuvwxyz", NULL}}; int main () {       VALUE  addvalue;       Addvalue.val_1 = 2;       addvalue.val_2 = 4;       int  result = 0;       Result  = page[0]-> unisetfunc (&addvalue);       printf ("The result of Add is%d\n", result);       return 0;}

At this point the array is transformed into a structure similar to the dictionary in the Python language, allowing for subsequent development and additional upgrades and maintenance.

Code Analysis: First we know that the function name can be used as the entry address of the function (similar to the name of the array to represent the address of the array), so in the table struct we define a member function int (*unisetfunc) (value*); Here Unisetfunc as the entry parameter of the function, value* represents the formal parameter type of the function, an array of table type PAGE[10] that contains the structure of the data and the function's entry address, you can call Page[0]-> Unisetfunc (& AddValue) to invoke the Add function indirectly and to implement the sum of Addvalue.val_1 and addvalue.val_1 two numbers in AddValue.

Memory hit ratio issues:

In order to improve the efficiency of code operation, to make full use of memory space, you should continuously access the memory area.

We know that the location of the array in memory is a whole block of area, and is continuously stored, for the defined array array[2][2], assuming that the address of array[0][0] is 0x04030, then array[0][1],array[1][0],array[1 ][1] Addresses are 0x04031, 0x04032, 0x04033; increasing the memory hit rate should be as contiguous as possible to access the area of memory space, rather than jumping access; Let's look at a matrix multiplication problem.

              for (int i = 0, i < 2; ++i)              {for                            (int j = 0, J < 2; ++j)                            {for                                          (int k = 0; k < 3; ++k)                                          {
  
   MATRIX[I][J] + = matrix1[i][k] * matrix2[k][j];}}              }
  

The above code is commonly used to multiply the matrix matrix1 and matrix2 and then assign to the matrix method, that is, using the matrix1 matrix to get the row vector multiplied by the matrix matrix2 column vector, and then assigned to the matrix, so because the matrix in memory storage structure, We can clearly know that access to the MATRIX2 is not the use of continuous access, so the memory hit rate is low. Next we look at a high memory hit rate method.

for (int i = 0, i < 2; ++i)       {for                     (int k = 0, K < 3; ++k)                     {for                                   (int j = 0; j < 2; ++j)                                   {
  
   MATRIX[I][J] + = matrix1[i][k] * matrix2[k][j];}}       }
  

It can be seen that the code simply swaps the second for loop with the third for loop, while the rest does not change, but the memory hit rate is greatly increased, and we use the matrix1 and matrix2 matrix to multiply and then add the elements in sequence, For the purpose of multiplying matrices so that no memory misses occur when accessing the matrix1 and matrix2 matrices, thus increasing the probability of memory hits.




The relationship between Volatile,const and static:


The Const keyword is a constant keyword, it acts as a constant, does not allow the program to change the value of the constant, such as the const int value = 12; This constant value does not allow the program to change it, and the Const keyword is often used in the development process. In order to prevent the program from accidentally changing a fixed constant, we should give it a const keyword in a timely manner, and the Const keyword must be initialized directly to the constant when it is used for constants, because it is not allowed to be changed during the whole program operation, so it must be initialized immediately, for example: const INT Value = 12 is correct, while the const int value; value = 12; Such a syntax is wrong! Let's look at a slightly more difficult question, constant pointers and pointer constants. Look at the code first:

#define SWITCH 1int Main () {       int val_1 = 5;       int val_2 = ten;       const int *P1 = &val_1;       int const *P2 = &val_1;       int *const p3 = &val_1; #ifdef  switch          //This is a switch       *P1 =;       *P2 = +;       *P3 = #endif #ifndef  SWITCH       p1 = &val_2;       P2 = &val_2;       P3 = &val_2; #endif       printf ("%d\n", *P1);       printf ("%d\n", *P2);       printf ("%d\n", *p3);       return 0;}


Executed under the Cygwin compiler, we can see an error like this:



We can clearly see that the pointer P1 and P2 can only read the value in Val_1 as a pointer constant, that is, the contents of the variable it refers to cannot be changed, so *p1 = 20; *P2 = 21; two commands are wrong! (#ifdef switch ... #endif for conditional compilation is a macro switch). Then we comment out the # define SWITCH 1 statement and run the second block of code to get the result as follows:






From the error can be seen P3 as a constant pointer, it can only point to a fixed address, and not change the direction it refers to, so P3 = &val_2; operation is wrong, so the correct code is as follows:

int main () {              int val_1 = 5;              int val_2 = ten;              const int *P1 = &val_1;              int const *P2 = &val_1;              int *const p3 = &val_1;              printf ("frist\n");              printf ("%d\n", *P1);              printf ("%d\n", *P2);              printf ("%d\n", *p3);              P1 = &val_2;              P2 = &val_2;              *P3 =;              printf ("second\n");              printf ("%d\n", *P1);              printf ("%d\n", *P2);              printf ("%d\n", *p3);              return 0;}


The result of the operation is:



Final end: The constant pointer (const int *P or int const *p) indicates that the pointer p cannot change the value that it points to in the address, but can change the address it points to, and the pointer constant (int *const p) indicates that the pointer p cannot change the address it points to. That is, the pointer cannot change the position it points to, but it can change the content in the position it refers to. If you want the pointer to not change the position you are pointing at, and you cannot change the content, you can define:


const INT * Const P = &a; or int const *CONST p = &a; When defining a function, if the entry parameter does not wish to be changed during the execution of the program, it must be decorated with a const, which prevents the program from changing it, and, for the formal parameter, an argument whether or not a const modifier can pass it into a const-shaped parameter. A const-shaped argument cannot be passed into a non-const formal parameter, so to make the compilation error when defining a function, be sure to modify the amount that you do not want to change with the const keyword.


The static keyword, which has the function of storing the variable in memory instead of depositing it in the register (the variable is stored in the heap instead of the stack), and the variable of the action holds only the last acquired value. Next, let's look at a piece of code.

void  Countfun () {       static  int  count = 0;++count;printf ("This is%d number, enter to this function!\n", c Ount);} int main () {for       (int i = 0; i < 5; ++i)       {                     countfun ();} return 0;}


The result of this code operation is as follows:





If the static keyword is removed, the result of the run is as follows:



From this we can clearly see that the static function of the variable count will only be deposited into the current results, so the loop call Countfun () function when the time does not reset the count variable to 0, but save the previous value.


The static keyword is widely used in projects, not only with the features described above, but also if you want to define a global variable that is only present in the entire project. c file, the global variable should also be modified with static, so that in other files can not access the variable, thereby reducing the coupling between the modules, improve the cohesion of the module, to prevent other files to change it, thus more secure.


The volatile keyword is a very important keyword in the embedded world, especially in hardware-related or multi-threaded programming. Volatile keyword-Modified variable description It can be changed at any time, we do not want the compiler to optimize some code, we need to modify this variable with the volatile keyword, so that every time the program accesses the variable is directly from the memory extracted, Instead of extracting a copy of the variable from a temporary register, use it! For example, when we want to implement an interrupt processing, the marker that is used to make the judgment condition should be modified with the volatile, so that when the interrupt is triggered elsewhere, it can be detected in real time, so that the interrupt is not ignored because of optimization. Next we look at a piece of code:

int main () {    volatile int i = ten;    int a = i;    printf ("i =%d\n", a); __asm{        mov dword ptr[ebp-4], 0x10}int b = i;printf ("i =%d\n", b); return 0;}

This program outputs the result for i = 10;i = 16; If the volatile keyword is removed, the result is i = 10;i = 10;




That is, without the keyword will ignore the assembly code, so in order to prevent code optimization and can be detected in a timely manner the external program changes to the variable, we must add the volatile keyword. We know that the volatile keyword indicates that the amount is variable, the const keyword indicates that the amount is constant cannot be changed, then whether volatile and const can be modified together with the same amount, is certain, for example, in hardware programming ROM storage data is not allowed to change the user, That is, the pointer to the data must be a constant pointer (const int *P = &ram_data), but the developer can change it unexpectedly, in order to prevent the contents of the ROM is accidentally changed, and the user program did not find in time, it must be modified with the amount of volatile, So the pointer should be defined like this (volatile const int *p = &rom_data).





Bit arithmetic


In the process of digital decoding and encoding, the operation of bit operation is commonplace, while bit operation is also the first in improving the performance of the program, so the bit operation is the problem that must be understood deeply.



In multiplication and division operations I can use not running to improve the quality of the code, for example: a = a * 16; This operation can be completely replaced by: a = a << 4; we know that the left shift is equivalent to multiplying the original number by 2, and the left shift N is the equivalent of multiplying by 2^n, provided that no overflow occurs , so the above example is equivalent to moving the number A to the left 4 bits, for some times not 2 of the integer power, such as a = a * 9; it can be rewritten to a = (a << 3) + A; The same right shift is equivalent to dividing by 2 by the power of an integer, of course, all of these cases are in the case of no data overflow, so the bit operation to be extra careful, otherwise there is a high likelihood of error.


In the process of data type conversion also need to do bit operations, for example, we want to put a unsigned short type of data into an array of unsigned char type, we need to do bit operations, first of all to understand that unsigned short occupies 16 bytes, unsigned char occupies 8 bytes, want to put large bytes of data into the small section, you have to split the large byte, the high 8-bit and the lower 8 is separated from each other, to see the implementation code:

unsigned char * drawcompo_pj_bt_change (unsigned short *subarray) {    unsigned char temp[500];    (void) _sys_memset (&temp, 0x00, sizeof (temp));    unsigned short i = 0;    while (subarray[i]! = 0x0000)    {            if ((Subarray[i] & 0xff00)  = = 0x0000)            {                    temp[i++] = (unsigned Char) (Subarray[i] & 0x00ff);            }            else            {                    Temp[i] = (unsigned char) ((Subarray[i] & 0xff00) >> 8);                    temp[i++] = (unsigned char) (Subarray[i] & 0x00ff);            }    }    Temp[i] = ' + ';    return temp;}


Temp[i] = (unsigned char) ((Subarray[i] & 0xff00) >> 8); Subarray[i] High 8 bits of data, temp[i++] = (unsigned char) (Subarra Y[i] & 0X00FF); Take the lower 8 bits. This enables the full deposit of high-byte data into a low byte.


Bit arithmetic can also be used to determine the symbol of a variable, we know that for a signed variable, its highest bit is its sign bit, so check the highest bit of change to know whether the variable is positive or negative. Look at the code:

int main () {short    test_data = -12;    if (Test_data & 0xf000)    {            printf ("This number is negative");    }    else    {            printf ("This number is positive");    }    return 0;}


For those who want to exchange two-digit values, we usually do the following:

void swap (int &data1, int &data2) {    int temp = 0;    temp = data1;    Data1 = data2;    Data2 = temp;}


This kind of code is easy to understand, but the drawback is that it will produce a temporary variable temp, then we use bit operations to rewrite the program;

void swap (int &data1, int &data2) {    data1 = data1 ^ data2;    data2 = data1 ^ data2;    Data1 = data1 ^ data2;}

From the code above we can see that a temporary variable is missing, and it also accelerates the efficiency of the code operation.


Tail Recursion:


Recursive invocation brings us a lot of convenience and simplifies the code, making the program look more concise and clear, but recursive invocation is often accompanied by a potential danger: out of the stack, let's look at a common recursion.


int factorial (int n) {    if (n < 1)    {            return 1;    }    else    {            return factorial (n-1) *n;    }   }

Usually in the case of a number of factorial we are accustomed to the above method, but the analysis that when the input n value is large, factorial (n-1) *n calculated value will continue to press on the stack, generate a lot of temporary variables, waiting for the next value of the determination to be calculated, However, the size of the stack in memory is fixed, and when the input n value is large, a stack overflow is most likely to occur! Therefore, there is a good way to solve this problem that is called tail-recursive. Next we look at this powerful algorithm.

int factorial (int n, int m) {    if (n < 2)    {            return m;    }    else    {            factorial (n-1, n*m);}    }

As can be seen from the code, by introducing a new parameter m to hold each recursive value generated, so as to avoid each recursive to the stack operation, there will not be a heap overflow phenomenon, and the normal recursive recursion can only wait for the next recursive result after the results can continue to operate, and the tail recursive each execution will perform the operation, once the loop executes completes can obtain the result, its time complexity is O (n);

  • Related Article

    Contact Us

    The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

    If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.