C language skills in microcontroller development (Part 1)

Publisher:CrystalSparkleLatest update time:2023-01-09 Source: zhihu Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

2. for(pRAMaddr=StartAddr;pRAMaddr

3. {

4. *pRAMaddr=0x00000000; //Clear the specified RAM address

5. }

Through analysis, we found that since pRAMaddr is an unsigned int pointer variable, the pRAMaddr+=4 code actually offsets pRAMaddr by 4*sizeof(int)=16 bytes, so every time a for loop is executed, the variable pRAMaddr will be The offset is 16 bytes of space, but only 4 bytes of space are initialized to zero. The content of the other 12 bytes of data will be random numbers in most processor architectures.

2.1.6 Keyword sizeof

I don’t know how many people initially thought sizeof was a function. In fact, it is a keyword whose function is to return the number of memory bytes occupied by an object or type. For most compilers, the return value is unsigned integer data. It should be noted that when using sizeof to obtain the length of an array, do not apply the sizeof operator to the pointer , such as the following example:

1. void ClearRAM(char array[])

2. {

3. int i;

4. for(i=0;i

5. {

6. array[i]=0x00;

7. }

8. }

9.

10. int main(void)

11. {

12. char Fle[20];

13.

14. ClearRAM(Fle); //Only the first four elements in the array Fle can be cleared

15. }

We know that for an array array[20], we use the code sizeof(array)/sizeof(array[0]) to get the elements of the array (here 20), but array names and pointers are often easily confused, there are and only In one case, the array name can be used as a pointer, that is, when the array name is used as a function parameter, the array name is considered a pointer, and at the same time, it can no longer serve as an array name.

**Note that only in this case can the array name be used as a pointer, but unfortunately this situation can easily lead to risks. In the ClearRAM function, array[] as a formal parameter is no longer an array name, but a pointer. sizeof(array) is equivalent to finding the number of bytes occupied by the pointer variable. Under a 32-bit system, the value is 4, and the operation result of sizeof(array)/sizeof(array[0]) is also 4. Therefore, calling ClearRAM(Fle) in the main function can only clear the first four elements in the array Fle.

2.1.7 Increment operator '++' and decrement operator '--'

The increment operator "++" and the decrement operator "--" can be used as prefixes or suffixes. **The difference between prefix and suffix is ​​that the time when the action of increasing or decreasing the value occurs is different. **As a prefix, it first adds or decrements itself and then performs other operations. When used as a suffix, it performs operations first and then adds or decrements itself. Many programmers don't know enough about this, which can easily lead to hidden dangers. The following example can well explain the difference between prefixes and suffixes.

1. int a=8,b=2,y;

2. y=a+++--b;

After the code is executed, what is the value of y?

This example is not a C puzzle specially designed to make you rack your brains (if you feel that you are very confident in mastering the details of C, it is a good choice to do some C puzzles to test it. Then, "The C Puzzle Book" This book is a must-read), and you can even use this obscure statement as an example of unfriendly code. But it also allows you to better understand C language. According to the operator precedence and the compiler's greedy method of identifying characters, the second sentence of code can be written in a more explicit form:

  1. y=(a++)+(--b);

When assigning a value to variable y, the value of a is 8 and the value of b is 1, so the value of variable y is 9; after the assignment is completed, variable a is incremented and the value of a becomes 9. Do not think that the value of y is is 10. This assignment statement is equivalent to the following two statements:

1. y=a+(--b);

2. a=a+1;

2.1.8 Traps of logical AND '&&' and logical OR '||'

In order to improve the efficiency of the system, the logical AND and logical OR operations are specified as follows: **If the final result can be inferred after evaluating the first operand, the second operand will not be evaluated! **For example, the following code:

1. if((i>=0)&&(i++ <=max))

2. {

3. //Other codes

4. }

In this code, i++ will be executed only when i>=0. In this way, it is not clear enough whether i is incremented, which may cause hidden dangers. Logic or similar.

2.1.9 Filling of structures

Structures may generate padding because, for most processors, it is faster to access word- or half-word-aligned data. When defining structures, the compiler may align them as half-word or word-aligned for performance optimization. , which will cause filling problems. For example, the following two structures:

The first structure:

1. struct {

2. char c;

3. shorts;

4. int x;

5. }str_test1;

The second structure:

1. struct {

2. char c;

3. int x;

4. shorts;

5. }str_test2;

The elements of these two structures are the same variables, but the positions of the elements have been changed. So do the memory sizes occupied by these two structure variables are the same?

In fact, the memory occupied by these two structure variables is different. For the Keil MDK compiler, the first structure variable occupies 8 bytes by default, and the second structure variable occupies 12 bytes, which is a big difference. The storage format of the first structure variable in memory is as shown below:



The storage format of the second structure variable in memory is shown in the figure below. Comparing the two figures, we can see how the MDK compiler aligns the data. The filling content is the data in the previous memory, which is random, so it cannot be compared byte by byte between structures; in addition, reasonable arrangement The location of elements within the cloth structure body can minimize padding and save RAM.



2.2 Priorities that cannot be underestimated

C language has 32 keywords but 34 operators. It's difficult to remember the precedence of all operators. If you don't pay attention, the logic of your code will be very different from the actual execution.

For example, the following code converts BCD code to hexadecimal number:

  1. result=(uTimeValue>>4)*10+uTimeValue&0x0F;

The BCD code stored in uTimeValue here needs to be converted into hexadecimal data. In actual operation, it is found that if the value of uTimeValue is 0x23, according to the logic I set, the value of result should be 0x17, but the operation result is 0x07. After various investigations, it was discovered that the priority of '+' is greater than '&', which is equivalent to the bitwise AND of (uTimeValue>>4)*10+uTimeValue and 0x0F. The result is naturally inconsistent with logic. The logical code should be:

1. result=(uTimeValue>>4)*10+(uTimeValue&0x0F);

Unreasonable #define will aggravate the priority problem and make the problem more hidden.

1. #define READSDA IO0PIN&(1<<11) //Read the port status of IO port p0.11

2.

3. if(READSDA==(1<<11)) //Determine whether port p0.11 is high level

4. {

5. //Other codes

6. }

The compiler brings in the macro after compilation, and the original code statement becomes:

1. if(IO0PIN&(1<<11) ==(1<<11))

2. {

3. //Other codes

4. }

The priority of operator '==' is greater than '&'. The code IO0PIN&(1<<11) ==(1<<11)) is equivalent to IO0PIN&0x00000001: Determine whether port P0.0 is high level. This Far from the original intention. Therefore, when using macro definitions, it is best to enclose the defined content in parentheses.

There are many other operators that may cause misunderstanding when used in the conventional way, as shown in the following table. Of course, the operators of C language do not stop at a large number!



There is a simple way to avoid priority problems: add "()" if the priority is unclear, but this will cause at least two problems:

  • Too many parentheses affect the readability of the code, both for yourself and future maintainers.

  • Other people's code doesn't necessarily use parentheses to solve priority issues, but you always have to read other people's code.

In any case, in terms of embedded programming, the basic knowledge that should be mastered cannot be cheated. It is recommended to spend some time to sort out the precedence order and error-prone precedence operators several times.

2.3 Implicit conversion

The design philosophy of the C language has been criticized because it assumes that C programmers know exactly what they are doing. One of the evidences of this is implicit conversion. C language stipulates that different types of data (such as char and int data) need to be converted into the same type before calculation can be performed.

**If you mix types, such as using char type data and int type data for subtraction, C uses a set of rules to automatically (implicitly) complete the type conversion. This can be convenient, but it can also be dangerous.

This requires us to understand this conversion rule and be able to apply it to the program!

1. When appearing in an expression, signed and unsigned char and short types will be automatically converted to int type, and if necessary, will be automatically converted to unsigned int (when short and int have the same size Hour). This is called type promotion.

Promotion usually does no big harm in arithmetic operations, but if the bitwise operators ~ and << are applied to operands whose basic types are unsigned char or unsigned short, the result should be immediately cast to unsigned char or unsigned short ( Depends on the type used during operation).

1. uint8_t port =0x5aU;

2. uint8_t result_8;

3. result_8= (~port) >> 4;

If we don't understand the type promotion in the expression, we think that the variable port is always of type unsigned char during the operation. Let's take a look at the operation process: the result of ~port is 0xa5, and the result of 0xa5>>4 is 0x0a, which is the value we expect.

But in fact, the result of result_8 is 0xfa! Under the ARM structure, the int type is 32 bits. The variable port is promoted to int type before operation: ~port result is 0xffffffa5, 0xa5>>4 result is 0x0ffffffa, assigned to variable result_8, type truncation occurs (this is also implicit!), result_8=0xfa. After such a weird implicit conversion, the result is very different from what we expected! The correct expression statement should be:

[1] [2] [3] [4] [5] [6] [7]
Reference address:C language skills in microcontroller development (Part 1)

Previous article:C language skills in microcontroller development (Part 2)
Next article:Microcontroller IO port design skills

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号