From: Alan W. I. <ir...@be...> - 2017-09-15 20:09:53
|
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 __________________________ |