📜 ⬆️ ⬇️

"The poor people" language Fortran


Many of us, learning programming at universities or at home, did it in C / C ++. Of course, everything depends on the time when our acquaintance with programming languages ​​began. Say, someone started with Fortran, others - with Basic or Delphi, but it is necessary to recognize that the share of a programmer who started his thorny path with C / C ++ is the greatest. Why am I doing all this? When we are faced with the task of learning a new language and writing code on it, we often base ourselves on how I would write it in my “base” language. Succeeding the question - if you need to write something on Fortran, then we remember how it would be implemented in C and do it by analogy. Once again faced with the subtlety of the language, which led to a completely non-working algorithm and a big problem escalated to me, I decided to find as many nuances of Fortran (Fortran 90/95) as possible, compared to C, which I personally encountered. This is a kind of "unexpected", which you obviously did not plan to see, and they bang - and surfaced!
Of course, we will not talk about syntax - in each language it is its own. I will try to talk about global things that can change everything "upside down." Go!

Passing Arguments to Functions
We all remember that with such a code in C it is impossible to change the value of the variable a in the function calling main:
void modify_a(int a) { a = 6; } int main() { int a = 5; modify_a(a); return 0; } 

Everything is correct - the arguments to the function in the C language are passed by value, so changing a in the modify_a function will not work. To do this, pass the argument by reference, and then we will work with the same a passed from the called function.
So, the “unexpected” number “one” is that in Fortran everything is the other way around! Arguments are passed to functions by reference, and such code will completely change the value of a :
 a = 5 call modify_a (a) contains subroutine modify_a (a) integer a a = 6 end subroutine modify_a end 

I think everyone understands the problems that may arise from ignorance of this fact. And this specificity can manifest itself in many places, in particular, when working with pointers, but this will be a separate conversation.

Work with arrays
By default, the indexing of arrays in Fortran starts from 1 , not from 0 , as in C. That is, real a (10) gives us an array from 1 to 10 , and in C float a [10] goes from 0 to 9 . However, we can also specify an array as real a (0: 100) in Fortran.

In addition, multidimensional arrays are stored in memory in Fortran column by column. Thus the usual matrix

is located in memory so:

Do not forget about this when working with arrays, especially if we transfer them to / from a C function through libraries.
')
Undeclared variables
Fortran by default will not swear on data that we have not explicitly declared, because here there is the concept of implicit data types. It went from ancient times, and the idea is that we can immediately work with the data, and their type will be determined depending on the first letter in the name - how tricky!
Attempting to compile code with the C compiler predictably produces the error 'b: undeclared identifier' :
 int main() { b = 5; } 

In Fortran work with a bang:
 i = 5 end 

How many completely diverse errors in the code can be from this. Therefore, do not forget to add to the code IMPLICIT NONE , prohibiting such "games" with implicit declarations:
 implicit none i = 5 end 

And we immediately see the error: error # 6404: This name doesn’t have an explicit type. [I]
By the way, the Fortran language is not case sensitive, so the variables a and A are the same. But this is the syntax, which I promised not to talk about.

Initialization of local variables
It would seem, than similar initialization can be bad:
 real :: a = 0.0 

And how does it differ from this:
 real a a = 0.0 

Unexpected surprise for C developers - there is a fundamental difference in this in Fortran! If a local variable is initialized at the time of the declaration, then the SAVE attribute is implicitly applied to it. What is this attribute? If a variable is declared as SAVE (explicitly or implicitly), then it is static, which means it is initialized only at the first call to the function. Subsequent entries to the function retain the previous value. And this may not be what we expect. As a piece of advice, avoid such initializations, and if necessary, use the SAVE attribute explicitly. By the way, the compiler even has a separate option -save , which allows you to change the default settings (selection on the stack) and make all variables static (except for recursive functions and those variables that are explicitly declared as AUTOMATIC ).

Pointers
Yes, in Fortran, too, there is the concept of pointers. But they are used much less frequently, because it is possible to allocate memory dynamically in it without their help, and the arguments are passed by reference. It is worth noting that the pointer mechanism itself works differently in FORTRAN, so I’ll dwell on this in more detail.
You cannot make a pointer to any object here - only to the one that is declared in a special way. For example:
 real, target :: a real, pointer :: pa pa => a 

With the operator => we associate the pointer pa with the object a . Do not attempt to perform an assignment operation instead of =>. Everything will successfully gather, but will fall in rantayme. So, those who are used to simply assigning pointers to C will have to be forced to write every time => instead of = . First you forget, but then you drag in.
If we want the pointer not to be associated with an object, we use nullify (pa) - this is a kind of pointer initialization. When we simply declare a pointer, its status in Fortran is undefined, and the function that checks its association with objects ( associated (pa) ) will not work correctly.
By the way, why can't a pointer be associated with any variable of the same type as is done in C? First, I wanted it in the standardization committee. Just kidding Most likely, the whole thing is in yet another level of protection against potential errors - just so now we can’t definitely link the pointer to a random variable, and a similar restriction gives the compiler more information and, therefore, more possibilities for optimizing the code.
Besides the fact that the type of the pointer and the object must match, and the object itself must be declared with the TARGET attribute, there is also a restriction on the dimension of the arrays. Let's say if we work with one-dimensional arrays, then the pointer must be declared accordingly:
 real, target :: b(1000) real, pointer :: pb(:) 

If the array were two-dimensional, then the pointer would be pb (: :) . Naturally, the size of the array in the pointer is not specified - we do not know which array the pointer will be associated with. I think the logic is clear. After association, we can work with the pointer as usual:
 b(i) = pa*b(i+1) 

What is the same as writing b (i) = a * b (i + 1) . You can assign a value, for example, pa = 1.2345 .
Thus, the value of a will be 1.2345 . An interesting feature of FORTRAN pointers is that they can be used to work with part of an array.
If we wrote b => pb , then we can work with 1000 elements of the array b through the pointer pb .
But you can write like this:
 pb => b(201:300) 

In this case, we will work with an array of only 100 elements, and pb (1) is b (201) .
It's funny how you can use the allocate memory function in case of pointers. Writing allocate (pb (20)) we will allocate an additional 20 elements of the array of the real type, which will be accessible only through the pointer pb .
In general, a person accustomed to C, all this will seem unusual. But, if you start writing code, you get used to it quickly enough, and everything starts to seem convenient.
The developer, who pushed me to the idea of ​​writing this blog, also thought so and actively worked with pointers to the right and left, creating the code, the algorithm of which uses the tree, but did not take into account one feature. For Fortran this Sishny code corresponded:
 void rotate_left(rbtree t, node n) { node r = n->right; ... 

The node structure has fields containing node * pointers, for example right .
The function creates a local variable r , assigns the value n-> right to it, and so on and so forth. The implementation in FORTRAN is as follows:
 subroutine rotate_left(t, n) type(rbtree_t) :: t type(rbtree_node_t), pointer :: n type(rbtree_node_t), pointer :: r r => n%right ... 

And here, at the very beginning, lies the "error of errors." We associate the pointer r with n% right . Changing the code r further, we will change n% right , in contrast to C, where only the local variable r will change. As a result, the whole tree has become incomprehensible into what. The way out is another local pointer:
 subroutine rotate_left(t, n_arg) type(rbtree_t) :: t type(rbtree_node_t), pointer :: n_arg type(rbtree_node_t), pointer :: r type(rbtree_node_t), pointer :: n n => n_arg r => n%right ... 

In this case, if we subsequently change the association at the pointer n , then this will not affect the “external” n_arg .

Thong
And finally, one small feature that spoiled a huge amount of memory in mixed applications (C and Fortran). What do you think could be the difference when working with strings in C:
 char string[80]="test"; 

And FORTRANE:
 character(len=80) :: string string = "test" 

The answer will easily help give a debugger. In this case, in FORTRAN, the remaining unused bytes are clogged with spaces. At the same time, there is no typical C character for the end of the string / 0 , so you need to be extremely careful when transferring the strings from Fortran to C and back. Again, I’ll say that in order to work safely with C and Fortran, you need to use a special module ISO_C_BINDING , which solves this distinction, and many other problems.

On this I finish my story. Now you know exactly the most important differences between C and FORTRAN, and if you really have to write code on the latter, I think you will do it no worse than C, right? Well, this post will help.

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


All Articles