This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author vstinner
Recipients alex, christian.heimes, erlendaasland, petr.viktorin, pitrou, serhiy.storchaka, skrah, vstinner
Date 2021-12-09.08:46:22
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1639039582.57.0.151188424716.issue45459@roundup.psfhosted.org>
In-reply-to
Content
Example:
---
struct Point { int x; int y; int z; };

int main()
{
    struct Point p = {1};
    return p.y;
}
---

gcc -O0 produces this machine code which sets p.y to 0 and p.z to 0:
---
Dump of assembler code for function main:
   0x0000000000401106 <+0>:	push   rbp
   0x0000000000401107 <+1>:	mov    rbp,rsp
   0x000000000040110a <+4>:	mov    QWORD PTR [rbp-0xc],0x0
   0x0000000000401112 <+12>:	mov    DWORD PTR [rbp-0x4],0x0
   0x0000000000401119 <+19>:	mov    DWORD PTR [rbp-0xc],0x1
   0x0000000000401120 <+26>:	mov    eax,DWORD PTR [rbp-0x8]
   0x0000000000401123 <+29>:	pop    rbp
   0x0000000000401124 <+30>:	ret    
---

gcc -O3 heavily optimize the code, it always return 0, it doesn't return a random value from the stack:
---
(gdb) disassemble main
Dump of assembler code for function main:
   0x0000000000401020 <+0>:	xor    eax,eax
   0x0000000000401022 <+2>:	ret    
---

The "C99 Standard 6.7.8.21" says:

    If there are fewer initializers in a brace-enclosed list than there are elements or members of an aggregate, or fewer characters in a string literal used to initialize an array of known size than there are elements in the array, the remainder of the aggregate shall be initialized implicitly the same as objects that have static storage duration.

The C99 standard says that p.y and p.z must be set to 0.

I'm talking about the specific C syntax of a structure static initialization: "struct MyStruct x = {...};".


If "Py_buffer data = {NULL, NULL};" is allocated on the stack, all "data" Py_buffer members are set to 0 or NULL:

typedef struct bufferinfo {
    void *buf;
    PyObject *obj;        /* owned reference */
    Py_ssize_t len;
    Py_ssize_t itemsize;  /* This is Py_ssize_t so it can be
                             pointed to by strides in simple case.*/
    int readonly;
    int ndim;
    char *format;
    Py_ssize_t *shape;
    Py_ssize_t *strides;
    Py_ssize_t *suboffsets;
    void *internal;
} Py_buffer;


If we want to add a version member to this structure, I would suggest to enforce the usage of a static initialization macro or an initialization function, like:
  "Py_buffer data; PyBuffer_Init(&data);"
or
  "Py_buffer data = PyBuffer_STATIC_INIT;"

The problem of the macro is that it is not usable on Python extensions was are not written in C or C++ (or more generally to extensions which cannot use macros).

--

A different approach is to use an API which allocates a Py_buffer on the heap memory, so if the structure becomes larger tomorrow, an old built C extensions continues to work:

Py_buffer *data = PyBuffer_New();
// ... use *data ...
PyBuffer_Free(data);

PyBuffer_New() can initialize the version member and allocates the proper memory block size.

The best is if the "... use *data ..." part is only done with function calls :-)
History
Date User Action Args
2021-12-09 08:46:22vstinnersetrecipients: + vstinner, pitrou, christian.heimes, alex, petr.viktorin, skrah, serhiy.storchaka, erlendaasland
2021-12-09 08:46:22vstinnersetmessageid: <1639039582.57.0.151188424716.issue45459@roundup.psfhosted.org>
2021-12-09 08:46:22vstinnerlinkissue45459 messages
2021-12-09 08:46:22vstinnercreate