Main / C
"Any fool can write code a computer can understand. Good programmers write code humans can understand." -Martin Fowler "I'm convinced that you could design a language about a tenth of the size of C++ (whichever way you measure size) providing roughly what C++ does." -Bjarne Stroustrup "The most important single aspect of software development is to be clear about what you are trying to build." -Bjarne Stroustrup "I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. Finding the smallest program that demonstrates the error is a powerful debugging tool." -Bjarne Stroustrup "The problem with object-oriented languages is they've got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana and the entire jungle." -Joe Armstrong "Instead of embedding comments into code I embed code into comments." -Timm Knape "Though lots of very clear code exists, truly self-documenting software is about as common as unicorns." -Jack Ganssle "Prefer clear code over optimal code." -Herb Sutter "C++ seems to be loaded with every artifact the committee could invent; only the bravest venture into its deepest depths." -Jack Ganssle "C++ - An object oriented programming language which has an on-going committee whose goal is to add so many obfuscating features that no single individual really understands all of its nuances." - Jack Ganssle Preferred C Style Guide Code Academy The 80-20 Rule80% of resources used by 20% of code | 80% of run time spent in 20% of code | 80% of disk accesses for 20% of code | 80% of maintenance on 20% of code | 80% of project effort on last 20% of functionality Operation Pairs for Function/Variable Naming
CThe % operator should be thought of more as a 'remainder' evaluation. The sign of the result will match the dividend (for a%b matches a). #define MathIf doing math on #define constants inside other dependent #defines, use parentheses as they may be reused in other places. #define FOO 1 #define BAR 2 #define FOOBAR (FOO+BAR) //not #define FOOBAR FOO+BAR Language Quirkshttps://gist.github.clom/zneak/5ccbe684e6e56a7df8815c3486568f01 C Program MemoryUninitialized global and static variables go in to the .bss section. Initialized globals and statics go in .data section. const storage may go in .rodata (.rdata) or in .text. Command line arguments and environment variables go at the top of program memory. Multi-dimensional array initSome compilers may give you a warning if you don't have an extra set of braces char params[2][2] = {{0}}; Since C is row-major implemented, compilers will not allow this: //no char params[][] = {{0,0},{0,0}}; //no char params[2][] = {{0,0},{0,0}}; //yes, because it must know the width of the columns char params[][2] = {{0,0},{0,0}}; Uses of volatile
Printing a variable nameUse the stringizing operator, # as in int var; printf("%s", #var); sprintf warningNote that sprintf always add a null-termination character, so if you try to write 8 chars to an 8-width char array you have a problem. What is libc.a?Archive file of standard C library (glibc). Lives in /usr/libc documentation: http://www-01.ibm.com/support/knowledgecenter/ssw_aix_53/com.ibm.aix.basetechref/doc/basetrf2/write.htm%23ci2270gaco Bool in C?The _Bool type is a standard C type available when using the C99 or later C standard. Use the <stdbool.h> header file when using this type, which allows you to use macros like true and false for the values held by these objects. AlignmentTo align something on a page boundary, use the __attribute__ ((align (boundary))) tag in the declaration of the object. Great stdio Documentationhttp://tigcc.ticalc.org/doc/stdio.html Pointer TipsA void pointer has the same representation and alignment requirements as the character pointer type. int *comp(void*) indicates a function that returns an integer pointer (somewhere the function comp is declared), whereas int (*comp)(void*) indicates that comp is a pointer to a function that returns an int (no declaration of comp exists - this is it). As another example void *(*start_routine) (void *) is a pointer to a function that takes a void pointer arg and returns a void pointer. Trying to cast a void* as a function pointer is problematic, due to the platform-specific potential size differences. Visual Studio compiler is particularly picky about this, more so than GCC it would seem. This is an option that works: //voidptrref is a void*, which holds the address of a void function(void) void (*funcfoo)(void) = (void(*)(void)) voidptrref; funcfoo(); Stringschar* s; s = "string"; You can do this, but you are setting s to point to what should be considered a read-only location. Writing to it results in undefined behavior. This is why you get compiler warnings when you don't use const for s. The string literal is used to create a little block of characters somewhere in memory which the pointer s is initialized to point to. We may reassign s to point somewhere else, but as long as it points to the string literal, we can't modify the characters it points to. It's also better to prefer const char s[] = "foo"; to const char* s = "foo"; because there is a slight difference and some compilers (like the PIC xc16) cannot handle the latter. strlen vs. sizeof => strlen returns the string length in characters, excluding the null termination, whil sizeof includes the null termination. EnumSome people use the term definite value instead of enumerated. The opposite could be an uninitialized variable. Header File TipsDon't define variables in headers. Put declarations in headers and definitions in one of the .c files. Otherwise you'll get a linker error: multiple definition of <symbol> You can get the pre-processor to generate compile time errors with this nifty trick: #ifndef DEFINE_ME #error #endif
$ touch dummy.hxx $ cpp -dM ./dummy.hxx What's up with time_t?Nice discussion on it: http://stackoverflow.com/questions/2467418/portable-way-to-deal-with-64-32-bit-time-t Conditional FunctionsHere's a cool way of conditionally compiling functions without having to mess with the calling references (sort of). #ifdef CONFIG_PM static int pwm_backlight_suspend(struct platform_device *pdev, pm_message_t state) { ... return 0; } #else #define pwm_backlight_suspend NULL #endif StringizingA little tip on how you can turn your version number #defs into strings in your code. #define VER_MAJOR 0 #define VER_MINOR 1 #define vermkstr(tok) #tok #define VERSION_STRING(one,two) "v" vermkstr(one) "." vermkstr(two) const char* greeting = "ESA Controller " VERSION_STRING(VER_MAJOR, VER_MINOR) "\r\n"; This relies on sending the defines through a macro function using the stringizing operator #. What's up with the _s functions?MS went on a "these standard functions aren't safe!" rampage a couple years ago and "deprecated" several standard functions, replacing them with _s ("safe") alternatives. strcpy is potentially unsafe because it can lead to buffer overflow if you try to copy a string to a buffer that is not large enough to contain it. The downside to this is that strcpy_s is non-standard and MS specific... and therefore if you write code to use it, your code will not be portable. glibc wiki: "New standard library functions should reflect good existing practice, and since it is not clear that these functions are good practice they have been omitted from glibc." Network ProgrammingSo, where are the network .h files that we need? Use #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> The errno_t Mysteryhttp://stackoverflow.com/questions/24206989/error-use-of-undeclared-identifier-errno-t Porting HelpPOSIX doesn't have a single API to wait for "all types" of events/objects as Windows does. Each one has its own functions. In Win programming, what's the diff between _beginthreadex and CreateThread?http://stackoverflow.com/questions/2100589/beginthread-vs-createthread Set EVs in the CodeSetEnvironmentString("BIN","/opt/bin"); Print functions and file namesprintf("\%]s:\%[s:\n", __FILE__, __func__); MathTo print a long double, use %Lg format string. Customize a macroText argument replacement in a macro: #define NP(id, pin) MX6PAD(SD##id##_##pin##__USDHC##id##_##pin) Double const, problems with const, etcNote that const binds to the left unless nothing is there, then it binds right. const ptr* const means the pointer and the data object are both const. You can have two different pointers to the same value, with one pointing to a const and the other not. The value will be treated differently depending on which pointer is used to access it. Pointer and data "const-ness" are independent. One big weirdness with const is that its placement can be counter-intuitive. const int * a = &b; // const integer *a int const * a = &b; // const integer *a int * const a = &b; // const pointer a You can const a C++ class member function to indicate it will not modify any class members. Interesting. The tricky part though is this nature needs to be carried through anything called by that function. So the compiler will complain if you have a const function that calls a non-const function, even if the top level function does not change anything itself. const variables can actually have their values changed via pointer access; in addition, a "const volatile" specification is not contradictory because for example a register val could still be changed by HW even if the code does not change it. These are meaningless as they bind to void and make a nothing const: void const foo(); const void foo(); storage class identifiers explained
Nice extern examination here http://stackoverflow.com/questions/14335742/can-local-and-register-variables-be-declared-extern What is __declspec?__declspec(dllimport) is a storage-class specifier that tells the compiler that a function or object or data type is defined in an external DLL. The function or object or data type is exported from a DLL with a corresponding __declspec(dllexport) How are structs passed as parameters?The entire struct goes onto the stack, and the registers are not used. This is not necessarily the case with the primitive data types, as it depends on the compiler and architecture/ABI. How to turn off warnings about unused variables?(void)(unused_int); Inline functionsFor an inline function, there is no actual function call - the instructions are placed into the caller. So there's no context switch. An inline function name will not appear in the executable file symbol table. Type promotionWhen you combine unsigned and signed types in math operations, the signed operands are promoted to unsigned. glibcglibc is the GNU C library. It’s an implementation of the standard C library. Any program written in C will use the standard library for things like accessing files and the network, displaying messages to the user, working with processes and so on. It’s a fundamental component of the operating system. Hundreds of applications, libraries, and even other non-C programming languages installed on a typical Linux system will make use of the C library. There are other C libraries on Linux too, such as musl, but glibc is the most common one. libstdc++ is similar to glibc, but for C++: it’s an implementation of the standard library for C++. Any program written in C++ will use this to implement things in the C++ libraries. Things like threads, streams, files, Input/Output and so on. Symbol versioningOne way glibc preserves the ABI but still provides new features is through the use of symbol versioning. Each symbol, or function, provided by glibc is associated with a version. The linker (ld) links to the function-name-with-the-version. If your C program calls the function glob64, the linker will link it to not just the glob64 symbol in glibc, but to the fully versioned-symbol glob64@GLIBC_2.27. You can think of the text glob64@GLIBC_2.27 as the glob64 symbol with the version GLIBC_2.27. app: /lib64/libc.so.6: version `GLIBC_3.1.45' not found (required by ./app) This error happens when the runtime linker tries to load the standard C library (libc) for app. The runtime linker sees that app has a dependency on a symbol and the version GLIBC_3.1.45 is not found in this C library. In other words, the errors mean that app was linked against an newer version of the C/C++ library. The C/C++ library available on the system is too old and does not provide those symbols with those versions. C++Modern C++ Context2003 - 2011 was a great period of self-reflection and cleanup, adding robust resource management, eliminating memory leaks and 2007 really kicked off the modern C++ movement. Resource safety is the biggest thrust of modern C++. Three key operations required for robust resource management: construct, copy, assign. The modern advice is to use smart pointers as much as possible, and not classic pointers. Common thread is the big players in the industry don't want to break backwards compatibility with C and traditional C++. C++ has stronger type safety than C. C++11 introduce the concurrency features (thread, atomic, etc). C++20 added built-in support for GPS, UTC, TAI, time zone conversion. Notes on exceptionsDon't throw exceptions from destructors. Some exception types shown here: https://faculty.cs.niu.edu/~mcmahon/CS241/Notes/exceptions.html Per ARM, an exception is described as synchronous if it is generated because of execution or attempted execution of the instruction stream, and where the return address provides details of the instruction that caused it. Otherwise, an exception is described as asynchronous. This sounds pretty analogous to interrupts. C SEH is a Microsoft extension to C and C++ for handling things like HW faults. The major difference between C structured exception handling (SEH) and C++ exception handling is that the C++ exception handling model deals in types, while the C structured exception handling model deals with exceptions of one type; specifically, unsigned int. That is, C exceptions are identified by an unsigned integer value, whereas C++ exceptions are identified by data type. When a structured exception is raised in C, each possible handler executes a filter that examines the C exception context and determines whether to accept the exception, pass it to some other handler, or ignore it. When an exception is thrown in C++, it may be of any type. A second difference is that the C structured exception handling model is referred to as asynchronous, because exceptions occur secondary to the normal flow of control. The C++ exception handling mechanism is fully synchronous, which means that exceptions occur only when they are thrown. Exceptions make interspersing C and C++ code error-prone. Calling C++ functions that can throw from C is problematic yet that compiles without error. I've come across issues using C++ exceptions in a pthread line of execution, with application crashes on a throw. This was caused by throwing an exception in a class constructor, which is a very convenient C++ pattern. There is some chatter about this problem, with various possible suggested workarounds such as signal and handler or using C's longjmp and setjmp as alternatives. Notes on vector and listTo put an object into a vector, you must have a public copy constructor implemented, due to the way management is done with the objects. In the old days, a copy constructor would be hidden in private if not implemented to generate an error. Now, you could put = delete on it to give a more helpful error. A list does not have this limitation because of the double linked list implementation that doesn't require a copy constructor. C String Array Class MembersOdd difference to make note of: char m_foo[] = "bar"; //will not work, although it works outside of a class char m_foo[4] = "bar"; //required for a class member Namespace UsageIn the code using namespace <foo> though you can also pick up specific pieces only, with using std::cout for example. When you see something like this auto yolo = xilinx::yolov3::YOLOv3::create_ex("yolov3_voc_416", true); keep in mind that the namespace tags could be nested namespace scopes OR classes. Copy argument reversalNote that std::copy reverses the arguments to src,dest instead of the dest,src order that memcpy uses. How is constexpr different than const?It is used to explicitly state that a compile time constant is needed/used. You'll get a compile error if you don't assign a value. constexpr specifies that the object should be available during compilation. Compilation and execution are disjoint and discontiguous, both in time and space. So once the program is compiled, constexpr is no longer relevant. With const, it can be set at run time (or compile time) so it won't complain. Every variable declared constexpr is implicitly const. One thing to keep in mind is that this is not straight-forward for C++ strings, but works for C-strings. std::string is not a compile time constant type, but there are actually upcoming updates to C++20 that will allow something like that anyway. constexpr static std::string foo = "bar"; //NOPE constexpr static auto foo = "bar"; //YEP constexpr static char foo[] = "bar"; //YEP What is RAII?Resource Acquisition Is Initialization. The process of acquiring a resource at initialization time, and releasing at termination. In other words, a class follows RAII with "constructor acquires, destructor releases". Resource acquisition must succeed for initialization to succeed. The resource is thus held while the object exists. lvalues and rvaluesEvery C++ expression is either an lvalue or an rvalue. An lvalue refers to an object that persists beyond a single expression. You can think of an lvalue as an object that has a name. All variables, including nonmodifiable (const) variables, are lvalues. An rvalue is a temporary value that does not persist beyond the expression that uses it. Arrays names decay into rvalue pointers in expressions, so they cannot be incremented like normal pointers, which are lvalues. Another way to state this is the lvalue is for the left side of an assignment op, the rvalue is the right side. New with C++11 you have this "double reference" operator which actually means the parameter is a temporary rvalue only. Instead of being a reference (aka a second variable label), think of it as a "reference to a reference" which logically is nothing/fleeting. void foo(int&& a) { //Some code... } int main() { int b; foo(b); //Error. An rValue reference cannot be pointed to a lValue. foo(5); //Compiles with no error. foo(b+3); //Compiles with no error. int&& c = b; //Error. An rValue reference cannot be pointed to a lValue. int&& d = 5; //Compiles with no error. } static_assertThe C++ 11 standard introduced a feature named static_assert() which can be used to test a software assertion at the compile time. Can be used to replace preprocessor #error directive. // https://www.geeksforgeeks.org/understanding-static_assert-c-11/ unique_ptr and shared_ptrA unique_ptr makes the object pointed to get deleted automatically after the pointer is out of scope. Only one pointer is allowed to point to the object at a time. So it doesn't really seem to make sense to use it for a global. A shared_ptr can point to the same object as others do, and in memory the object is followed by a reference counting block of data. When the last shared_ptr pointing to an object is deleted (ref count becomes 0), the object is deleted. Note that these are not true types, they are template classes. What about make_unique? This template will actually construct an object and return a unique_ptr to it. You cannot use unique_ptr<void> because the automatic deletion wouldn't know what to do with a void type. If you want to delete the object the unique pointer points to and later re-use the unique pointer, then you can use the reset() function. For the C way of allocation memory, replace as follows: //C char* dbufp = (char*) malloc(nbytes); //... free(dbufp); //modern C++ std::unique_ptr<char[]> databuf = std::make_unique<char[]>(nbytes); char* dbufp = databuf.get(); Note that make_unique was not introduced until C++14. You can implement it for C++11 if you like: namespace std { // for non-array objects template<typename T, typename... Args> std::unique_ptr<T> make_unique(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); } //for dynamic array types template <class T> typename std::enable_if < std::is_array<T>::value, std::unique_ptr<T> >::type make_unique(std::size_t n) { typedef typename std::remove_extent<T>::type RT; return std::unique_ptr<T>(new RT[n]); } } Cast OperatorsC++ introduced four new ones:
Overloading OperatorsSimple example: class Foo { public: ... Foo & operator=(const Foo &rhs); ... } The following operators cannot be overloaded: . .* :: ?: new delete sizeof typeid static_cast dynamic_cast const_cast reinterpret_cast The following operators should not be overloaded: && || , Proxy Classes (from More Effective C++)Say we have a string character and we want to be able to distinguish between read and write accesses to it. We would create a proxy class within String. Three things can be done with a proxy: * Create it * Use it as a target of assignment (a write) * Use it any other way (a read) Here's an example reference-counted String class using a proxy class: class String { public: class CharProxy { public: CharProxy(String& str, int index); //creation CharProxy& operator=(const CharProxy& rhs); //writes CharProxy& operator=(char c); operator char() const; //reads private: ... }; ... }; Virtual FunctionCreated in part to shift burden of type-related function calls to compiler, instead of a programmer doing a lot of maintenance. A parent class virtual function can be re-implemented by child classes. It's akin to an operator performing different functions depending on the type of the operand. A call to a virtual function is called a message dispatch. Double-dispatching (which is required when calls to the same virtual function of a common inherited base class for two objects are needed) consists of two separate calls, each to determine the type of the object. Unfortunately, each class must be aware of its siblings. A great thing about VFs is that there can be no unexpected object types. A pure virtual is declared with a zero assignment virtual int run(const char& foo) = 0; You MUST derive a class (and implement said function) in order to use it. This is a good description of the virtual function override mechanism: https://en.cppreference.com/w/cpp/language/override Friend FunctionA friend function can access the non-public members of a class, i.e. a class can allow non-member functions and other classes to access its own private data by making them friends. Within some_function you can access myfoo.a and myfoo.b. class Foo { private: int a,b; public: void assign_ab() { a=100; b=200; } friend int some_function(Foo myfoo); }; Array Member InitializationIt turns out, if you want to init array values inside a class declaration you can, but you must specify the size of the array. Otherwise, zero is the default. This is not needed for an unassociated global/local array. http://www.stroustrup.com/C++11FAQ.html#member-init Modern C++ Parameter EfficiencyDefault advice. For big costly types, returning unique_ptr and using a dynamic allocation is also fine.
C++ StructA struct in C++ behaves much like a class, effectively the same thing other than scope, but with members all being public by default. It can inherit. You can assign methods and constructors to a struct by defining them inside, and you can define operators as well. As shown here you can also overload a typecasting instruction. struct foo { uint16_t index; foo() {} operator bool() const { return index == 0; } bool foo() const { return index++; } } Code LocksWhen using lock_guard on a mutex, you probably don't need to unlock because the lock_guard is a local variable and unlock is called in the destructor once the scope is terminated. std::string VS std::vector<char>Growing an assemblage of characters is more easily done with the string class than a vector of char, due to problems using the .insert() or the std::copy functions with the vector when you want to append a bunch of chars. Simply use the + operator to extend the string. C++ string_view additionTo address the issue with std::string being expensive to initialize (or copy), C++17 introduced std::string_view (which lives in the <string_view> header). std::string_view provides read-only access to an existing string (a C-style string, a std::string, or another std::string_view) without making a copy. Combining C and C++
i.e. #ifdef __cplusplus extern "C" { #endif in header files at top and then close the bracket with #ifdef __cplusplus } // extern "c" #endif What extern "C" does is affect linkage. C++ functions, when compiled, have their names mangled -- this is what makes overloading possible. The function name gets modified based on the types and number of parameters, so that two functions with the same name will have different symbol names. Code inside an extern "C" is still C++ code. There are limitations on what you can do in an extern "C" block, but they're all about linkage. You can't define any new symbols that can't be built with C linkage. That means no classes or templates, for example. You cannot function overload in C. Functions like printf are what's called variadic functions, declared differently from overloaded functions. Notes on Refactoring C code to C++ codeJohn Carter has some tips on going from C to C++: So I have recently completed converting a large C embedded system to C++. Here are some lessons learnt.
Once it was all compiling linking running on target, all unit tests running, all automated on target tests running, I "Flipped the Switch" and stripped out the transitional support., Static Analysis: Previously we were a "splint" shop, our code was checked by splint. But splint doesn't do C++.
Floating Pointfloats are generally 32 bit, doubles are 64 bit Doubles can go up to 15 decimal place of precision, but floats only up to 7. In C++ you can use setprecision() to get all the way up to the 15 for doubles, otherwise it remains at 7. Four letter hex words for magic numbersACED BABE BADE BEAD BEEF CAFE CEDE DEAD DEAF DEED FACE FADE FEED DebuggingFor this cryptic error undefined reference to _aeabi_uldivmod? Note that if you are using anything less that GCC 9, you'll have problems running with the C++17 filesystem library. Unix/Posix/Linux NotesAn operation requiring more than one function call cannot be atomic, because the kernel can suspend us between function calls. An atomic operation allows us to complete multiple instructions without being interrupted/suspended by another thread. The execution of the group of instructions is all or nothing. |