I compiled my model code successfully, however it gives me this error when I try to run it:
forrtl: severe (408): fort: (8): Attempt to fetch from allocatable variable COS_ZENITH when it is not allocated.
I checked several questions and answers online but I'm not sure how I should handle this problem in my case. I think the error message seems quite clear but I don't have enough experience with fortran so I don't know if it's a bug or I have to modify the code etc.
This is the module of the code that apparently gives trouble:
MODULE mo_zenith
!
! Description:
! <Say what this module is for>
!
! Current Code Owner: <Name of person responsible for this code>
!
! History:
!
! Version Date Comment
! ------- ---- -------
! <version> <date> Original code. <Your name>
!
! Code Description:
! Language: Fortran 90.
! Software Standards: "European Standards for Writing and
! Documenting Exchangeable Fortran 90 Code".
!
! Modules used:
!
USE mo_mpi, ONLY: p_parallel_io, p_bcast, p_io
USE mo_kind, ONLY: dp
USE mo_io_units, ONLY: nout
USE mo_exception, ONLY: finish
USE mo_jsbach_grid , ONLY: domain_type
IMPLICIT NONE
PRIVATE
PUBLIC :: compute_orbit_and_solar, init_zenith, cos_zenith
REAL(dp), ALLOCATABLE, SAVE :: cos_zenith(:) !< Cosine of solar zenith angle for domain
LOGICAL :: module_configured = .FALSE.
LOGICAL :: module_initialized = .FALSE.
CONTAINS
SUBROUTINE compute_orbit_and_solar(domain)
! Compute orbital parameters for current time step
! Called from *jsbalone_driver* at the beginning of each time step
! If coupled with ECHAM:
! this is done in *pre_radiation* from *mo_radiation*, called in *scan1*
! the cosine of zenith angle is passed from ECHAM to the interface
! the interface copies the packed zenith angle to *cos_zenith*
USE mo_time_control, ONLY: l_orbvsop87, get_orbit_times
USE mo_orbit, ONLY: orbit_kepler, orbit_vsop87
USE mo_radiation_parameters, ONLY: nmonth,solar_parameters
TYPE(domain_type), INTENT(in) :: domain
LOGICAL :: l_rad_call = .FALSE.
LOGICAL :: lyr_perp = .FALSE. !< Switch to specify perpetual vsop87 year
INTEGER :: yr_perp = -99999 !< year if lyr_perp == .TRUE.
REAL(dp) :: rasc_sun, decl_sun, dist_sun, orbit_date, time_of_day
REAL(dp) :: flx_ratio, cos_mu0(domain%nland,1), daylght_frc(domain%nland,1)
REAL(dp), DIMENSION(domain%nland,1) :: sinlon, sinlat, coslon, coslat
INTEGER :: nland
nland = domain%nland
IF (.NOT. module_initialized) CALL init_zenith(domain)
CALL get_orbit_times(l_rad_call, lyr_perp, nmonth, yr_perp, time_of_day, orbit_date)
IF (l_orbvsop87) THEN
CALL orbit_vsop87 (orbit_date, rasc_sun, decl_sun, dist_sun)
ELSE
CALL orbit_kepler (orbit_date, rasc_sun, decl_sun, dist_sun)
END IF
sinlon(:,1) = domain%sinlon(1:nland)
sinlat(:,1) = domain%sinlat(1:nland)
coslon(:,1) = domain%coslon(1:nland)
coslat(:,1) = domain%coslat(1:nland)
CALL solar_parameters(decl_sun, dist_sun, time_of_day &
,sinlon, sinlat, coslon, coslat &
,flx_ratio, cos_mu0, daylght_frc)
cos_zenith(1:nland) = cos_mu0(1:nland,1)
END SUBROUTINE compute_orbit_and_solar
!
!=================================================================================================
SUBROUTINE init_zenith(domain)
! Pre-compute some work quantities for domain
TYPE(domain_type), INTENT(in) :: domain
IF (module_initialized) RETURN
ALLOCATE(cos_zenith(domain%nland))
module_initialized = .TRUE.
END SUBROUTINE init_zenith
END MODULE mo_zenith
any idea?
Thank you in advance!
Related
I'm writing a HDF5 wrapper subroutine that will read/write a double precision array of any shape from/to a dataset inside a HDF5 file. To achieve this, I use some C pointer trickery such that the subroutine takes in only the first element of the array as val, but it actually reads/writes the whole array using the temporary buffer buf(1:sz_buf).
So far I have the following for the read subroutine (after removing error checks to keep it concise):
SUBROUTINE hdf5_read_array_d( fname, path, name, val, dims )
USE ISO_C_BINDING, ONLY: C_SIZE_T, C_LOC, C_F_POINTER
! Input arguments
CHARACTER(LEN=*), INTENT(IN) :: fname, path, name
REAL(KIND(1.D0)), TARGET, INTENT(OUT) :: val
INTEGER, DIMENSION(:), INTENT(IN) :: dims
! Internal variables
INTEGER(KIND=HID_T) :: h5root, h5path, h5dset
INTEGER(KIND=HSIZE_T), DIMENSION(SIZE(dims)) :: h5dims
REAL(KIND(1.D0)), DIMENSION(:), POINTER :: buf
INTEGER(KIND=C_SIZE_T) :: sz_buf
INTEGER :: dim
! Open the file in read-only mode
CALL h5fopen_f( TRIM(fname), H5F_ACC_RDONLY_F, h5root, ierr )
! Open the pre-existing path in the file as a group
CALL h5gopen_f( h5root, TRIM(path), h5path, ierr )
! Open the dataset
CALL h5dopen_f( h5path, TRIM(name), h5dset, ierr )
! Convert dims to HSIZE_T
h5dims(:) = dims(:)
! C pointer trickery: cast double -> void* -> double*
sz_buf = PRODUCT(dims)
ALLOCATE( buf( sz_buf ) )
CALL C_F_POINTER( C_LOC(val), buf, (/sz_buf/) )
! Read data from dataset through buffer
CALL h5dread_f( h5dset, H5T_NATIVE_DOUBLE, buf, h5dims, ierr )
! Clean up and close HDF5 file
NULLIFY(buf)
CALL h5dclose_f( h5dset, ierr )
CALL h5gclose_f( h5path, ierr )
CALL h5fclose_f( h5root, ierr )
RETURN
END SUBROUTINE hdf5_read_array_d
Now, the question is, do I need to also put in DEALLOCATE(buf) in addition to / in place of the NULLIFY(buf)?
Any help would be appreciated.
Note: I am aware that Fortran 2018 includes assumed-rank arrays val(..) that will elegantly solve this problem. But again, it's a newer feature that might not be implemented by all compilers yet.
Edit: On C_F_POINTER(), here's a screenshot of Metcalf, Reid, and Cohen (4th Edition, not the newest one that has Fortran 2018 stuff):
You can use C-style pointer trickery to do what you want, but you have some things to address in your approach:
you have a memory leak with allocate(buf)
you are (subtly) lying about the scalar nature of val
you'll horribly confuse anyone reading your code
The reason why this is horribly confusing, is because you don't need to do this trickery. That's also why I won't show you how to do it, or to address the question "do I need to deallocate as well as nullify?".
You know that you have an array val to stuff n values in, in a contiguous lump. You worry that that can't do that because you (without using an assumed-rank dummy) have to match array shape. Worry not.
integer :: a(2,2,2,2), b(4,2,2), c(4,4)
are all arrays with 16 elements. So is
integer :: d(16)
You can associate actual arguments a, b and c with dummy argument d. Let's see that in action:
implicit none
integer :: a(2,2,2,2), b(4,2,2), c(4,4)
call set_them(a, SHAPE(a))
call set_them(b, SHAPE(b))
call set_them(c, SHAPE(c))
print '(16I3)', a, b, c
contains
subroutine set_them(d, dims)
integer, intent(in) :: dims(:)
integer, intent(out) :: d(PRODUCT(dims))
integer i
d=[(i,i=1,SIZE(d))]
end subroutine
end program
You can even associate array sections in this way to define portions.
You can see several other questions around here about this sequence association, in particular looking at changing shapes of arrays. This answer is more of a motivation of what to look for when tempted to do something complicated instead.
This question already has an answer here:
Associated pointers in derived type? gFortran vs. Intel
(1 answer)
Closed 3 years ago.
I'm trying to learn the data-strcuture of a linked-list using Pointers in Fortran 90 on the platform of Win32/x86.
But when I runs the test-case that simply aims to build a list of 100 items, the program terminates with and error of 'Access violation'.
And the list is far from finished before the error occurs.
In debug run, it turns out that, at some point, the allocation of NEXT-Pointer fails.
I'm wondering why could this happen?
How could the linked-list be build successfully using pointers?
Codes are as follows:
TYPE Ptr
INTEGER :: I
TYPE(Ptr),POINTER :: PREV,NEXT
END TYPE Ptr
TYPE(Ptr),POINTER :: P
INTEGER :: IP,Err
ALLOCATE(P)
DO IP=1,100
P%I = IP
IF ( .NOT.ASSOCIATED(P%NEXT) ) THEN
ALLOCATE(P%NEXT, STAT=Err )
END IF
P%NEXT%PREV => P
P => P%NEXT
END DO
On UBUNTU with gfortran 7.2 your program compiled and ran just fine. There are, however two issues with your program, that may trip off your compiler:
After you allocate p the status of the pointers p%next and p%prev is undefined. It might be that your compiler then gets tripped on the statement IF(.not.associated(p%next)), as the status of p%next is undefined. Always nullify pointers before you use them or refer to them in any which way. Try:
program pointers
!
TYPE Ptr
INTEGER :: I
TYPE(Ptr),POINTER :: PREV,NEXT
END TYPE Ptr
!
TYPE(Ptr),POINTER :: P => NULL() ! Make sure all pointer are nullified
TYPE(Ptr),POINTER :: head => NULL()
TYPE(Ptr),POINTER :: temp => NULL()
INTEGER :: IP,Err
!
NULLIFY(P) ! This is identical to the nullify in the declaration statement
NULLIFY(head) ! I've added it to show both styles
NULLIFY(temp)
!
ALLOCATE(P)
NULLIFY(P%NEXT) ! Always nullify pointers
NULLIFY(P%PREV) ! Always nullify pointers
head => P ! To keep a header to your list is a good idea
!
DO IP=1,100
write(*,*) ip
P%I = IP*IP ! Trivial unimportant change
!
IF ( .NOT.ASSOCIATED(P%NEXT) ) THEN
ALLOCATE(P%NEXT, STAT=Err )
P%NEXT%I = ip+1 ! define entries
NULLIFY(P%NEXT%NEXT) ! for next node
NULLIFY(P%NEXT%PREV) ! for all elements of your TYPE
END IF
!
P%NEXT%PREV => P
P => P%NEXT
!
END DO
!
temp => head ! temporary pointer to navigate
DO WHILE( ASSOCIATED(temp) )
write(*,*) ' AT NODE ', temp%i
temp => temp%next
ENDDO
!
end program pointers
As pointed out by #Alexander Vogt, you loose the start of the list. I added a pointer called head to retain this beginning of the list. If you look at the output you will notice that the program actually makes a list of 100+1 entries, since you make p first, then you add 100 times a p%next.
[Working example at the end of this post!]
I'm trying to write a simple module to handle physical units in arithmetic operations. My aim is to create derived units out of primary ones.
As you can see in the following code, I have a derived type, namely unit_t, which stores a string, representing the unit itself, power of the unit, conversion factor (to convert it to SI), a logical variable to show if the unit is cloned and next and prev pointers which point to the next or previous unit (in case we have a combination of units, for example kg * m / s**2, so basically it's a linked list connecting different units to each other).
I have a function named unit_clone to clone a primary unit. The unit_int_pow function overloads the exponentiation operator (**) and it simply clones a given primary unit and updates its exponent. The units_mul function overloads the multiplication operator (*). This function first check if the two given units are cloned (if not, it clones them) and then just connect them using next and prev pointers.
Here is my code (you should be able to compile it with gfortran)
module units
implicit none
type unit_t
character(len=16) :: symb
integer :: pow
real :: conv
logical :: cloned
type(unit_t), pointer :: next => null(), prev => null()
end type unit_t
! definitions
type(unit_t), target :: m = unit_t("m", 1, 1.d0, .false.)
type(unit_t), target :: km = unit_t("km", 1, 1.d3, .false.)
type(unit_t), target :: kg = unit_t("kg", 1, 1.d0, .false.)
type(unit_t), target :: s = unit_t("s", 1, 1.d0, .false.)
interface operator (**)
procedure unit_int_pow
end interface operator (**)
interface operator (*)
procedure units_mul
end interface operator (*)
contains
!> Cloning a given node (unit)
function unit_clone(u) result (clone)
implicit none
type(unit_t), intent(in) :: u
type(unit_t), allocatable, target :: clone
allocate(clone)
clone%symb = u%symb
clone%conv = u%conv
clone%pow = u%pow
clone%cloned = .true.
clone%next => u%next
clone%prev => u%prev
end function unit_clone
!> integer powers
function unit_int_pow(u1, p) result(u)
implicit none
type(unit_t), intent(in) :: u1
integer, intent(in) :: p
type(unit_t), allocatable, target :: u
u = unit_clone(u1)
u%pow = u%pow * p
end function unit_int_pow
!> multiplication
function units_mul (u1, u2) result (u1c)
implicit none
type(unit_t), intent(in) :: u1, u2
type(unit_t), allocatable, target :: u1c, u2c
if ( u1%cloned ) then
u1c = u1
else
u1c = unit_clone(u1)
end if
if ( u2%cloned ) then
u2c = u2
else
u2c = unit_clone(u2)
end if
u2c%prev => u1c
u1c%next => u2c
end function units_mul
end module units
program test
use units
implicit none
type(unit_t) :: u
u = kg**2 * m
print *, u%symb, "^", u%pow, " [expected: kg^2]"
print *, u%next%symb, "^", u%next%pow, " [expected: m^1]"
print *, u%next%prev%symb, "^", u%next%prev%pow, " [expected: kg^2]"
end program test
The problem is, I'm getting the following output:
kg ^ 2 [expected: kg^2]
�ȷ2�U ^ 1 [expected: m^1]
�ȷ2�U ^ 2 [expected: kg^2]
Apparently, after accessing the next or next%prev unit (which is basically the head of this short linked list), the code outputs random character instead of the symbs. If I change the order of the variables in the derived type, unit_t, for example if I put symb at the end of the derived type, I will get right symbs, but this time wrong pows.
Any idea what is the culprit of this rather odd behavior?
Using Rudrigo's comment below, I rewrote the code, and it works fine now. Just for the reference, the working code is as follows (if you have further suggestion or modification, please let me know, Nombre respository)
module units
implicit none
type unit_t
character(len=16) :: symb
real :: conv
real :: pow = 1.d0
logical :: cloned = .false.
type(unit_t), pointer :: next => null(), prev => null()
end type unit_t
! units definitions
type(unit_t), target :: m = unit_t("m", 1.d0)
type(unit_t), target :: km = unit_t("km", 1.d3)
type(unit_t), target :: kg = unit_t("kg", 1.d0)
type(unit_t), target :: s = unit_t("s", 1.d0)
interface operator (**)
procedure unit_int_pow
end interface operator (**)
interface operator (*)
procedure units_mul
end interface operator (*)
contains
!> Cloning a given node (unit)
function unit_clone(u) result (clone)
implicit none
type(unit_t), intent(in) :: u
type(unit_t), pointer :: clone
allocate(clone)
clone%symb = trim(u%symb)
clone%conv = u%conv
clone%pow = u%pow
clone%cloned = .true.
clone%next => u%next
clone%prev => u%prev
end function unit_clone
!> integer powers
function unit_int_pow(u1, p) result(u)
implicit none
type(unit_t), intent(in) :: u1
integer, intent(in) :: p
type(unit_t), pointer :: u
if ( u1%cloned ) then
! TODO: should be able to handle complex cases like: a * (b * c)**3
! most likly, only updating the power of the linked list chain
! would do the job
else
u => unit_clone(u1)
end if
u%pow = u%pow * p
end function unit_int_pow
!> multiplication
function units_mul (u1, u2) result (u2c)
implicit none
type(unit_t), intent(in), target :: u1, u2
type(unit_t), pointer :: u2c
if ( u2%cloned ) then
if ( associated(u2%prev) ) then
u2c => u2%prev%next
else
u2c => u2
end if
else
u2c => unit_clone(u2)
end if
if ( u1%cloned ) then
if ( associated(u2%prev) ) then
u2c%prev => u1%prev%next
else
u2c%prev => u1
end if
else
u2c%prev => unit_clone(u1)
end if
u2c%prev%next => u2c
end function units_mul
end module units
A pointer in Fortran has three posible association status:
associated: the pointer is actually pointing to a defined and allocated variable / matching data storage (its target);
disassociated: it was (or is part of an objects that was) explicitly nullified or deallocated, or its target was properly disassociated.
undefined: anything different than the former, e.g. its target is (or became) undefined, or was deallocated by other means than by calling deallocate directly in the pointer itself, among other causes.
When execution of an instance of a subprogram completes (e.g. when function units_mul reaches end function), any unsaved local variable becomes undefined. Also, any allocatable local variable that is not saved or is a function result gets deallocated, and when an allocatable entity is deallocated, it also becomes undefined.
Back to your problem, u2c is an allocatable unsaved local variable inside units_mul function, where you associate u1c%next to it. When this function reaches the end, u2c ends its lifecycle and becomes undefined, bringing u1c%next to become also undefined, in a state referred in the Fortran lingo as dangling pointer.
This a text from the Fortran Standard describing this phenomena (even though it is referring to the case of modules host association, it's the same logic):
Note 19.10
A pointer from a module program unit might be accessible in a
subprogram via use association. Such pointers have a lifetime that is
greater than targets that are declared in the subprogram, unless such
targets are saved. Therefore, if such a pointer is associated with a
local target, there is the possibility that when a procedure defined
by the subprogram completes execution, the target will cease to exist,
leaving the pointer “dangling”. This document considers such pointers
to have an undefined association status. They are neither associated
nor disassociated. They cannot be used again in the program until
their status has been reestablished. A processor is not required to
detect when a pointer target ceases to exist.
A dangling pointer is not a reliable pointer, and the compiler has no control over it. They may, by any reason, keep pointing to their last memory address (and accidentally give the expected result in some cases, or the values would be gibberish from random memory address), but it will most certainly break, and the fail can be anything, from just wrong results to a SIGSEG fault or a memory address violation.
See this example code:
program dangling_pointer
implicit none
integer, pointer :: p(:)
integer, allocatable :: a(:)
call sub1(p)
print *, 'sub1: ', p
call sub2(p)
print *, 'sub2: ', p
call sub3(p, a)
print *, 'sub3: ', p
p => fun4()
print *, 'fun4: ', p
contains
subroutine sub1(dummy_p)
! the pointer passed as argument outlives the local target
! when the procedure ends, it becomes a "dangling pointer"
integer, pointer :: dummy_p(:)
integer, allocatable, target :: local_a(:)
allocate(local_a(5))
local_a = 100
dummy_p => local_a
end
subroutine sub2(dummy_p)
! here the local variable is saved, so it persists. No problem here.
integer, pointer :: dummy_p(:)
integer, allocatable, target, save :: saved_a(:)
allocate(saved_a(5))
saved_a = 100
dummy_p => saved_a
end
subroutine sub3(dummy_p, out_a)
! here the target is a passed argument, so it persists. No problem here.
integer, pointer :: dummy_p(:)
integer, allocatable, target :: out_a(:)
allocate(out_a(5))
out_a = 100
dummy_p => out_a
end
function fun4() result(result_p)
! here the function result will be returned as a pointer. No problem here.
integer, pointer :: result_p(:)
allocate(result_p(5))
result_p = 100
end
end
With gfortran 9.0.0 I get:
sub1: 14316208 0 14287184 0 100
sub2: 100 100 100 100 100
sub3: 100 100 100 100 100
fun4: 100 100 100 100 100
Edit
I think this snippet would work for your problem:
allocate(u1c%next)
if (u2%cloned) then
u1c%next = u2
else
u1c%next = unit_clone(u2)
end if
u1c%next%prev => u1c
I have a commercial C library which I want to use with Fortran. There are two functions and a pointer struct like:
struct env;
typedef struct env *ENVptr;
the two functions have the prototype:
ENVptr open(int *status_p);
int close(ENVptr **env_p);
I wrote an Fortran interface to access them:
interface
function c_open(status) bind(c, name='c_open')
use, intrinsic :: iso_c_binding
integer(kind = c_int) :: status
type(c_ptr) :: c_open
end function
function c_close(env) bind(c, name='c_close')
use, intrinsic :: iso_c_binding
type(c_ptr) :: env
integer(kind = c_int) :: c_close
end function
end interface
I use this code to use this in my Fortran program:
type(c_ptr) :: env = c_null_ptr
env = c_open(status)
if ( status .ne. 0 ) then
print *, 'Could not open environment'
stop
end if
...some more code...
if ( c_associated(env) ) then
status = c_close(env)
if ( status .ne. 0 ) then
print *, 'Could not close environment.'
end if
end if
but when I execute the program, I get an Segmentation fault error when the program reaches the c_close function.
Is this the right way to interface the C routines?
I don't see how your program could have linked because the binding name of a procedure has to agree with the name in the actual C prototype. I suppose you could square the names up with a *.def file. Also Fortran has a concept of argument keywords so it is good practice in my opinion to make the Fortran dummy arguments in the interface agree with their documented argument names. Other than that you seem to have the right level of indirection in your interface bodies, so my version would be:
interface
function c_open(status_p) bind(C,name='open')
use, intrinsic :: iso_c_binding
implicit none
type(c_ptr) :: c_open
integer(kind = c_int) status_p
end function c_open
function c_close(env_p) bind(c,name='close')
use, intrinsic :: iso_c_binding
implicit none
integer(c_int) c_close
type(c_ptr) env_p
end function c_close
end interface
Now, there is a problem with the level of indirection when you invoke c_close because the C typedef for ENVptr already makes it a pointer, so ENVptr** envp is a pointer to a pointer to a pointer. In your Fortran code you are passing a c_ptr that points at an opaque type by reference, so you are passing a pointer to a pointer. Thus you need to create an extra level of indirection to make it fly. I would accordingly attempt to modify your code to something like:
type(c_ptr) :: env = c_null_ptr, envpp = c_null_ptr
target env
integer(c_int) status
env = c_open(status)
if ( status .ne. 0 ) then
print *, 'Could not open environment'
stop
end if
!...some more code...
if ( c_associated(env) ) then
envpp = c_loc(env)
status = c_close(envpp)
if ( status .ne. 0 ) then
print *, 'Could not close environment.'
end if
end if
I can't test this obviously but at this point it's syntactically correct and has the right level of indirection according to my reading of your problem statement.
In Fortran, you can reshape arrays with pointers:
program working
implicit none
integer, dimension(:,:), pointer :: ptr
integer, dimension(6), target :: trg
trg = (/1,2,3,4,5,6/)
ptr(1:2,1:3) => trg
! Here, you can use the 6-sized 1D array trg
! or the 2 by 3-sized 2D array ptr at the same time.
! Both have the same content (6 integers), but are only indexed
! differently.
write(*,*) ptr(1,2)
end program working
This program writes "3", which is according to the reshape rules.
Similarly, I attempted to do the same, but not with 1D to 2D, but 0D to 1D.
program not_working
implicit none
integer, dimension(:), pointer :: ptr
integer, target :: trg
trg = 1
ptr(1:1) => trg
! I hoped to be able to use the scalar trg at the same time as
! the one-sized 1D array ptr. I thought they both have the same
! content, but are only indexed differently.
write(*,*) ptr(1)
end program not_working
I expected to see a "1". But it does not compile.
Gfortran 4.9 says:
Error: Rank remapping target must be rank 1 or simply contiguous at
(1)
Ifort 14.0.2 says:
<file>.f90: catastrophic error: Internal compiler error:
segmentation violation signal raised Please report this error along
with the circumstances in which it occurred in a Software Problem
Report. Note: File and line given may not be explicit cause of this
error. compilation aborted for <file>.f90 (code 1)
I do not understand how the scalar trg can be not contiguous and what the fundamental difference between the two example programs is.
The scalar is not simply contiguous array because it is not an array at all. It is as simple as that. Gfortran detects it and complains, ifort is confused and crashes. But your code is invalid, you cannot point an array pointer on a scalar.
Array pointers are desinged to point to arrays, so cannot point to scalars (instead, we can use scalar pointers for this purpose). But if we definitely want to use array pointers to point to scalars (for some reason), we could use c_f_pointer() such that
use iso_c_binding
integer, target :: a
integer, pointer :: p(:), q(:,:), r(:,:,:)
a = 777
call c_f_pointer( c_loc( a ), p, [1] )
call c_f_pointer( c_loc( a ), q, [1,1] )
call c_f_pointer( c_loc( a ), r, [1,1,1] )
print *, "p = ", p(:), "with shape", shape(p)
print *, "q = ", q(:,:), "with shape", shape(q)
print *, "r = ", r(:,:,:), "with shape", shape(r)
But this is clearly an "unsafe" feature (in the sense that it allows access to raw memory), and if used with wrong arguments, it could give a wrong result (or even disaster), for example:
call c_f_pointer( c_loc( a ), p, [3] )
print *, "p = ", p(:) !! gives "p = 777 202 0" etc with garbage data
So, unless there is some special reason, I think it is probably better (safer) to use scalar pointers for scalar variables...