Programming Language Textbook D. Part 2

Original author: Ali Çehreli
  • Transfer
  • Tutorial
The second part of the translation of D Programming Language Tutorial by Ali Çehreli . Most of the material is focused on beginners, but since most of the audience already has basic programming knowledge, this material has been removed for habrakat. This section discusses fundamental types, type properties, the basics of compilation and imperative programming.



Other parts:


Base material

Compiler


We saw earlier that the most commonly used tools in D are a text editor and compiler . Programs in D are written in text editors (approx. Your KO).

When using compiled languages such as D, you need to understand the concept of compilation and the function of the compiler.

Machine codes


The brain of a computer is a microprocessor (or CPU, short for central processing unit ). Coding is an indication of what the CPU should do, and the instructions that are used are called machine codes .

Most CPU architectures use machine-specific codes specific to them. These machine instructions are determined by hardware limitations during architecture design. At the lowest level, these instructions are implemented as electrical signals. Since the simplicity of programming at this level is not the main goal, writing programs directly in the computer code of the CPU is a very difficult task.

These machine instructions are special numbers that represent the various operations supported by a particular CPU. For example, for an imaginary 8-bit CPU, the number 4 can represent the load operation, the number 5 is the save operation, and the number 6 is the operation of increasing the value by one. Assuming that the first 3 bits on the left are the operation number and 5 subsequent bits are the value that is used in this operation, the example program in machine codes of this CPU may look like this:
Operation   Value            Meaning
   100      11110        LOAD      11110
   101      10100        STORE     10100
   110      10100        INCREMENT 10100
   000      00000        PAUSE


Being so close to the hardware, machine codes are not suitable for representing high-level concepts such as a playing card or a student record .

Programming languages


Programming languages ​​are designed to be an effective way of CPU programming with the ability to present high-level concepts. Programming languages ​​do not have to deal with hardware limitations; their main task is ease of use and expressiveness. Programming languages ​​are easier to understand by people, they are closer to natural languages:
if (a_card_has_been_played()) {
   display_the_card();
}


However, programming languages ​​adhere to much more strict and formal rules than any language that is spoken.

Compiled languages


In some programming languages, instructions must be compiled before becoming an executable program. Such languages ​​create fast-executing programs, but the development process involves two main steps: writing a program and compiling it.

In general, compiled languages ​​help in detecting errors even before the program starts to run.

D is a compiled language.

Interpreted Languages


Some programming languages ​​do not require compilation. Such languages ​​are called interpreted . The program can be launched directly from freshly printed source code. Some examples of interpreted languages: Python, Ruby, and Perl. Since there is no compilation stage, program development may be easier for such languages. On the other hand, since program instructions must be parsed for interpretation each time the program is launched, programs in such languages ​​are slower than their equivalents written in compiled languages.

In the general case for interpreted languages, many types of errors in the program cannot be detected until the start of execution.

Compiler


The purpose of the compiler is translation: it translates programs written in a programming language into machine code. This is a translation from the programmer language to the CPU language. This translation is called compilation . Each compiler understands a specific programming language and is described as a compiler for this language, for example, “compiler for D”.

Compilation errors


Since the compiler compiles the program according to the rules of the language, it stops compilation as soon as it reaches incorrect instructions. Incorrect instructions are those that get out of the language specifications. Problems, such as: inappropriate brackets, missing semicolon, misspelled keyword, etc. - all cause compilation errors.

The compiler also generates compilation warnings when it sees a suspicious piece of code that may be troubling, but not necessarily an error. However, warnings almost always indicate a real mistake or bad style, so the most common practice is to treat most or all warnings as errors.



Fundamental types


Earlier, we found out that the CPU is the brain of the computer. Most program tasks are performed on the CPU, and the rest are distributed across other parts of the computer.

The smallest unit of data in a computer is called a bit , which can take values ​​0 or 1.

Since a data type that can contain only values ​​0 or 1 would have very limited use, the CPU determines larger data types that are combinations of more than one bit. For example, a byte contains 8 bits. The most productive data type determines the CPU bit: 32-bit CPU, 64-bit CPU, etc.

The types defined by the CPU are still not enough: they cannot describe high-level concepts like the name of a student or a playing card. D provides many useful data types, but even these types are not enough to describe many high-level concepts. Such concepts should be defined by the programmer as structures (struct) or classes (class), which we will see in the following chapters.

The fundamental types of D are very similar to the fundamental types of many other languages, as shown in the following table. The terms appearing in this table are explained below:
A type DefinitionInitial value
bool Boolean True / Falsefalse
byte 8 bit signed number 0
ubyte 8 bit unsigned number 0
short 16 bit signed number 0
ushort 16 bit unsigned number 0
int 32 bit number cos sign 0
uint 32 bit unsigned number 0
long 64 bit signed number 0
ulong 64 bit unsigned number 0
float 32 bit floating point real number float.nan
double 64 bit floating point real number double.nan
real highest floating point number supported by hardware real.nan
ifloat imaginary part of a complex number for float float.nan * 1.0i
idouble imaginary part of a complex number for double double.nan * 1.0i
ireal the imaginary part of the complex number for real real.nan * 1.0i
cfloat complex option float float.nan + float.nan * 1.0i
cdouble complex option double double.nan + double.nan * 1.0i
creal complex option real real.nan + real.nan * 1.0i
char UTF-8 character (code point) 0xFF
wchar UTF-16 character (code point) 0xFFFF
dchar UTF-32 character (code point) 0x0000FFFF


In addition to the above, the void keyword describes entities that are not of type . The keywords cent and ucent are reserved for future use to represent signed and unsigned 128-bit values.

You can use int for most values, as long as there is no particular reason not to. Consider a double to represent concepts that have a fractional part.

Explanations of the concepts presented in the table
Boolean : A type of boolean expression that is true for true and false for false .

Signed Type: A type that can take negative and positive values. For example, byte can have values ​​from 0 to 255. The letter u at the beginning of the name of these types is taken from the word unsigned .

Floating-point number: The type can represent decimal-fractional values, as in 1.25. The accuracy of floating point calculations directly depends on the number of bits in the type: the more the number of bits, we get more accurate results.

Only floating point numbers can represent decimal fractions; integer types (e.g. int) can hold only values ​​like 1 and 2.

Complex types of numbers: These types can represent complex numbers from mathematics.

Imaginary types of numbers: These types can represent only the imaginary part of complex numbers. The letter i , which is in the column of initial values, in mathematics is the square root of -1.

nan: Acronym for "not a number", representing an invalid floating point value .


Translator Comments
It may seem strange that for floating-point numbers, NaN values ​​are used as initializers. An official explanation from the developers (although I do not agree with this position):

NaNs have the interesting property that no matter what operation they are used in, the result is NaN again. Therefore, NaN will be distributed and will be the result of any calculations where it was used. This means that a sudden NaN is an unambiguous indicator that an uninitialized variable has been used.

If 0.0 were used as the initial value, its consequences would not have been noticeable on the output, so if the initialization was unintentional by default, the bug could go unnoticed.

It is not meant that the default initializer should be a useful value, its purpose is to open bugs. NaN is well suited for this role.

But, of course, can the compiler detect and generate an error about variables that have not been initialized? In most cases, it can, but not always, and what it can depends on the complexity of the internal analysis of data flows. Therefore, the hope for this mechanism is an intolerable and unreliable solution.

Due to the nature of the CPU implementation, NaN is absent for integers, so D instead uses 0. A value of zero does not have such advantages for detecting errors as NaN has, but at least errors from unintentional initialization will be repeatable and therefore more debugged.


Type properties


In D, types have properties . To get the value of a property, you need to write the name of the property after the type through the dot. For example, the sizeof int property is invoked like this: int.sizeof . This chapter discusses only the following four attributes:
  • stringof: type name
  • sizeof: type size in bytes (for the number of bits, multiply by 8)
  • min: the minimum value that the type can take
  • max: the maximum value that the type can take

A program that prints these properties for int
import std.stdio;
 
void main ()
{
    writeln ("Type:", int.stringof);
    writeln ("Length in bytes:", int.sizeof);
    writeln ("Minimum value:", int.min);
    writeln ("Maximum value:", int.max);
}



Translator Notes
Some types have other properties, for example, float and double do not have the min property , instead -float.max and - double.max are used , more in detail .


size_t


You will also encounter the size_t type , whose name stands for “size type”. It is not an independent type, but an alias of the unsigned type, which is enough to represent all possible addresses in memory. Therefore, this type depends on the system: uint on 32- bit systems , ulong for 64-bit systems, etc.

You can use the .stringof property to see what type size_t refers to on your system:
The code
import std.stdio;
 
void main ()
{
    writeln (size_t.stringof);
}


Output on my system:
ulong



Exercises
Print properties of other types.

Note : You cannot use the reserved types cent and ucent in any program; as an exception, void has no properties .min and .max .

Decision
import std.stdio;
 
void main ()
{
    writeln ("Type:", short.stringof);
    writeln ("Size in bytes:", short.sizeof);
    writeln ("Minimum value:", short.min);
    writeln ("Maximum value:", short.max);
 
    writeln ();
 
    writeln ("Type:", ulong.stringof);
    writeln ("Size in bytes:", ulong.sizeof);
    writeln ("Minimum value:", ulong.min);
    writeln ("Maximum value:", ulong.max);
}




Base material

Assignment Operator and Calculation Order



The first two stumbling blocks that await students of programming are the assignment operator and the order of calculations.

Assignment operator

You will find similar lines in almost all programs, in almost all programming languages:
a = 10;


The meaning of this line is “make the value of a equal to 10”. Similarly, the following line means “make the value of b equal to 20”:
b = 20;


Based on the above information, what about the next line?
a = b;


Unfortunately, this line does not contain a comparison operator from mathematics, which, I assume, everyone knows. The expression above does not mean "a is b"! When we use the same logic as in the previous two lines, the expression above should mean “make the value of a equal to the value of b ”.

The well-known symbol = in mathematics has a completely different meaning in programming: "make the value on the left equal to the value on the right side."

Calculation order

Program operations are performed step by step in a specific order. The previous three expressions in the program can be seen in the following order:
a = 10;
b = 20;
a = b;


The meaning of these lines together: "make the value of a equal to 10, then make the value of b equal to 20, then make the value of a equal to the value of b ". Accordingly, after performing these three operations, both a and b will be equal to 20.

Exercise


Learn how the following three operations interchange the values ​​of a and b . If at the beginning their values ​​were equal to 1 and 2, respectively, then after performing the operations, the values ​​become equal to 2 and 1:
c = a;
a = b;
b = c;


Decision
The values a , b, and c are printed on the right side of each operation:
в начале           →     a 1, b 2, c неважно
c = a              →     a 1, b 2, c 1
a = b              →     a 2, b 2, c 1
b = c              →     a 2, b 1, c 1

At the end, the values ​​of a and b are interchanged.


Also popular now: