How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

Adapted from online information, Author: Xiao Ma Er

I can’t remember when I started frequently taking on the task of recruiting for companies, focusing on the recruitment of personnel for industrial embedded product development.

Out of a sense of responsibility towards both the company and newcomers, I tend to seek individuals with solid foundational knowledge and an interest in embedded programming, as this benefits both the company’s development and personal growth. Unfortunately, many times it feels like searching for a needle in a haystack, with hope leading to disappointment.

This has been a troubling issue for me, and it reminds me of a story I once heard: a foreigner came to a certain company in China for an inspection and first noticed the employees’ work attitudes, criticizing that such employees should all be laid off; then during lunch, after tasting the company’s meal, he began to criticize the company again.

Looking back at the current university education model, many schools still follow a rote learning approach similar to that of primary and secondary schools. Most students are just going through the motions, enjoying life, and even though freshmen may have some enthusiasm, they quickly get tainted by the environment. This leads to mutual blame among students, schools, and companies, creating a chaotic situation.

A good friend of mine teaches computer science at a 985 university in China. During a chat, he complained that many students take a long time to get hands-on after joining the workforce, requiring extensive training. However, his viewpoint is that this is to be expected; what can one learn in university that can be applied directly in the workplace? It’s frustrating…

Should it really be this way? I feel lost, but seeing the hardworking parents behind each student, witnessing their hopeful gazes towards their children, I believe we can do better and should do better.

To help everyone understand my inner turmoil, let me briefly discuss the issue of recruitment test questions. In the past, I participated in a recruitment process at ZTE, where the recruiter posed a question: int n = 3,4; I said I didn’t know, that it was a bizarre usage, and that such questions shouldn’t be asked. Perhaps I was too young and impetuous, which infuriated the recruiter, and of course, I didn’t get the job at ZTE. Learning from past experiences, I later established a basic principle in organizing recruitment: only knowledge that is frequently used in work should be included in the assessment scope.

To discover some good candidates, we have been thinking about what kind of questions can better assess the abilities and interests of job seekers. Initially, we would ask some basic concepts from foundational courses and specialized courses to filter out those with weak foundational knowledge; then we hoped candidates could explain a program they had written out of interest, and based on their descriptions, we would delve deeper to assess their specific abilities.

Unfortunately, 99% of people cannot articulate a program they wrote out of interest, and when encountering a few who fabricate stories, it becomes evident within a few sentences. Eventually, we had to shelve that question and lower the difficulty, presenting some programming examples involving single-layer loops with moderate judgment (such as counting the number of zeros in an integer, etc.). If we could find one or two students who could quickly write such programs during each recruitment, everyone would be excited for half a day.

This reflects the general ability of the students we can recruit. In my view, to reach this level of ability, one only needs to study programming casually for a few days. What’s the point of spending four years in university? It’s frustrating. But a more realistic problem is how to utilize such individuals once they are recruited into the company. We cannot directly hand over products to novice personnel for practice; everyone knows that products are meant to be sold to users, and initial minor issues can escalate into major problems when they reach the users.

Training in companies differs from education in schools; no one will spoon-feed you, and don’t expect someone to guide you hand-in-hand. Most of the time, it’s more like free-range training, with at most a few pointers before you are left to study on your own. This situation also leads to many people struggling to get started initially, with some even being forced to change careers, wasting years of their professional direction.

How to transition quickly? Combining my years of work and experience in training newcomers, I have summarized a relatively effective method that cleverly integrates the basic C language knowledge required for product development into three examples, allowing newcomers to learn, encounter obstacles, and think through these examples, continuously improving their C language skills and quickly acquiring the ability to participate in product development.

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

How to Learn C Language?

Many newcomers like to ask a similar question: “How to learn…?” Then some friends will copy a large amount of books and materials, and the more enthusiastic ones will even guide which book to read first. Unfortunately, many times it just ends there; a pile of materials remains quietly on the computer hard drive, only opened occasionally to glance at the table of contents. Having gone through some companies, the training system generally follows this pattern:

1. After joining, the mentor gives a pile of materials to read, and the newcomer reluctantly reads some; 2. When the mentor is not busy, they remember this newcomer and assign them a product to tinker with; 3. Unfortunately, specific products generally involve multiple disciplines, and facing a pile of questions, newcomers feel like they are walking on clouds, unable to proceed; 4. After a while, some people manage to overcome the despair cliff they faced upon joining, accumulating their own knowledge, and slowly begin to engage with the product, but due to a lack of documentation, they can only learn while adjusting; 5. Years later, newcomers become veterans, and a new product system is born; 6. Then the above dead cycle repeats.Over time, the company’s product system becomes very chaotic, technology is difficult to reuse, and effective accumulation is impossible. The kind of team building that is as easy as using one’s arms and legs becomes a mere talk. How can we break out of this dead cycle? Throughout my career, I have conducted extensive explorations and attempts, gaining some insights: not only do we need to advance the training of newcomers as much as possible and systematize it, but we also need to integrate our design and team philosophy as early as possible. Therefore, in the following three C language examples, everyone will experience that many things are not just about C syntax.

I mentioned earlier that in the company, it is impossible to repeat the rote learning education model of schools, nor will anyone guide you hand-in-hand. So in this case, how to organize training often becomes a choice dilemma with a certain artistic sense.

With the rise of mobile internet, online education has also become popular, and a large number of video tutorials have appeared online. Following the trend of the times, I tried to create books and videos for the content that newcomers need to learn, but the results were mediocre. Later, after carefully studying this field, I found that most videos merely transferred university classroom content to the internet, and many people only endure watching a few episodes before giving up, leading to poor results. I also have some thoughts and reflections on online education, which I will describe in a dedicated article later, so I won’t delve into it here. In short, the initial attempts were not very successful.

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

First Example in C Language: Unclear Requirements

The first example: “Write a console program that, given the heights of the inner and outer rhombuses, outputs a hollow rhombus.” Doesn’t it seem simple? As I write this, I hope that you, the reader, can pause for a moment, think it over, and then take action by writing a few lines of code to try it out before continuing to read; the rewards will be greater. Over the years, I have mentored many people, and most of them, upon seeing this question, immediately start writing the program, which leaves me a bit disappointed. Why?

Generally, software personnel recruited through layers of company screening can always manage to produce this example, but unfortunately, more than half of them cannot implement it accurately. Just the understanding of the height of a rhombus varies widely, leading to executions like these:

Or like this:

Or simply like this:

Why does this happen? This is precisely the first major pitfall of this example: unclear requirements. Encountering setbacks right from the start is akin to a heavy blow for many newcomers, but it is also the most memorable. In a team collaboration, there will be a lot of communication, and during the communication process, there will always be some ambiguities, and it is precisely these ambiguities that lead to unclear requirements, resulting in a lot of rework and even project failures. My first piece of advice to newcomers: express the requirements in your own words and confirm with the other party before implementation. This point is very important in future team collaborations, so I embedded it early in the onboarding training. After this ordeal, the first example is rephrased as follows: “Write a console program where the user inputs the heights of the inner and outer rhombuses, and outputs a hollow rhombus. The height of the rhombus is defined as the height of the upper triangle of the rhombus. For example, if the inputs are 5 and 3, the output should be: *

*** ** ** ** **** ** ** ** ** ** *** *

Let me add two points:

1. The definition of rhombus height may seem counterintuitive, but in the programming world, simplicity is logic. If defined as the entire height of the rhombus, not only would it require checking for positive integers, but also odd numbers, increasing the complexity of the program.

2. Describing with an example is much more effective than a pile of text; a picture is worth a thousand words. Once it is clear what needs to be done, many people go back, but often the execution results are as follows:

This introduces the second important concept: boundary checking. Given two numbers, how to concisely and completely determine whether they are reasonable inputs is a fundamental skill in programming. This content is generally not emphasized in university teaching, but to create a qualified embedded product, this must be given sufficient attention. How to perform elegant boundary checking is relatively simple in this example, so I’ll leave it to you, the reader, to think about it, and also reflect on my earlier definition of rhombus height. After being sent back a second time, many people may feel anxious; the rhombus hasn’t been output yet, and they are already being inundated with a pile of rules. In such cases, I explain the characteristics of embedded programs (which I will describe in detail later) and share my previous experiences. When I first started working, due to CPU speed limitations, the program was mainly in assembly language. My supervisor asked me to write a small piece of assembly code, but every time I submitted it, I was scolded and sent back for revisions until I could no longer spare a single assembly instruction. Looking back, for such a small example, I became familiar with most assembly instructions, finally understanding my supervisor’s good intentions. Nowadays, newcomers are a bit more sensitive and don’t dare to scold directly; they can only slowly explain the reasoning. After the second round of trials, most newcomers can adjust their attitudes slightly, and this time the examples they write are generally in line with the requirements. However, when they excitedly come to submit their work, I instead do not look at the program but start asking about the debugging techniques they used during the implementation process. This example poses a bit of difficulty for newcomers; it is unlikely to be written correctly in one go, and they will definitely go through some painful debugging processes. However, under the domestic university education system, basic debugging skills are not emphasized, and newcomers are expected to realize that debugging is a fundamental skill that requires sufficient attention and continuous strengthening. The road ahead is indeed as tough as iron, and now taking the first step on a small example is already a painful experience for many. To become a qualified embedded engineer, one needs methods, intelligence, and, more importantly, the sweat and persistence behind it. This series of articles will guide everyone through my growth journey, but it cannot replace your own efforts and sweat.

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

Second Example in C Language: Boundary Checking

In the previous text, I mentioned that this example introduces three important concepts, with the second concept being: boundary checking. Given two numbers, how to completely and concisely determine whether they are reasonable inputs is a fundamental skill in programming. Unfortunately, none of these examples have achieved this. Based on my years of experience in mentoring, let me elaborate here. Input two numbers, one is the height of the outer rhombus (denoted as m), and the other is the height of the inner rhombus (denoted as n), with the following criteria:

1. Both heights should be positive integers (the height of the inner rhombus can be 0); 2. Due to screen size limitations, the height of the rhombus should be limited; 3. The height of the outer rhombus should be greater than that of the inner rhombus.In the past, when mentoring, I liked to input values like (1000, 800) because these are often overlooked, and many people return frustrated. Most people lack the criteria, while some are more cautious and tend to write a long list of conditions, like this:

if (m > 0 && n > 0 && m > n && m < 30 && n < 30) { ……}

In summary, they fail to grasp the basic principle that the judgment conditions should be complete yet concise. In fact, this example is quite simple; with a little analysis, the judgment conditions can be reduced to three, as follows:

1. The height of the inner rhombus must be greater than or equal to 0; 2. The height of the outer rhombus must be less than a specified limit (assumed to be 30); 3. The height of the outer rhombus must be greater than that of the inner rhombus.

The program can be represented as follows:

if (n >= 0 && m > n && m < 30) { ……}

Do you remember in section (1) we agreed that the height of the rhombus is the height of the upper triangle? The benefit of this is the simplification of the judgment; concepts serve the goal. Otherwise, the judgment would require additional checks for odd numbers, which would complicate the program. Haha, you can reflect on this again. Finally, we are about to output the hollow rhombus, but for freshly graduated students, this example is still somewhat convoluted. What is their thought process like?

Most people will find that outputting a hollow rhombus is slightly more complex. So, if we modify it to output a rhombus, would it be much simpler? If that still seems complex, how about modifying it to output a triangle? Haha, in essence, it’s about simplifying complex problems by breaking them down into manageable parts.

The initial problem becomes much simpler: given the height of the triangle m, output the triangle. For example, if m=5, the output should be:

*

***

*****

*******

*********

To visualize, let’s represent spaces as follows:

—-*

—***

–*****

-*******

*********

At this point, the conclusion is very clear: the spaces output in each line decrease from m-1, while the stars output increase from 1, with the loop running m times. The program can be represented as follows:

for (i = 1; i &lt;= m; i++){  j = m - i;  while (j--)    printf(" ");

  j = i * 2 - 1;  while (j--)    printf("*");

  printf("\n");}

Now, considering the complexity of outputting a hollow triangle, it essentially adds another triangle inside. When outputting, we can simply deduct it. Assuming n=3, the output would look like this:

—-*

—***

–****

-**—**

**—–**

The program can be represented as follows:

for (i = 1; i &lt;= m; i++){  j = m - i;  while (j--)    printf(" ");  j = i * 2 - 1;  for (k = 0; k &lt; j; k++)  {    if (k &lt; m-n || k &gt;= j-m+n)      printf("*");    else      printf(" ");   }   printf("\n");}

Further complicating, to output a complete hollow rhombus, we only need to repeat the above program, simply reversing it and adjusting the height (subtracting 1), and I won’t display the example program here; those interested can try it themselves.

Some people, when implementing this program, start by analyzing the hollow rhombus directly. The most intuitive analysis strategy is to divide the rhombus into three sections: the upper triangle, the lower triangle, and the middle hollow part, as illustrated below:

*

***

** **

** **

** **

** **

** **

***

*

Following this thought process, the program can be represented as follows:

for(i=1;i&lt;2*m;i++){    if(i&lt;=m-n) {        star = 2*i-1;        empty= m-i;        while(empty--)            printf(" ");        while(star--)            printf("*");    }    else if(m-n&lt;i &amp;&amp;  i&lt; m+n &amp;&amp; j&lt; 2*n) {        if(j &lt;= n &amp;&amp; i &lt;= m){            num_empty = 2*j-1;            empty = m -i;        }        else{            num_empty = 2*(2*n-1-(j-1))-1;            empty = i-m;        }        num_star = star = m-n;

        while(empty--)            printf(" ");        while(star--)            printf("*");        while(num_empty--)            printf(" ");        while(num_star--)            printf("*");        j++;    } else {        star = 2*(2*m-1-(i-1))-1;        empty = (2*m-1-star)/2;        while(empty--)            printf(" ");        while(star--)            printf("*");    }    printf("\n");}

Regardless, this example is now complete, but have you noticed that none of the above programs can be considered elegant? The various expressions of m and n, after some time, look almost like a tangled mess. Imagine if this were a product program; how would future developers read and maintain it?

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

Third Example in C Language: The Simple and Brutal Beauty of Numbers

Beauty always captivates the heart. When we think of beauty, we often think of pleasing paintings, melodious music, and exquisite poetry… However, mathematics, the queen of natural sciences, contains more beauty than poetry and paintings. Elegant programs may well embody the beauty of mathematics behind them. In the previous section, we were always piecing together various expressions of m and n. Instead of struggling to find these, why not directly place the hollow rhombus on the coordinate axis? On the computer screen, people are accustomed to calling the right side the x-axis and the bottom the y-axis, drawing the hollow rhombus on the screen, as illustrated below:

Then, using analytical geometry knowledge, we can outline the hollow rhombus, represented in the program as follows:

for (x = 0; x &lt; m * 2 - 1; x++){    for (y = 0; y &lt; m * 2 - 1; y++)    {        if (abs(m - 1 - y) &lt;= m - 1 - abs(m - 1 - x) &amp;&&            abs(m - 1 - y) &gt; n - 1 - abs(m - 1 - x))            printf("*");        else            printf(" ");    }    printf("\n");}

We have centralized all the judgments, making it easy to understand what this large judgment statement is doing. Isn’t it much more elegant than previous implementations?

However, this criterion seems quite complex. Is there a better way? I suspect many friends have already thought of this when they saw the above illustration: that is, to move the coordinate axis to the center of the rhombus, as illustrated below:

The four edges of the outer rhombus can be described with expressions as follows:

(+x) + (+y) &lt; m(-x) + (+y) &lt; m(+x) + (-y) &lt; m(-x) + (-y) &lt; m

The merged expression is: abs(x)+abs(y)<m. The program can be represented as follows:

for (x = -m; x &lt;= m; x++){    for (y = -m; y &lt;= m; y++)    {        t = abs(x) + abs(y);        if (t &gt;= n &amp;&& t &lt;= m)            printf("*");        else            printf(" ");    }    printf("\n");}

What do you think of this code? I remember when I first discovered this implementation, I was struck by the simple beauty of mathematics. If you feel the same way, I firmly believe you can go far on the thorny path of programming…

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

Fifth Example in C Language: The Huge Differences Between Embedded C Language and Desktop C Language

During my university years, I majored in computer science, focusing my programming interests on various clever algorithms and implementations. After starting work, I began engaging in embedded product development, encountering numerous obstacles and hardships, only to realize that there are significant differences between embedded C language and desktop C language. To help newcomers quickly step into the embedded threshold, today is destined to be a critique session, as one must experience setbacks to grow. First, let’s take the most elegant implementation as an example, as shared in the last section:

for (x = -m; x &lt;= m; x++){    for (y = -m; y &lt;= m; y++)    {        if (abs(x) + abs(y) &gt;= n &amp;&& abs(x) + abs(y) &lt;= m)            printf("*");        else            printf(" ");    }    printf("\n");}

This implementation places the most complex conditional checks within two nested loops, resulting in very low program efficiency. In embedded programming, especially in some real-time modules, performance is often a tight string. Industrial embedded product development pursues teamwork, product maintainability, and a balance between performance and resources. Readability, maintainability, CPU computing power, memory, reliability, and other factors all become resources that require our careful consideration. Often, we like to refer to industrial embedded product programming as dancing on the tip of a needle. Besides this, the previous program has many shortcomings, such as:

1. The entire program often mixes various elements, increasing the complexity of reading the program for others; 2. Names like m and n are typical of school styles; 3. Various complex expressions involving m and n, after a while, become quite confusing; …Therefore, we need to continue on this path. For the convenience of subsequent program implementations, we will structure the entire program as follows:

int main(){    /* Input the heights of the inner and outer rhombuses, and perform legality checks */
    /* Loop to output the rhombus */    for (……)    {        /* Output leading spaces */
        /* Output left stars */
        /* Output middle spaces */
        /* Output right stars */
        /* Output newline */        printf("\n");    }    return 0;}

Upon seeing this program framework, it becomes clear that we require the output of the rhombus in a single-layer loop. By closely observing the structure of the hollow rhombus, each line can be abstracted into four parts: leading spaces, left stars, middle spaces, and right stars. We only need to find the functional relationships of these four values with the line number, and the entire program will be easily resolved, as illustrated below:

—-*—***–**-**-**—****—–**-**—**–**-**—***—-* My career mentor often discusses the concept of programming thinking with me. To this day, we still cannot provide an accurate definition, but through repeated iterative training, we have gradually come to understand which implementations can be considered as computer programming thinking. For example, in the computer world, we often refer to the mouse, keyboard, disk, and data on the disk as files, which can be simply traversed. Why do we need to abstract unrelated things into a unified concept? This cannot be explained in a single sentence, and I will guide everyone to gradually understand this in future discussions. However, this design philosophy is prevalent in product development. After inserting these two paragraphs of digression, let’s return to our rhombus output example. Clearly, the structure of each line is not the same (recall the second implementation version, which outputs them separately by type), but we insist on treating them uniformly. Perhaps I hope to plant the seeds of architectural design in the minds of newcomers from the very beginning, anticipating their later sprouting. Although this topic starts with newcomer training, it is also a reflection of my own growth journey. The difference is that I have grown through numerous falls and struggles. By integrating the stumbles I have experienced into the examples of C language training for newcomers, and even writing out my entire growth history, I hope others can use me as a mirror to grow more solidly and quickly.

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

Sixth Example in C Language: The Huge Differences Between Embedded C Language and Desktop C Language

In the previous section, we established the basic framework of the program and briefly introduced the concept of programming thinking. It seems that newcomers can quickly write programs that meet the requirements. Unfortunately, everyone’s growth journey is not smooth sailing. I remember when I first learned computer programming, which skill wasn’t developed through extensive imitation, gradually integrating into my knowledge system, growing bit by bit? Based on the overall framework requirements from the previous section, many newcomers I have mentored, including some fans who have emailed me, wrote programs that were essentially old wine in new bottles, merely rehashing previous examples. Let me illustrate this for you, and you can reflect on whether you also have the impulse to implement it this way.

/* Loop to output the rhombus */for (……){    /* Output leading spaces */    if (upper rhombus)       output in triangular form;    else       output in inverted triangular form;
    /* Output left stars */    if (upper triangle)       ...    else if (center hollow)       ...    else       ...
    /* Output middle spaces */    if (upper hollow)       output in triangular form;    else if (lower hollow)       output in inverted triangular form;
    /* Output right stars */    if (upper triangle)       ...    else if (center hollow)       ...    else       ...
    /* Output newline */    printf("\n"); }

Note: This example combines the second implementation version, dividing a rhombus into upper triangle, lower triangle, and middle hollow parts for separate output. For details, please refer to “Onboarding C Language Example One (2).” How should this be implemented? In the previous section, we mentioned that the focus is on finding four functional relationships. The expressiveness of text is often limited, so let me show you a casual hand-drawn diagram I used when guiding newcomers; perhaps it will help you understand immediately.

Understanding the above diagram, let me emphasize a few points:

1. Each line of the hollow rhombus must be treated equally; forget about the upper triangle, lower triangle, and middle hollow divisions; 2. No if statements are allowed within the four functional relationships, but common functions like abs are permitted; 3. There are no shortcuts to success, and there are no shortcuts to learning skills; put aside your restless heart. By this point, many people have written it seven or eight times, thanks to the current university education model, many people are experiencing restlessness again. A timely reassurance is still necessary; a frequently joked line is to look at your senior xxx, don’t be fooled by their current ease in handling several products; they also wrote it over ten times back then, haha. In fact, many people realize the purpose of my struggles. University learning is often superficial, and many things are pursued hastily, but bringing this mindset into the workplace and product development can be fatal. I merely want to use this form to help newcomers avoid detours. My good intentions often lead to numerous misunderstandings; introverts may silently curse, while the more outspoken ones directly question the significance of my struggles. Haha, I’ve rambled on about the ups and downs of life. After this guidance, most people can smoothly complete this example according to the requirements, even though each part is a complex expression. At this point, I will explain a crucial computational concept: iteration. The embedded products we engage in are strong real-time industrial products, often involving complex calculations like Fourier transforms, and they must be completed within specified time limits. Therefore, the requirements for computational efficiency are quite high, and the most common strategy is to use iteration, retaining valuable intermediate calculation results to reduce overall computational load. Through iteration, not only do the expressions of each part become less complex, but it also reduces the computational load. This example is relatively simple and doesn’t involve much technical content, and most people quickly complete the iterative adjustments. However, the contrast before and after is quite impressive, which can be considered an additional gain. Thus, all technical points of the hollow rhombus program have been described. Following these requirements, most people can write programs that meet the standards. I highly recommend that you, the reader, try writing and adjusting it yourself. In the next section, I will post the standard answer, and you can compare it; the differences will be the starting point for our subsequent content.

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

Seventh Example in C Language: Continuously Perfecting the Answers in Your Heart

In the previous sections, I listed a very elegant implementation, and some friends reminded me that the program output was incorrect. I tested it myself, and indeed it was. When I wrote the program, I did so directly in document form, and some examples were copied from the programs written by the young people, focusing on the intent rather than the actual code, which lacked rigor. The optimized version is as follows:

for (x = -m+1; x &lt; m; x++){    for (y = -m+1; y &lt; m; y++)    {        t = abs(x) + abs(y);        if (t &gt;= n &amp;&& t &lt; m)            printf("*");        else            printf(" ");    }    printf("\n");}

Let’s return to the previous section. After some bumps, newcomers finally wrote programs that met the technical requirements, and everyone was happy, thinking they were done. Unfortunately, the road ahead is still long. At this point, I will share the standard answer for this topic, allowing everyone to compare it with their own programs. Previously, the programs were provided in fragments, while the standard answer is presented in a complete format, as shown below:

/********************************************************************* *   Copyright (C), 1999-2004, xxxxxx. Co., Ltd.* *   File Name: diamond.c*   Software Module: Hollow Rhombus Output*   Version Number: 1.0*   Generation Date: 2003/3/23*   Author: Xiao Ma Er*   Function: Hollow rhombus output, this program occupies little memory, but calculations are slightly larger, can be modified to "memory for resources" algorithm* *********************************************************************/
#include &lt;stdio.h&gt;
/* Maximum height of the rhombus */#define MAX_DIAMOND_HEIGHT 16
/* Pre-declaration */int myGreater(int n);
/* Main program */int main(){    int n, nRow;    int nIn, nOut;    int nCount1, nCount2, nCount3;
    /* Input the heights of the inner and outer rhombuses, and perform legality checks */    for (;;)    {        printf("Input the heights of the inner and outer rhombuses (maximum %d rows): outer rhombus height, inner rhombus height:\n", MAX_DIAMOND_HEIGHT - 1);        scanf("%d,%d", &amp;nOut, &amp;nIn);        if (nIn &lt; nOut &&& nOut &lt; MAX_DIAMOND_HEIGHT &&& nIn &gt;= 0)            break;        printf("Input is invalid, please re-enter:\n\n");    }
    /* Loop to output the rhombus */    nIn = nOut - nIn;        /* Adjust to the difference between the inner and outer rhombuses */    for (nRow = -nOut+1; nRow &lt; nOut; nRow++)    {        // Line number        printf("%-010d", nRow);
        /* Output leading spaces */        nCount1 = nRow &gt;= 0 ? nRow : -nRow;        /* Take absolute value */        for (n = 0; n &lt; nCount1; n++)            printf(" ");
        /* Output left stars */        nCount1 = nOut - nCount1;            /* Outer triangle part, used for subsequent iterations */        nCount2 = myGreater(nCount1 - nIn);  /* Inner triangle part, used for subsequent iterations */        nCount3 = nCount1 - nCount2;         /* The difference between inner and outer is what needs to be output */        for (n = 0; n &lt; nCount3; n++)            printf("*");
        /* Output middle spaces */        nCount3 = 2 * nCount2 - 1;           /* Expand from triangle to rhombus */        for (n = 0; n &lt; nCount3; n++)            printf(" ");
        /* Output right stars */        nCount1--;                           /* Outer triangle part */        nCount2 = myGreater(nCount1 - nIn);  /* Inner triangle part */        nCount3 = nCount1 - nCount2;         /* The difference between inner and outer is what needs to be output */        for (n = 0; n &lt; nCount3; n++)            printf("*");
        /* Output newline */        printf("\n");    }    return 0;}
/* Return the greater of zero */int myGreater(int n){    if (n &lt; 0)        return 0;    return n;}

What do you think of this standard answer? Can you identify the differences between it and your program? In the next section, we will use this program as a starting point to introduce some characteristics of real product code.

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

Let’s Set Aside Grand Theories and Talk About Embedded Code

In the previous section, we provided the standard answer written in the style of real product code, hoping that our partners can compare it with their implementations. The post-90s generation is a unique one, and many people are very resistant to direct preaching of grand theories. Therefore, I hope our partners can find answers themselves, and through the collision of ideas, grow better. In this article, let’s identify the valuable aspects of the standard answer together. 1. Meaningful Variable Naming When university teachers teach programming, they focus primarily on syntax, showcasing all the syntax to everyone (I strongly disagree with this learning method, and will later present my own approach, known as the “Tree Method” in the project team). Therefore, they often use short programs to demonstrate syntax, but because the programs are short, variable naming tends to be casual, leading to frequent use of names like i, j, k, m, n, which inadvertently carry over into products. However, in product development, even relatively simple devices involve a considerable amount of code, making meaningful names crucial for ease of reading and maintenance. In the standard example, the use of nIn and nOut aims to represent the heights of the inner and outer rhombuses with simple English words. Some friends may have read books like “The Art of Readable Code” or “Clean Code,” where the authors emphasize using precise English words to convey specific meanings. However, as Chinese people, it is already quite challenging to think of simple vocabulary, let alone express accurately. Therefore, after countless iterations and explorations, our project team has formed a habit of variable definition: to use simple, similar English words as much as possible, with global variables requiring accurate Chinese comments. For some automatic variables within functions, due to their limited scope, comments can sometimes be omitted. There are many stories about variable naming, and this is just a starting point; we will have a dedicated series of articles on this topic. Remember, in real product code, meaningful variable naming is essential; forget about m and n. 2. Code Sections What is a section? I first learned this concept during the global millennium transition when I was working part-time at a company in Beijing. The company was undertaking a project for a Japanese bank, and the Japanese side had strict requirements for code quality, sending an expert to explain various requirements and the reasoning behind them. At that time, I was still young and arrogant, so most of the expert’s advice went in one ear and out the other. However, I distinctly remember the concept of sections (perhaps it was introduced first, haha; I lost patience for the rest). When we read code, our minds can only focus on a narrow range of points for a limited time. If faced with endless code, we subconsciously perceive it as a long, tedious mess—unpleasant and counterproductive. Therefore, we need to logically divide the code into sections, using spaces as separators, and adding appropriate comments before each section to explain its functionality, which is the concept of sections. After such modifications, reading the code feels completely different. The value of sections goes far beyond this, and I will gradually share more in the series of articles on programming standards. For now, we just need to understand that piling up long, tedious code is incorrect. In fact, in section 5, when I established the overall structure of the program, I already had this intention; everyone can recall and appreciate it.

/* Loop to output the rhombus */for (……){    /* Output leading spaces */
    /* Output left stars */
    /* Output middle spaces */
    /* Output right stars */
    /* Output newline */ }

3. Detailed AnnotationsSome code contains a certain depth, and over time, it can be forgotten. Adding simple annotations on the right side not only facilitates subsequent reading and understanding of the code but also highlights key code segments, making code reviews easier. In the example code, you will see simple annotations on the right side of the iterative expressions (due to formatting issues, they often end up on the next line), which serve this purpose. However, too much of a good thing can be counterproductive. Many newcomers tend to add excessive annotations on the right side, so it requires long-term practice and gradual understanding to identify which annotations are valuable. Perhaps one day, when you review your code, you will delete many unnecessary comments, indicating that you have mastered this skill. 4. Resources In embedded systems, resources are a constant concern. What do we mean by resources? In our concept, not only the size of memory and flash space are resources, but also CPU computing power, code readability (maintainability), implementation complexity (manpower costs), reusability, and many other aspects are considered resources. Good steel should be used on the cutting edge, but first, we must understand where the cutting edge is. Without clear boundaries, discussing improving resource utilization is meaningless. For example, if readability is the top priority and memory and CPU resources are relatively abundant, then the most elegant implementation is the best. If CPU computing power is tight, the standard implementation from the previous section is better. If memory is slightly abundant, we can enhance code readability with better methods. Some people have already mentioned this method to me; I wonder if everyone has felt it. By now, simply outputting a rhombus is so easy, but insisting on creating a hollow rhombus complicates the program unnecessarily. However, if we use an array to represent the entire rhombus output, first output a rhombus with stars, then output another rhombus with spaces, and finally output the entire array, wouldn’t that be very simple? The readability of the code would instantly skyrocket, and execution efficiency would also improve, occupying only a bit more memory.

How to Dance on the Tip of Embedded C Programming? 7 Examples from a 15-Year Veteran

Summary

After a marathon journey, we finally reach the last chapter. Let’s summarize the knowledge points mentioned in the first hollow rhombus output example:

1. Clear understanding of requirements; the most commonly used strategy is to express the requirements in your own words and confirm with the other party before implementation. 2. Boundary checking; make newcomers aware that in embedded products, the robustness of the final product often manifests in these simple criteria. 3. Training in basic debugging skills; one must sharpen their tools before they can do good work, no need to elaborate. 4. Introduction of programming thinking; one must gradually appreciate the value of abstraction, which is a challenge, but to go far, one must endure it. 5. In numerical calculations, a very important concept: clever use of iteration. 6. Introduction of basic programming standards; appreciate the concept of sections and recognize the importance of code readability and maintainability in product code. 7. In the field of embedded programming, resources are limited, and we must learn to dance on the tip of a needle. 8. Keep work notes; be good at summarizing and develop the habit of reflecting on the steps of growth.

Remember, when I first started engaging in embedded programming, my career mentor showed me his notebook filled with various debugging records, insights, technical materials, and knowledge summaries. I finally understood why he received recognition from everyone in the company. Therefore, our team has formed an unwritten rule: everyone must keep work notes, regardless of the method or format, as long as they start recording.

Leave a Comment