[TOC]

One, foreword

We often need to check the security of the code when we strip the code, for example:

  1. Is the pointer null?
  2. Is the dividend equal to 0?
  3. Is the return result of the function call valid?
  4. Was opening a file successful?

Both if and Assert assertions can be used to check such boundary conditions. Does that mean that you can use either, whichever comes to mind?

Should you use “if” or “assert” assertions in different situations?

While writing this article, I remembered kong Yiji’s question: how many ways are there to write the character “Hui” in anise beans?

It seems that there is no need to agonize over which one to choose, since both can implement the desired features. That’s what I used to think, but now I don’t.

Being a technology leader and getting a better offer can be the difference between winning and losing.

2. Assert

I asked a nearby embedded developer who has been working for over 5 years: How do you choose between if and Assert? He said: What does assert do? !

It seems worth mentioning a few assert assertions first.

The prototype for Assert () is:

void assert(int expression);

  1. If the macro evaluates to a non-zero value, no action is taken.
  2. If the macro’s argument is zero, the diagnostic message is printed and abort() is called.

For example:

#include <assert.h> int my_div(int a, int b) { assert(0 ! = b); return a / b; }Copy the code
  1. When b is not 0, assert asserts that the program does nothing and executes down;
  2. When b is 0, the Assert assertion prints an error message and terminates the program;

Functionally, assert(0! = b); Equivalent to the following code:

if (0 == b) { fprintf(stderr, "b is zero..." ); abort(); }Copy the code

Assert is a macro, not a function

In the assert.h header, there is the following definition:

#ifdef NDEBUG
    #define assert(condition) ((void)0)
#else
    #define assert(condition) /*implementation defined*/
#endif
Copy the code

Since it is a macro definition, it indicates that the macro replacement is performed at the time of preprocessing. For more on macros, see this article: How to Improve Your Code: Macro Definitions – From getting started to Giving up.

As can be seen from the above definition:

  1. If the macro NDEBUG is defined, the assert() macro will do nothing, which is equivalent to an empty statement:(void)0;This macro is defined in the Makefile whenever code is compiled during the Release phase.
  2. If the macro NDEBUG is not defined, then the assert() macro will replace some of the checking code, which is usually masked when we compile in Debug mode during development.

3, If VS Assert

Instead of describing the problem as a code snippet, it’s easier to understand it as a scenario.

Char *my_concat(char *str1, char *str2) {int len1 = strlen(str1); int len2 = strlen(str2); int len3 = len1 + len2; char *new_str = (char *)malloc(len3 + 1); memset(new_str, 0 len3 + 1); sprintf(new_str, "%s%s", str1, str2); return new_str; }Copy the code

If a developer wrote the above code, he would be interviewed by his boss! It has the following problems:

  1. No validation check for input parameters;
  2. Malloc results were not checked;
  3. Sprintf is very inefficient;
  4. .

1. Use the if statement to check

char *my_concat(char *str1, char *str2) { if (! str1 || ! Str2) // Error return NULL; int len1 = strlen(str1); int len2 = strlen(str2); int len3 = len1 + len2; char *new_str = (char *)malloc(len3 + 1); if (! New_str) return NULL; memset(new_str, 0 len3 + 1); sprintf(new_str, "%s%s", str1, str2); return new_str; }Copy the code

2. Check with assert assertions

Char *my_concat(char *str1, char *str2) { = str1); assert(NULL ! = str2); int len1 = strlen(str1); int len2 = strlen(str2); int len3 = len1 + len2; char *new_str = (char *)malloc(len3 + 1); Assert (NULL! = new_str); memset(new_str, 0 len3 + 1); sprintf(new_str, "%s%s", str1, str2); return new_str; }Copy the code

3. Which one do you like?

First of all, both of these checks are common in real code and don’t seem to make any difference functionally. Therefore, there is no strict right or wrong, much depends on the preferences of each person.

(1) Assert

I, as the implementer of my_concat(), want to concatenate strings, so the arguments passed must be valid, and the caller is responsible for that. I would be very surprised if the parameter passed was invalid! What to do: Crash for you!

(2) If supporters

The my_concat() function I wrote was so robust that I expected the caller to mess around and deliberately pass in invalid arguments to test my coding skills. It’s okay. Come on. I can handle anything!

Both factions seem to have good reasons! So how do you choose? Do you really follow your feelings?

Suppose we develop a project strictly following the usual process:

  1. During development, if the macro NDEBUG is not defined in the build options, assert comes into play;
  2. When the project is released and NDEBUG is defined in the build option, assert is equivalent to an empty statement;

That is, it is only during the Debug development phase that assert assertions are used to correctly check for invalid parameters. In the Release phase, assert doesn’t work, and if the caller passes an invalid argument, the program will crash.

What does that mean? Is there a bug in the code? Or is the code not robust enough?

From my personal understanding, this is a case where the unit test was not written well and the parameters were not invalid.

4. The Nature of Assert

Assert is just a way to verify validity, and its best purpose is to make our program crash as fast as possible during development. Every crash means there are bugs in the code that need to be corrected.

When we write an assert assertion, it tells us that it should not be allowed to fail. The assertion must succeed before the program can proceed.

The nature of if-else

If-else statements are used for logical processing, which is designed to handle a variety of possible situations. That is, every branch is reasonable and permissible, and we have to deal with those branches.

6. My favorite version

Char *my_concat(char *str1, char *str2) { = str1); assert(NULL ! = str2); int len1 = strlen(str1); int len2 = strlen(str2); int len3 = len1 + len2; char *new_str = (char *)malloc(len3 + 1); // It is possible and permissible to fail to apply for heap space. if (! new_str) return NULL; memset(new_str, 0 len3 + 1); sprintf(new_str, "%s%s", str1, str2); return new_str; }Copy the code

For parameters: I believe that the parameters passed in must be valid. If invalid parameters appear, there is a bug in the code that cannot be allowed and must be fixed.

For resource allocation outcomes (malloc functions) : I consider resource allocation failures to be reasonable, possible, and allowed, and I have addressed the situation.

Of course, this doesn’t mean that you should use assert to check parameters. It depends on scenarios and semantics. Take this example:

int g_state; void get_error_str(bool flag) { if (TRUE == flag) { g_state = 1; assert(1 == g_state); } else {g_state = 0; assert(0 == g_state); // Make sure the assignment succeeds}}Copy the code

The flag argument represents different branching cases, and once you assign g_state, you must ensure that the assignment is correct, so use assert assertions.

Five, the summary

This article analyzes one of the more obscure and vague concepts in C, which seems a bit ethereal, but does require us to pause and think about it.

If there’s a situation that I can’t get right, I ask myself the question:

Is this allowed to happen?

Do not allow: Use assert assertions and try to find all error conditions during development;

Allow: use if-else to indicate that this is a reasonable logic and needs to be processed in the next step.











The summary article concluded by Doug is written with great care, which is very helpful for my technical improvement. Good things, to share!



C language pointer – from the underlying principles to the tricks, with text and code to help you explain thorough step by step analysis – how to use C to achieve object-oriented programming I like the communication between the process – message bus Internet of things gateway development: based on MQTT message bus design process (on) improve the code force lattice sharp tools: Macro definition – from the beginning to abandon the original GDB low-level debugging principle so simple use of C language setjMP and LongjMP, to achieve exception capture and coroutine about encryption, certificates of those things in-depth LUA scripting language, let you thoroughly understand the principle of debugging