Embedded C language summary (a) keywords, data types, memory management, pointers and arrays

  It is not complicated from the C programming language syntax, but to write high quality and reliable embedded C program is not easy, not only need to be familiar hardware features and defects, but also have a certain understanding of compiler theory and computer technology. Embedded in so many years of development, we have accumulated a lot of knowledge and thinking in this area, we want to conclude, elaborate Embedded C language of the important points of the system, and will have this article. Based on its own practice on ARM chips as the core, in conjunction with relevant information to explain the need to understand embedded C language focus, I hope everyone reading this article can be harvested.

1. Keyword

  Keywords are reserved identifier C language with special features, functions can be divided in accordance with

  1) data type (commonly char, Short, int, Long, unsigned, a float, Double )

  . 2) and the operation expression ( =, +, -, *, the while, the while-do, IF, GOTO, Switch-Case )

  3) data storage ( Auto , static, extern, const, Register, volatile, Restricted ),

  4) Structure ( struct, enum, Union, typedef ),

  5) bit operations, and logic operations (. <<, >>, &, |, ~ , ^, && ),

  6). Pretreatment (#define, #include, # error, # if ... # elif ... # else ... # endif , etc. ),

  7). Platform extension keywords ( __asm, __inline , __ syscall )

  These keywords together form the C syntax for embedded platforms.

  Embedded applications can be abstracted from the logic into three parts:

  1) input data (e.g., sensor signals, input interfaces),

  2) processing the data (e.g., decoded and packet protocol, AD conversion sample values, etc.)

  3) The output data of the (GUI display, output pin state, control voltage output of the DA, the duty ratio of the PWM wave and the like),

  For management data to develop throughout the entire embedded applications, which includes a data type, storage management, and bit logical operations, and data structures, to achieve the above-described C language functions supported from the grammar, and provide optimization mechanism, embedded in response to a more limited resources and the environment.

2 Data Type

  C language supports common character, integer, floating-point variables, and some microcontroller 51 also supports ' bit (bit) and SFR (register) data types to meet specific address control. C language only a predetermined minimum range for each of the basic data types, the same type of different lengths may take up storage space on different chip platform, which need to be considered when transplant compatibility subsequent code implementation, the C language provides the typedef keyword is used to deal with this situation, most adopted in support of cross-platform software projects, typically as follows:

typedef unsigned char uint8_t; 
typedef unsigned short uint16_t; 
typedef unsigned int uint32_t; 
...... 
typedef signed int int32_t;

  Since the basic data of different widths on different platforms, how to determine the width of the basic data types such as int the current platform, which requires an interface sizeof C language provides, the implementation is.

printf("int size:%d, short size:%d, char size:%d\n", sizeof(int), sizeof(char), sizeof(short));

  There are also important knowledge, is the width of the pointer, such as

char *p; 
printf("point p size:%d\n", sizeof(p));

  This fact, and the width of addressable chip related, such as 32-bit MCU width is the width of 4, 64-bit MCU is 8, which is also sometimes see bits wide MCU relatively simple way.

3. The memory management and storage architecture

  C language program allows for sophisticated memory management, through the scope, and keyword extern, static, to achieve the fine processing mechanism, in accordance with the different areas of hardware, memory allocation There are three ways (Adapted from high quality C ++ programming):

  1). Dispensed from the static storage area. Memory in the program compile time has been allocated good, this memory during the entire operation of the program are present. Such as global variables, static variables.

  2) Create on the stack. During a function, the function of the storage unit can create a local variable on the stack, the memory cells are automatically released at the end of the function execution. Stack memory allocation operation in the processor instruction set, high efficiency, but the limited amount of memory allocated.

  3) From the heap allocation, also known as dynamic memory allocation. Program at run time to apply any number of memory using malloc or new, the programmer is responsible when using free or delete release memory. Dynamic memory is determined by the lifetime of programmers, using very flexible, but at the same time experiencing the most problems.

  Here we look at a simple example of C language.

// main.c 
#include <stdio.h>  
#include <stdlib.h> 
static int st_val; // static global variable - static memory int ex_val; // global variable - static memory int main ( void ) { int A = 0 ; // local variables - application on the stack int * PTR = NULL; // pointer variable static int local_st_val = 0 ; // static variable local_st_val + = . 1 ; A =local_st_val; PTR = ( int *) the malloc ( the sizeof ( int )); // the application stack space IF (PTR =! NULL) { the printf ( " * P value:% D " , * PTR); Free (PTR) ; ptr = NULL; // the need to free ptr blank, otherwise lead to subsequent ptr checksum failure occurs pointer field } }

  Scope C language not only describes the area accessible identifier, in fact, provides for the storage area of the variable, the variable file-scope st_val and ex_val was assigned to the static storage area, which is mainly limited to the static keyword variables can accessible to other files, and the variable scope of a code block, and PTR will have different types of local_st_val, allocated to different regions, wherein a is a local variable, it is assigned to the stack in, as the pointer PTR, allocated by malloc space, so the definition of the heap, and local_st_val keywords were limited, assigned to represent the static storage area , where it comes to the important points, meaning static at file scope and code block scope is different: file role external links defined for the domain of the functions and variables (other files can be accessed), then the scope for the block to locate a variable static memory .

  For the C language, if such knowledge for understanding the basic memory management is sufficient, but for embedded C, the definition of a variable, it is not necessarily in the memory (SRAM), it is also possible in the FLASH space, or directly from the store register (register optimizing section defined variables or local variables at a high level), as defined as global variables are defined in the FLASH const, defined as local variables will be optimized to register directly on the general-purpose registers , the optimal operation speed or storage by limit, to understand this part of the knowledge for the maintenance of the code makes sense. In addition, the embedded C language compiler will be extended memory management mechanisms, such as support scatter-loading mechanism and __attribute __ ((section ( "user-defined area"))), allows you to specify variables are stored in a special area such as (SDRAM, SQI FLASH), which greatly strengthened the management of memory in order to adapt to the complex application environments scenarios and requirements.

LD_ROM 0x00800000 0x10000 { ;load region size_region 
    EX_ROM 0x00800000 0x10000 { ;load address = execution address 
      *.o (RESET, +First) 
      *(InRoot$$Sections) 
      .ANY (+RO) 
    } 
  EX_RAM
0x20000000 0xC000 { ;rw Data
    .ANY (+RW +ZI)
  }
  EX_RAM1 0x2000C000 0x2000 {
    .ANY(MySection)
  }
  EX_RAM2 0x40000000 0x20000{
    .ANY(Sdram)
}
}

int a[10] __attribute__((section("Mysection")));
int b[100] __attribute__((section("Sdram")));

  In this way, we can assign a variable to the needs of the region, which in some cases is necessary, such as when doing GUI or web page because of lack of internal FLASH space, you can declare a variable to an outside area, additional memory some of the more important part of the data, in order to avoid being covered by other content, you may need a separate division SRAM area, to avoid inadvertent modification lead to fatal errors, these experiences are common and important in the actual product development, but because of space reasons here only to provide a brief example, if encountered in the work of this demand, it is recommended to understand in detail below. As for the use of the heap for embedded Linux, the standard C language to use and consistent attention to check the malloc, remember that after the release of blank, to avoid the "field guide", but for resource-constrained microcontroller, using malloc usually less scene, the scene if frequent application memory blocks, the memory management mechanism will build a static memory and memory-based block division, will be more efficient on the one hand (with a fixed-size blocks divided in advance, in use Find numbers directly deal), on the other hand for the use of controlled memory block can effectively avoid the problem of memory fragmentation, such as the common RTOS and network LWIP are using this mechanism, my personal habits this way, it is about details are not described in the stack, if want to know, can be described with reference to <C Primer Plus> regarding storage related.

4. pointers and arrays

  Arrays and pointers are often the main cause of a bug program, such as an array of cross-border, cross-border pointer, illegal address access, non-aligned access, behind these problems often have shadow pointers and arrays, so understand and master pointers and arrays, it is to become qualified the only way the C language developers.

  Arrays are formed of the same types of elements, when it declares it, the compiler allocates some space in the memory according to the characteristics of the internal element, additional C language also provides a multi-dimensional array, in order to cope with the needs of specific scenarios, and the pointer is to provide the use of symbolic methods address only makes sense to point to a specific address, the C language pointer for maximum flexibility, before being accessed, can point to any address, which greatly facilitate the operation of the hardware, but also for developers a higher demand.

int main(void) { 
    char cval[] = "hello"; 
    int i;
    int ival[] = {1, 2, 3, 4}; 
    int arr_val[][2] = {{1, 2}, {3, 4}}; 
    const char *pconst = "hello"; 
    char *p; 
    int *pi; 
    int *pa;
    int ** PAR; 

    P = CVAL; 
    P ++;             // addr increases. 1 
    PI = ival is; 
    PI + = . 1 ;           // addr increased. 4 
    PA = arr_val [ 0 ]; 
    PA + = . 1 ;           // addr increased. 4 
    PAR = arr_val; 
     PAR ++;          // addr increased. 8 
    for (I = 0 ; I < the sizeof (CVAL); I ++ ) 
    { 
        the printf ( " % D ", cval[i]); 
    } 
    printf("\n"); 
    printf("pconst:%s\n", pconst); 
    printf("addr:%d, %d\n", cval, p); 
    printf("addr:%d, %d\n", icval, pi);
    printf("addr:%d, %d\n", arr_val, pa); 
    printf("addr:%d, %d\n", arr_val, par); 
}

/ * End of the operation results in the 64-bit PC

0x68 0x65 0x6c 0x6c 0x6f 0x0

pconst:hello

addr:6421994, 6421995

addr:6421968, 6421972

addr:6421936, 6421940

addr:6421936, 6421944 */

  For an array, the general began to get the value from 0 to length-1 as the end, by [0, length) half-closed interval access, which is generally not a problem, but sometimes, we need to read backwards an array, it is possible to ength error as a starting point, thereby resulting in access violation, further definition of variables, C language type unsigned char accessible range from 0 to 255, sometimes in order to save space, the index variable is defined to be accessed as an unsigned char, if the array is large, will not lead to an array of more than cut-off, to fall into an infinite loop, when this was originally constructed in the code is very easy to avoid, but later if demand change, after increasing array, use these places have an array of risks, need special attention.

  In the previously mentioned, the address space occupied by the width of the pointer related to the chip, the platform 32 is 4 bytes, 8 bytes of 64 bits, and the length of addition and subtraction and the pointer type associated with it, such as char type 1, int type 4 , if you look closely above may be found in par value increased by 8, because the pointer to a pointer, a pointer to the corresponding variable, that is, the length is the length of the pointer type, in the 64-bit platform 8, if compared with the 32-bit platform 4 , this knowledge is not difficult to understand, but these features slightest mistake in engineering application, the issue will be planted imperceptible. Also supports additional pointer cast, which is quite useful in some cases, with reference to the following code:

#include <stdio.h>
 typedef struct
{ 
    int b;
    int a; 
}STRUCT_VAL;
static __align(4) char arr[8] = {0x12, 0x23, 0x34, 0x45, 0x56, 0x12, 0x24, 0x53};

int main(void) 
{ 
    STRUCT_VAL *pval; 
    int *ptr;
    pval = (STRUCT_VAL *)arr;
    ptr = (int *)&arr[4]; 
    printf("val:%d, %d", pval->a, pval->b); 
    printf("val:%d,", *ptr); 
} 

//0x45342312 0x53241256 
//0x53241256

  Based cast pointer in protocol analysis, data storage management in a quick and efficient solution to the problem of data analysis, but the data relating to the alignment in the process, the size of the terminal, is a common and easy error problem, as described above with arr character array by __align (4) is defined as a mandatory 4-byte alignment is necessary here to ensure that subsequent to int pointer accesses, will not trigger unaligned access exception, if not forcibly defined, char a default byte aligned of course this is not necessarily trigger an exception (arr by the decision to address the entire memory layout), which may lead to changes in other variables, it could trigger an exception, local variables out unusual and often add nothing to this increases the difficulty of solving the problem, it is more difficult to troubleshoot in large projects. Further, C language pointer to use is to have a special physical addresses to access particular by casting, achieved through the callback function pointer, as follows:

#include <stdio.h> 
typedef int (*pfunc)(int, int); 

int func_add(int a, int b)
{ 
    return a+b; 
} 

int main(void) 
{ 
    pfunc *func_ptr; 
    *(volatile uint32_t *)0x20001000 = 0x01a23131; 
    func_ptr = func_add; 
    printf("%d\n", func_ptr(1, 2)); 
}

  Here there is knowledge, volatile variable, variable, generally used for the following conditions:

  1) parallel device hardware registers (eg: Status Register)

  2) an interrupt service routine access to the non-automatic variable (Non-automatic variables)

  3) variable multi-threaded applications to be shared by multiple tasks

  When volatile resolve the user mode and abort access the same variable, there is no synchronization problem, and when accessing the hardware address, volatile also prevent access to optimize the address, so the actual address of the visit, which is embedded in the bottom layer important. Function pointer is not common in embedded software development in general, but for many important implementation such as asynchronous callback, the driver module, you can use the function pointer with a simple way to achieve many applications.

Guess you like

Origin www.cnblogs.com/zc110747/p/11450586.html