📜 ⬆️ ⬇️

The world is driven by language C



We recently published a translation of an article in which arguments were made in favor of studying the languages ​​of the C family. This post caused a lot of controversy, including the view that the languages ​​of the C family were leaving the stage; their demand, although great, is decreasing. Perhaps this is so. But still, C is still one of the most common.

Operating Systems


Many projects written in this language started decades ago. The development of UNIX began in 1969, and its code was rewritten in C in 1972. Initially, the OS kernel was written in assembler. And the C language was created in order to rewrite the kernel in a higher level language that performs the same tasks using fewer lines of code.

In 1985, Windows 1.0 was released. Although the Windows code is closed, Microsoft claims that its kernel is written mostly in C. That is, the core of the operating system, which has occupied about 90% of the world market for decades, is C.
')
The development of the Linux kernel began in 1991, it is also mostly written in C. In 1992, the Linux kernel was released under the GNU license and was used as part of the GNU Operating System. And many of the components of the GNU OS itself are written in C, since the project used this language and Lisp. Today, 97% of supercomputers from the Top 500 of the most powerful computing systems in the world use the Linux kernel . Not to mention the millions of servers and personal computers.

The OS X kernel, as is the case with the two operating systems listed above, is also mostly written in C.

Android , iOS and Windows Phone are also based on C, since these systems are adaptations of Linux, Mac OS and Windows respectively. So the world of mobile devices is controlled by this language.



Database


All the most popular DBMS, including Oracle, MySQL, MS SQL Server and PostgreSQL are written in C (the first three are in C and C ++). These DBMSs are used throughout the world, in financial, government, media, telecommunications, educational and trade organizations, in social networks and many other areas.



But the scope of C is not limited to these well-known projects that began long before the birth of many of today's developers. Despite the huge variety of existing programming languages, today many projects are still being created in C.

3D video


Modern cinema uses to create three-dimensional films of the application, which are mainly written in C and C ++. One of the reasons for this is the high demands on the efficiency and speed of such systems, since they have to process huge amounts of data per unit of time. The higher their efficiency, the less time it takes to create personnel, and, as a result, studios spend less money.

Embedded Systems


We are surrounded by many familiar instruments and devices, many of which also use the C language : electronic alarm clocks, microwaves, coffee makers, televisions, radios, remote control systems, etc. Or take the same modern car, stuffed with all sorts of systems, which are also programmed in C:


In most cases, C is used for programming vending machines for various snacks and other small items. Also this language is massively used in cash registers, in plastic card terminals.

Most of the devices we encounter are equipped with embedded systems, that is, a processor / microcontroller with the appropriate firmware and the necessary element strapping. This allows in a small amount to implement fast processing of algorithms (sometimes quite complex) and interaction with users. For example, the same alarm clock determines which button you press, how long you hold it, and accordingly executes this or that procedure, displaying the necessary information on the screen. Or an anti-lock system in a car: in a very short period of time, it is necessary to determine whether wheel locking has occurred after the start of braking and, if necessary, turn the brakes on and off cyclically, preventing an uncontrolled slip of the car.

Of course, manufacturers use different languages ​​for programming embedded systems, but most often it is C, because of its flexibility, efficiency, high performance and “proximity” to equipment.



Why do we still use C?


Today there are many languages ​​that are more effective in some projects than C. In some languages ​​there are much more built-in libraries that simplify working with JSON, XML, user interfaces, web pages, client requests, connections to the database, multimedia and etc.

But despite this, there are many reasons why C will probably be used for a long time.

Portability and performance


C language can be called "portable assembler." It is close to the machine code, and at the same time it can run on almost all existing processor architectures, for which at least one compiler is written. And thanks to the high level of optimization of binary files created by compilers, there are not so many opportunities for their further improvement manually using assembler.

Portability and efficiency of the language are also related to the fact that " compilers, libraries and interpreters of other programming languages ​​are often implemented in C ". Basic implementations of interpreted languages ​​such as Python , Ruby and PHP are also written in C. This language is even used by compilers of other languages ​​to interact with the hardware component. For example, C is used as an intermediary for the Eiffel and Forth languages. This means that instead of generating machine code for each architecture, the compilers of these languages ​​generate intermediate C code, it is processed by the C compiler and it already generates machine code.

Language C has actually become “ lingua franca ” for developers. As noted by Alex Ellaine, head of engineers at Dropbox and creator of the site www.cprogramming.com :

The C language allows you to conveniently and clearly express various general ideas in programming. Moreover, many of the things used here — for example, argc and argv for command-line parameters, loop operators, variable types — are used in many other languages. So, knowing C, it will be much easier for you to communicate with specialists in other programming languages.

Memory management


Arbitrary access to memory cells and pointer arithmetic are important language features that allow it to be used for “system programming,” that is, the creation of an operating system and embedded systems.

When software interacts with the hardware component, memory is allocated for peripheral components of computer systems and input / output tasks. System applications must use the allocated memory for interacting with the "world." And here, at the right time, the possibilities of the C language for managing random access to memory occur.

For example, the microcontroller can be programmed so that the byte located at 0x40008000 is sent by a universal asynchronous transceiver (UART, the standard component of the hardware component for interacting with peripheral devices) each time the fourth bit of the address 0x40008001 is assigned a value of 1. And after this value is assigned, it is reset by the peripheral device.

Here is what the C code looks like by sending a byte through UART:

#define UART_BYTE *(char *)0x40008000 #define UART_SEND *(volatile char *)0x40008001 |= 0x08 void send_uart(char byte) { UART_BYTE = byte; // write byte to 0x40008000 address UART_SEND; // set bit number 4 of address 0x40008001 } 

The first line will be presented in the form:

 *(char *)0x40008000 = byte; 

This entry tells the compiler to interpret the value 0x40008000 as a pointer to char , then use the first operator * to dereference the pointer (get the value at the address), and finally assign a value to byte to the dereferenced pointer. In other words, write the value of the variable byte to memory at address 0x40008000.

The second line will be presented in the form:

 *(volatile char *)0x40008001 |= 0x08; 

Here we perform the bitwise OR operation on the value at 0x40008001 and the value 0x08 (00001000 in binary representation, that is, 1 in the 4th bit), and store the result again at address 0x40008001. That is, the value is assigned to the fourth bit of a byte located at address 0x40008001. It is also declared that the value at this address is volatile (volatile). The compiler understands that it can be changed by processes external to our code, so it does not make any assumptions about this value after writing to this address is complete. In this case, the UART returns the bit to its previous state immediately after the software assigned it a value.

This information is important to the compiler optimizer. If, for example, you do this inside a for loop, without specifying the variability of the value, then the compiler may assume that it never changes after the recording, and after the end of the first cycle it stops the execution of the command.

Deterministic use of resources


Garbage collection is one of the standard features of a language that cannot be relied upon in system programming. For some embedded systems, even dynamic memory allocation is unacceptable. Embedded applications must run quickly and operate with very limited memory resources. Most often these are real-time systems in which it is impermissible to make an undetermined call to the garbage collector. And since dynamic allocation cannot be used due to lack of memory, it is necessary to apply other memory management mechanisms, for example, to place data at specific addresses. Pointers in C allow you to do this. And languages ​​that depend on dynamic memory allocation and the garbage collector cannot be used in systems with limited resources.

Code size


The C code runs very fast and uses less memory than most other languages. For example, C binaries for embedded systems are about half the size of similar C ++ files. This is largely due to the support for exceptions.

Exceptions are a great tool that appeared in C ++, and if you use it wisely, they practically do not affect the file execution time, although they increase the size of the code.

C ++ example:

 // Class A declaration. Methods defined somewhere else; class A { public: A(); // Constructor ~A(); // Destructor (called when the object goes out of scope or is deleted) void myMethod(); // Just a method }; // Class B declaration. Methods defined somewhere else; class B { public: B(); // Constructor ~B(); // Destructor void myMethod(); // Just a method }; // Class C declaration. Methods defined somewhere else; class C { public: C(); // Constructor ~C(); // Destructor void myMethod(); // Just a method }; void myFunction() { A a; // Constructor aA() called. (Checkpoint 1) { B b; // Constructor bB() called. (Checkpoint 2) b.myMethod(); // (Checkpoint 3) } // b.~B() destructor called. (Checkpoint 4) { C c; // Constructor cC() called. (Checkpoint 5) c.myMethod(); // (Checkpoint 6) } // c.~C() destructor called. (Checkpoint 7) a.myMethod(); // (Checkpoint 8) } // a.~A() destructor called. (Checkpoint 9) 

Class , B and C methods are defined elsewhere (for example, in other files). Therefore, the compiler cannot analyze them and understand whether they will throw exceptions. So the compiler must be ready to handle possible exceptions from any constructors, destructors, or calls to other methods. In general, destructors should not throw exceptions, this is a very bad practice, but the user can do it. But destructors can call functions or methods (explicitly or implicitly) that will throw an exception.

If one of the calls in myFunction throws an exception, then the stack unwinding mechanism should be able to call all destructors for already created objects. To check the “checkpoint number” of the call that initiated the exception, the stack return mechanism uses the return address of the last function call. This is done with the help of an auto-generated auxiliary function (something like a lookup table), which is used to return the stack if an exception is thrown from the body of this function:

 // Possible autogenerated function void autogeneratedStackUnwindingFor_myFunction(int checkpoint) { switch (checkpoint) { // case 1 and 9: do nothing; case 3: b.~B(); goto destroyA; // jumps to location of destroyA label case 6: c.~C(); // also goes to destroyA as that is the next line destroyA: // label case 2: case 4: case 5: case 7: case 8: a.~A(); } } 

If an exception is thrown at control points 1 and 9, then the objects do not need to be destroyed. If issued at checkpoint 3, then B and A must be destroyed. If at point 6 - you need to destroy C and A In each of these cases, the order of destruction must be respected. At points 2, 4, 5.7 and 8, only object A destroyed.

This auxiliary function increases the size of the code, which is part of the additional cost of free space, a characteristic difference in C ++ from C. But for many embedded systems this “bloating” is unacceptable. Therefore, C ++ compilers for embedded systems often include a flag to disable exceptions . But disabling them comes at a price because the Standard Template Library makes extensive use of exceptions to report errors. And the refusal of exceptions requires from C ++ developers certain skills in identifying possible causes of errors and finding bugs.

One of the principles of C ++: "You do not pay for what you do not use." In other languages, things are even worse with increasing code size due to the addition of various useful functionality that embedded systems cannot afford. Although the language does not have these capabilities, the amount of code is much less.

Why study C


This language is not difficult to learn, so you do not have to climb out of the skin. What benefits will you gain by mastering C?

Lingua franca . As mentioned above, C is a kind of "universal" language for developers. Many implementations of new algorithms in books and on websites are provided first, or exclusively, in C. This allows them to be ported as widely as possible. However, some programmers who are not familiar with the basic concepts of C, have great difficulty in converting C-algorithms to other programming languages.

Since C is an old and widespread language, almost any necessary algorithms written on it can be found on the network. So knowledge of C opens up many opportunities for the developer.

Understanding the car . Often, when developers discuss the behavior of a particular piece of code or functionality in other languages, the conversation is conducted in "C terms": Is only a pointer to an object passed here, or is it copied in its entirety? Is it possible that there is any cast? Etc.

When analyzing the behavior of a high-level language code, few people discuss in the terminology of assembler commands. When we discuss the actions of the machine, we usually speak (and think) in C.



Work on many interesting projects created in C. Many interesting projects have been created in this language, from large DBMS and OS kernels to small applications for embedded systems. Is it worth refusing to work with products that you like, just because you have not learned an old, compact, powerful and time-tested programming language?



Conclusion


The world is ruled not by the Illuminati , but by the programmers in C.

It does not seem that the old man C began to leave the stage. Its proximity to hardware, excellent portability, and deterministic use of resources make it an ideal choice for low-level development of operating systems and embedded software. Versatility, efficiency and good performance are very important when creating applications that perform complex data manipulations, such as database or 3D animation. Yes, there are many languages ​​that are more profitable to use in some tasks than C. But, in terms of the total aggregate of advantages, C hardly has any competitors. The performance of C is still unsurpassed.

The world is filled with devices whose software is written in C. These devices are used daily by billions of people. No need to condescendingly relate to this language: it is very actively used to this day and, apparently, will be used for a very long time in various fields.

Source: https://habr.com/ru/post/265427/


All Articles