The POINTER statement establishes pairs of variables and pointers. @ Each pointer contains the address of its paired variable.
POINTER ( p1, v1 ) [, ( p2, v2 ) ... ]
where:
v1, v2 are pointer-based variables.
p1, p2 are the corresponding pointers.
A pointer-based variable is a variable paired with a pointer in a POINTER statement. A pointer-based variable is usually just called a based variable. The pointer is the integer variable that contains the address.
Example: A simple POINTER statement:
POINTER ( P, V )
Here, V is a pointer-based variable, and P is its associated pointer.
See "POINTER", for more examples.
Normal use of pointer-based variables involves the following steps. The first two steps can be in either order.
Define the pairing of the pointer-based variable and the pointer in a POINTER statement.
Define the type of the pointer-based variable.
The pointer itself is integer type and should not appear in a type declaration.
Set the pointer to the address of an area of memory that has the appropriate size and type.
You do not normally do anything else explicitly with the pointer.
Reference the pointer-based variable.
Just use the pointer-based variable in normal FORTRAN statements--the address of that variable is always from its associated pointer.
No storage for the variable is allocated when a pointer-based variable is defined, so you must provide an address of a variable of the appropriate type and size, and assign the address to a pointer, usually with the normal assignment statement or data statement.
The loc(), malloc(), and free() routines associate and deassociate memory addresses with pointers. (These routines are described in Chapter 6.)
When compiled for 64-bit environments, pointers declared by the POINTER statement are INTEGER*8 values.
You can obtain the address from the intrinsic function LOC().
Example: Use the LOC() function to get an address:
* ptr1.f: Assign an address via LOC() POINTER ( P, V ) CHARACTER A*12, V*12 DATA A / 'ABCDEFGHIJKL' / P = LOC( A ) PRINT *, V(5:5) END
In the above example, the CHARACTER statement allocates 12 bytes of storage for A, but no storage for V. It merely specifies the type of V because V is a pointer-based variable, then assign the address of A to P, so now any use of V will refer to A by the pointer P. The program prints an E.
When compiled for 64-bit environments, LOC() returns an INTEGER*8 value. The receiving variable must be either a pointer or an INTEGER*8 variable to avoid possible address truncation.
The function MALLOC() allocates an area of memory and returns the address of the start of that area. The argument to the function is an integer specifying the amount of memory to be allocated, in bytes. If successful, it returns a pointer to the first item of the region; otherwise, it returns an integer 0. The region of memory is not initialized in any way.
Example: Memory allocation for pointers, by MALLOC
COMPLEX Z REAL X, Y POINTER ( P1, X ), ( P2, Y ), ( P3, Z ) ... P1 = MALLOC ( 10000 ) ...
In the above example, MALLOC() allocates 10,000 bytes of memory and associates the address of that block of memory with the pointer P1.
The subroutine FREE() deallocates a region of memory previously allocated by MALLOC(). The argument given to FREE() must be a pointer previously returned by MALLOC(), but not already given to FREE(). The memory is returned to the memory manager, making it unavailable to the programmer.
POINTER ( P1, X ), ( P2, Y ), ( P3, Z ) ... P1 = MALLOC ( 10000 ) ... CALL FREE ( P1 ) ...
In the above example, MALLOC() allocates 10,000 bytes of memory, which are associated with pointer P1. FREE() later returns those same 10,000 bytes to the memory manager.
Here are some special considerations when working with pointers and memory allocation with malloc(), loc(), and free():
The pointers are of type integer, and are automatically typed that way by the compiler. You must not type them yourself.
A pointer-based variable cannot itself be a pointer.
The pointer-based variables can be of any type, including structures.
No storage is allocated when such a pointer-based variable is declared, even if there is a size specification in the type statement.
You cannot use a pointer-based variable as a dummy argument or in COMMON, EQUIVALENCE, DATA, or NAMELIST statements.
The dimension expressions for pointer-based variables must be constant expressions in main programs. In subroutines and functions, the same rules apply for pointer-based array variables as for dummy arguments--the expression can contain dummy arguments and variables in common. Any variables in the expressions must be defined with an integer value at the time the subroutine or function is called.
Address expressions cannot exceed the range of INTEGER*4 on 32-bit environments. If the expression is not in the range (-2147483648, 2147483647), then the results are unpredictable.
When compiling for 64-bit environments, use malloc64() to access the 64-bit address space. Routine malloc64() takes an INTEGER*8 argument and returns a 64-bit pointer value. In 64-bit programs, pointers defined by the POINTER statement are 64-bit INTEGER*8 values. See the Fortran Library Reference Manual and the malloc(3F) man pages.
Pointers have the annoying side effect of reducing the assumptions that the global optimizer can make. For one thing, compare the following:
Without pointers, if you call a subroutine or function, the optimizer knows that the call will change only variables in common or those passed as arguments to that call.
With pointers, this is no longer valid, since a routine can take the address of an argument and save it in a pointer in common for use in a subsequent call to itself or to another routine.
Therefore, the optimizer must assume that a variable passed as an argument in a subroutine or function call can be changed by any other call. Such an unrestricted use of pointers would degrade optimization for the vast majority of programs that do not use pointers.
There are two alternatives for optimization with pointers.
Do not use pointers with optimization level -O4.
Use a pointer only to identify the location of the data for calculations and pass the pointer to a subprogram. Almost anything else you do to the pointer can yield incorrect results.
The second choice also has a suboption: localize pointers to one routine and do not optimize it, but do optimize the routines that do the calculations. If you put the calling the routines on different files, you can optimize one and not optimize the other.
Example: A relatively "safe" kind of coding with -O3 or -O4:
REAL A, B, V(100,100) This programming unit does POINTER ( P, V ) nothing else with P other than P = MALLOC(10000) getting the address and passing it. ... CALL CALC ( P, A ) ... END SUBROUTINE CALC ( ARRAY, X ) ... RETURN END
If you want to optimize only CALC at level -O4, then avoid using pointers in CALC.
Any of the following coding practices, and many others, could cause problems with an optimization level of -O3 or -O4:
A program unit does arithmetic with the pointer.
A subprogram saves the address of any of its arguments between calls.
A function returns the address of any of its arguments, although it can return the value of a pointer argument.
A variable is referenced through a pointer, but the address of the variable is not explicitly taken with the LOC() or MALLOC() functions.
Example: Code that could cause trouble with -O3 or -O4:
COMMON A, B, C POINTER ( P, V ) P = LOC(A) + 4 Possible problems here if optimized ...
The compiler assumes that a reference through P may change A, but not B; this assumption could produce incorrect code.