On 2017-09-15 09:10+0100 Phil Rosenberg wrote:

The output [from C++ example code you provided] is
0,1,2,3
0,1,2,3
00000000,00000001

So this shows that a static 2d array is really just a pointer, not a
double pointer. One of the features of C is that you are permitted to
cast a pointer to one type to a pointer to any other type. You need
may need this to get to the bit level of binary data, e.g. swapping
endianness - read in a word, cast a pointer to that word to a pointer
to a char, swap the byte order. For zCast2 we are casting a pointer to
an int, to a pointer to a pointer to an int. Prefectly acceptable to
the compiler, but it is our responsibility to ensure this is sensible.
In the commented out line, we go to zCast[0], find a value of
00000000, i.e. a NULL pointer, so by trying to get the zeroth element
of this array we are trying to do *(NULL+0) which of course gives a
segfault.

I have no idea why the warning you have in GCC. You are right, it
looks like it implies z is a pointer to an array. But I am certain
that it won't be.

My understanding is that the semantics for pointers and arrays are
different for C than they are for C++ so I think we should stick to C
to draw any conclusions.

So here is a C version of your example code which confirms that
casting to int ** leads to an invalid read and segfault.
However, this same example also shows zStatic can be cast to a
pointer to an array with absolutely no issues!

// C programme to test casts of a statically allocated array

#include<stdio.h>
void main(void)
{
  int zStatic[2][3];
  int* zCast = (int*)zStatic;
  int (*zCast2)[3] = zStatic;
  int ** zCast3 = (int **)zCast2;
  zStatic[0][0] = 0;
  zStatic[0][1] = 1;
  zStatic[0][2] = 2;
  zStatic[1][0] = 3;
  zStatic[1][1] = 4;
  zStatic[1][2] = 5;

  printf("%d, %d, %d, %d, %d, %d\n", zStatic[0][0], zStatic[0][1], 
zStatic[0][2], zStatic[1][0], zStatic[1][1], zStatic[1][2]);
  printf("%d, %d, %d, %d, %d, %d\n", zCast[0], zCast[1], zCast[2], zCast[3], 
zCast[4], zCast[5]);
  printf("%d, %d, %d, %d, %d, %d\n", zCast2[0][0], zCast2[0][1], zCast2[0][2], 
zCast2[1][0], zCast2[1][1], zCast2[1][2]);
  printf("%d, %d, %d, %d, %d, %d\n", zCast3[0][0], zCast3[0][1], zCast3[0][2], 
zCast3[1][0], zCast3[1][1], zCast3[1][2]);
}

irwin@raven> gcc -g test_2d_static.c irwin@raven> ./a.out
0, 1, 2, 3, 4, 5
0, 1, 2, 3, 4, 5
0, 1, 2, 3, 4, 5
Segmentation fault
irwin@raven> valgrind ./a.out
==15290== Memcheck, a memory error detector
==15290== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==15290== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==15290== Command: ./a.out
==15290== 0, 1, 2, 3, 4, 5
0, 1, 2, 3, 4, 5
0, 1, 2, 3, 4, 5
==15290== Invalid read of size 4
==15290==    at 0x400640: main (test_2d_static.c:20)
==15290==  Address 0x30000000a is not stack'd, malloc'd or (recently) free'd
==15290== ==15290== ==15290== Process terminating with default action of signal 11 (SIGSEGV)
==15290==  Access not within mapped region at address 0x30000000A
==15290==    at 0x400640: main (test_2d_static.c:20)
==15290==  If you believe this happened as a result of a stack
==15290==  overflow in your program's main thread (unlikely but
==15290==  possible), you can try to increase the size of the
==15290==  main thread stack using the --main-stacksize= flag.
==15290==  The main thread stack size used in this run was 8388608.
==15290== ==15290== HEAP SUMMARY:
==15290==     in use at exit: 0 bytes in 0 blocks
==15290==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==15290== ==15290== All heap blocks were freed -- no leaks are possible ==15290== ==15290== For counts of detected and suppressed errors, rerun with: -v
==15290== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
Segmentation fault

If you comment out the last printf, then the valgrind result is

==15299== Memcheck, a memory error detector
==15299== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et
al.
==15299== Using Valgrind-3.10.0 and LibVEX; rerun with -h for
copyright info
==15299== Command: ./a.out
==15299== 0, 1, 2, 3, 4, 5
0, 1, 2, 3, 4, 5
0, 1, 2, 3, 4, 5
==15299== ==15299== HEAP SUMMARY:
==15299==     in use at exit: 0 bytes in 0 blocks
==15299==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==15299== ==15299== All heap blocks were freed -- no leaks are possible ==15299== ==15299== For counts of detected and suppressed errors, rerun with: -v
==15299== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

i.e., perfect!

So the conclusion appears to be a cast to a pointer to a pointer leads
to invalid reads/segfaults, but a cast to a pointer to an array works
fine.  It is the strong C distinction between array and pointer in this
context that I am trying to understand at this stage.

I am currently leaning towards the following tentative explanation:

int zStatic[2][3];

and

int (*zCast2)[3];

are actually identical types that describe a single block of memory
containing 6 ints and NOTHING else.  So the second version isn't
actually a pointer to an array in the Iliffe sense where there is a
block of memory containing the addresses of the various row vectors.
But for both types above the "3" does give the essential information
to the compiler so that subsequent references to zStatic[0][2] or
zCast2[0][2] "just work".

Does that explanation of what is going on make sense to you?

Anyhow, my best guess at this stage is I will be able to confirm this
tentative explanation (no block of memory that contains addresses with
the statically allocated array) with gdb results, but we will see.

There are two more related topics.

You appear not to feel strongly about either of these so here is
what I am going to do for each of them.

I. Deprecation of plshade1?

Although we have not yet reached a final conclusion about the above
results, if gdb confirms my tentative explanation, then that means we
will never be able to use plshade with a statically allocated z array.
Therefore, in this case I am going to make plshade1 permanently into a
C-only function that is not part of our common API and which is mostly
there as an illustrative exercise for C alone that should not be
propagated to our bindings. Currently from find/grep results the only
bindings that mention (pl)shade1 are c++, ada, and possibly octave and
the only examples that mention it are c and ada. So once the
confirmation happens, I plan to drop (pl)shade1 from these remaining
bindings, and for our c and ada examples I plan to replace plshade1
calls with plshade.

II. Deprecation of historical C++ API variants [concerning the
distinction between PLINT and bool arguments]?

I am going ahead with this backwards-incompatible change as described 
previously.

Alan
__________________________
Alan W. Irwin

Astronomical research affiliation with Department of Physics and Astronomy,
University of Victoria (astrowww.phys.uvic.ca).

Programming affiliations with the FreeEOS equation-of-state
implementation for stellar interiors (freeeos.sf.net); the Time
Ephemerides project (timeephem.sf.net); PLplot scientific plotting
software package (plplot.sf.net); the libLASi project
(unifont.org/lasi); the Loads of Linux Links project (loll.sf.net);
and the Linux Brochure Project (lbproject.sf.net).
__________________________

Linux-powered Science
__________________________

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Plplot-devel mailing list
Plplot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/plplot-devel

Reply via email to