admin管理员组

文章数量:1194732

I have this source code:

#include <iostream>
#include <cstdint>
#include <cstring>

struct bt_data {
uint8_t type;
uint8_t data_len;
const uint8_t* data;
};


static const char* devName="LSM6DSR_Sensor";


    bt_data ad[] = {
        { .type = (0x01), .data_len = (sizeof((uint8_t []) { (0x02 | 0x04) })), .data = (const uint8_t *)(((uint8_t []) { (0x02 | 0x04) })), },
        { .type = (0x09), .data_len = (strlen(devName)-1), .data = (const uint8_t *)(devName), },

    };

int main() {

uint8_t d1[] { (0x02 | 0x04) };

bt_data ad3[] = {
        { .type = (0x01), .data_len = 1, .data = d1, },
        { .type = (0x09), .data_len = 14, .data = (const uint8_t *)devName, },
    };



    bt_data ad2[] = {
        { .type = (0x01), .data_len = (sizeof((uint8_t []) { (0x02 | 0x04) })), .data = (const uint8_t *)(((uint8_t []) { (0x02 | 0x04) })), },
        { .type = (0x09), .data_len = (strlen(devName)-1), .data = (const uint8_t *)(devName), },

    };

    std::cout << "global: " << sizeof(ad) / sizeof(ad[0]) << " " << ad[1].data << std::endl;
    std::cout << "local, extra: " << sizeof(ad3) / sizeof(ad3[0]) << " " << ad3[1].data << std::endl;
    std::cout << "local temp: " << sizeof(ad2) / sizeof(ad2[0]) << " " << ad2[1].data << std::endl;
    

}

it allocates different arrays of variable size in local and global scope. Note in particular, that the definition of ad and ad2 is exactly the same, but ad is in global scope.

The program output of GCC 12.2.0 is :

Program returned: 0
global: 2 LSM6DSR_Sensor
local, extra: 2 LSM6DSR_Sensor
local temp: 1

When running with GCC 14, I get this:

Program returned: 0
global: 2 LSM6DSR_Sensor
local, extra: 2 LSM6DSR_Sensor
local temp: 2 LSM6DSR_Sensor

Clang warns for ad2 that the temporary array is destroyed at the end of the expressions.

Question:

  1. Why is the temporary array not destroyed in the global scope?
  2. Does this depend on a particular C++ standard? Would it be different in C language, also for the local array?

I have this source code:

#include <iostream>
#include <cstdint>
#include <cstring>

struct bt_data {
uint8_t type;
uint8_t data_len;
const uint8_t* data;
};


static const char* devName="LSM6DSR_Sensor";


    bt_data ad[] = {
        { .type = (0x01), .data_len = (sizeof((uint8_t []) { (0x02 | 0x04) })), .data = (const uint8_t *)(((uint8_t []) { (0x02 | 0x04) })), },
        { .type = (0x09), .data_len = (strlen(devName)-1), .data = (const uint8_t *)(devName), },

    };

int main() {

uint8_t d1[] { (0x02 | 0x04) };

bt_data ad3[] = {
        { .type = (0x01), .data_len = 1, .data = d1, },
        { .type = (0x09), .data_len = 14, .data = (const uint8_t *)devName, },
    };



    bt_data ad2[] = {
        { .type = (0x01), .data_len = (sizeof((uint8_t []) { (0x02 | 0x04) })), .data = (const uint8_t *)(((uint8_t []) { (0x02 | 0x04) })), },
        { .type = (0x09), .data_len = (strlen(devName)-1), .data = (const uint8_t *)(devName), },

    };

    std::cout << "global: " << sizeof(ad) / sizeof(ad[0]) << " " << ad[1].data << std::endl;
    std::cout << "local, extra: " << sizeof(ad3) / sizeof(ad3[0]) << " " << ad3[1].data << std::endl;
    std::cout << "local temp: " << sizeof(ad2) / sizeof(ad2[0]) << " " << ad2[1].data << std::endl;
    

}

it allocates different arrays of variable size in local and global scope. Note in particular, that the definition of ad and ad2 is exactly the same, but ad is in global scope.

The program output of GCC 12.2.0 is :

Program returned: 0
global: 2 LSM6DSR_Sensor
local, extra: 2 LSM6DSR_Sensor
local temp: 1

When running with GCC 14, I get this:

Program returned: 0
global: 2 LSM6DSR_Sensor
local, extra: 2 LSM6DSR_Sensor
local temp: 2 LSM6DSR_Sensor

Clang warns for ad2 that the temporary array is destroyed at the end of the expressions.

Question:

  1. Why is the temporary array not destroyed in the global scope?
  2. Does this depend on a particular C++ standard? Would it be different in C language, also for the local array?
Share Improve this question asked Jan 24 at 11:31 Maximilian MatthéMaximilian Matthé 3382 silver badges12 bronze badges 8
  • Regarding a particular C++ standard: No C++ standard includes compound literals. That's only added as an extension in some implementations. You also have narrowing conversions in your initializer lists which is forbidden (except if not allowed by an extension). – Ted Lyngmo Commented Jan 24 at 11:37
  • this looks like a gcc bug that got fixed, fyi, ad[0] and ad2[0] have pointers to dangling temporaries but this shouldn't invoke UB if you never access them, changing the first row to nullptr instead of (const uint8_t *)(((uint8_t []) { (0x02 | 0x04) })) fixes the issue, so it is likely gcc bugged out due to all the illegal C casts of temporaries that could result in reading undefined stack memory that doesn't exist when constructing the object ... maybe gcc optimized out the first row when it realized it was UB to use it anyway ?! – Ahmed AEK Commented Jan 24 at 11:57
  • the temporary array IS destroyed in the global scope, you just never use ad[0] which has a dangling pointer, and AFAIK using it is UB any all C and C++ standards. – Ahmed AEK Commented Jan 24 at 12:04
  • thanks for the comments. The code originates from using the macro BT_DATA_BYTES from the Zephyr OS. So, does that mean that this macro is actually impossible to use reliably? – Maximilian Matthé Commented Jan 24 at 12:10
  • It is okay to use this macro, just never pass a temporary in that field and use static or non-temporart objects like ad3, never do ad1 or ad2 – Ahmed AEK Commented Jan 24 at 12:41
 |  Show 3 more comments

1 Answer 1

Reset to default 0

1. Why is the temporary array not destroyed in the global scope?

Since it is in global scope, it won't be destroyed until after main returns.

2a. Does this depend on a particular C++ standard?

The C++ standard won't matter. The compiler and its version will matter, as demonstrated with GCC.

Destruction is not guaranteed to happen at a specific time. The standard only says the destruction will occur sometime after it leaves scope, as determined by the compiler implementation.

2b. Would it be different in C language, also for the local array?

C doesn't have destructors. When you leave main, all the memory is left as it was for the OS to reuse.

Additional Thoughts

I wouldn't trust the output counts. Instrumenting constructors and destructors might provide meaningful information. I didn't do it because designated initializers cannot be used with constructors present.

I copied the code to Compiler Explorer and got many errors since I compiled it with -Wall, -Wpedantic, and -Werror. After removing those, I got the same results as you with different versions of CLang and GCC. Given all the errors, I wouldn't trust this code in production.

It bothered me that there was no output from ad3[1].data. On a whim, I replaced the division with std::size. The size is the same, except a garbled name is output. With the division the output stops after the output of the space. I guess data contains a null character, which shuts down the output to std::cout, but why does it work with size? I don't have a clue.

Ha! Rerunning it with GCC 12.2 outputs a garbled name. Here's the Compiler Explorer version.

本文标签: cUndefined behaviour Local vs global scope and pointer to temporary arrayStack Overflow