Allocazione dinamica: confronto tra ALLOCATABLE e POINTER

Le funzionalità delle variabili di attributo ALLOCATABLE hanno molto in comune con le funzionalità delle variabili POINTER, e in particolare ne costituiscono un sottoinsieme.

Quando è possibile è meglio comunque usare variabili ALLOCATABLE, su cui il compilatore è in grado effettuare un maggior numero di ottimizzazioni.

È da notare che i puntatori in F90 hanno, volutamente, delle funzionalità molto ristrette rispetto ai puntatori in C, in particolare non è possibile ottenere il valore numerico (indirizzo in memoria) del puntatore, eseguire su di esso operazioni aritmetiche, e, nelle espressioni, le variabili di attributo POINTER sono trattate come variabili ordinarie, cioè rappresentano il valore a cui il puntatore punta e non il valore del puntatore stesso come accade invece in C.


PROGRAM pointer_cf
INTEGER :: a = 5
INTEGER, POINTER :: pa

pa => a
PRINT*,a ! stampo a: 5
PRINT*,pa ! stampo pa: sempre 5!!
pa = pa + 3

END PROGRAM pointer_cf
#include <stdio.h>
void main(void) {
int a = 5;
int *pa;

pa = &a;
printf("%d\n", a); /* stampo a: 5 */
printf("%d\n", *pa); /* stampo *pa: 5 */
*pa = *pa + 3;

}
L'unico caso in cui una variabile POINTER si comporta da vero puntatore è il caso in cui un parametro di un sottoprogramma ha l'attributo POINTER ed esiste un'interfaccia implicita o esplicita a tale unità di sottoprogramma; in tal caso il corrispondente parametro nell'unità chiamante dovrà pure avere l'attributo POINTER e l'unità di programma chiamata è in grado, oltre che di cambiare il valore della variabile in questione, anche cambiarne lo stato di associazione, ad esempio allocandola o deallocandola.

L'analogo in C del parametro di un sottoprogramma con l'attributo POINTER è il passaggio di un puntatore ad un puntatore.



SUBROUTINE modif_pointer(p, n)
INTEGER, POINTER :: p(:)
INTEGER :: n

ALLOCATE(p(n))
p = 0

END SUBROUTINE modif_pointer


PROGRAM pointer_cf
INTEGER, POINTER :: p
INTERFACE
  SUBROUTINE modif_pointer(p, n)
  INTEGER, POINTER :: p(:)
  INTEGER :: n
END INTERFACE

CALL modif_pointer(p, 10)

END PROGRAM pointer_cf


SUBROUTINE non_va()
INTEGER, POINTER :: p

CALL modif_pointer(p, 10)

END SUBROUTINE non_va


SUBROUTINE non_compila_neanche(p)
INTEGER :: p(:)
INTERFACE
  SUBROUTINE modif_pointer(p, n)
  INTEGER, POINTER :: p(:)
  INTEGER :: n
END INTERFACE

CALL modif_pointer(p, 20)

END SUBROUTINE non_compila_neanche
#include <malloc.h>

void modif_pointer(int **p, int n) {
int i;


*p = malloc(n*sizeof(int));
for(i=0; i<n; i++) **p = 0;

}


void main(void) {
int *p;






modif_pointer(&p, 10);

}


non_va_1() {
int *p;

modif_pointer(p, 10);

}













Si fa notare che nella maggior parte dei casi, cioè quando una subroutine intende semplicemente usare e/o modificare il valore di una parametro avente eventualmente l'attributo POINTER e non modificarne lo stato di associazione, è non solo inutile, ma anche dannoso aggiungere l'attributo POINTER a tale parametro.

MODULE sette_e_quaranta

CONTAINS

SUBROUTINE usa_pointer(p)
INTEGER :: p(:)

p = 0

END SUBROUTINE usa_pointer


SUBROUTINE funziona_perfettamente_1()
INTEGER :: p(10)

CALL usa_pointer(p)

END SUBROUTINE funziona_perfettamente_1


SUBROUTINE funziona_perfettamente_2()
INTEGER, POINTER :: p(:)

ALLOCATE(p(37))
CALL usa_pointer(p)

END SUBROUTINE funziona_perfettamente_2

END MODULE sette_e_quaranta