(char) 指针的 GCC 优化没有意义
GCC optimization of (char) pointers makes no sense
我正在为 STM32 MCU 项目使用 STM32 Cube IDE 1.8.0 版。
生成器设置是默认的。它设置为“外部构建器”。
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for STM32 9-2020-q2-update.20201001-1621) 9.3.1 20200408 (release)
Copyright (C) 2019 Free Software Foundation, Inc.
Properties->C/C++ Build->Settings->Tool Settings MCU GCC compiler command为gcc -c
,All options内容为
-mcpu=cortex-m4 -std=gnu11 '-D__weak=__attribute__((weak))' '-D__packed=__attribute__((__packed__))' -DUSE_HAL_DRIVER -DSTM32F412Rx -c -I../Drivers/STM32F4xx_HAL_Driver/Inc -I../Drivers/STM32F4xx_HAL_Driver/Inc/Legacy -I../Middlewares/Third_Party/FreeRTOS/Source/include -I../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2 -I../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F -I../Middlewares/ST/STM32_USB_Device_Library/Core/Inc -I../Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc -I../Drivers/CMSIS/Device/ST/STM32F4xx/Include -I../Drivers/CMSIS/Include -Os -ffunction-sections -fdata-sections -Wall -fstack-usage --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -mthumb
关注-Os
!
现在,如果我使用以下代码(片段)
bool ESP32_WifiCommCmds_ConnectToAP ( char* ssid, char* password, char* mac )
{
char cmd [ 96 ] = { 0 };
char *ssidEscaped, *passwordEscaped;
ESP32_ModuleCommCmds_EscapeSpecialCharacters ( ssid, &ssidEscaped );
if ( ssidEscaped == NULL )
{
return false;
}
ESP32_ModuleCommCmds_EscapeSpecialCharacters ( password, &passwordEscaped );
if ( passwordEscaped == NULL )
{
free ( ssidEscaped );
return false;
}
if ( strlen(ssidEscaped) + strlen(passwordEscaped) + strlen(mac) + 17 > sizeof(cmd) )
{
free ( ssidEscaped );
free ( passwordEscaped );
return false;
}
// THE FOLLOWING IF ELSE WILL GET OPTIMIZED !!!!!!
if ( mac == NULL )
{
// THIS WILL GET OPTIMIZED OUT
sprintf ( cmd, "AT+CWJAP=\"%s\",\"%s\",,,1", ssidEscaped, passwordEscaped );
}
else
{
// THIS WILL STAY
sprintf ( cmd, "AT+CWJAP=\"%s\",\"%s\",\"%s\",,1", ssidEscaped, passwordEscaped, mac );
}
free ( ssidEscaped );
free ( passwordEscaped );
char buffer [ 48 ] = { 0 };
// Send cmd to ESP is left out here, for simplification
}
并且在业务逻辑中,只调用这个函数:
success = ESP32_WifiCommCmds_ConnectToAP ( data->ssid, data->password, NULL );
有人建议,优化后的代码将删除 if ( mac == NULL )
,因为它始终是 true
。但事实上恰恰相反。
问题出在strlen(mac)
这一行,但我不知道为什么会这样。我需要解释,因为我担心无论我在这里做错什么,都不会影响我的其余代码。
PS:如果您想自己检查,只需搜索生成的二进制文件以包含“AT+CWJAP=...”字符串。只有其中一个在那里(除非你关闭优化)。
如果您调用 strlen(mac)
,则允许编译器假定 mac
永远不会为 null。
推理是这样的:
A) 如果 mac 不为空,那么它假设正确并且它基于该假设所做的一切都是正确的。
B) 如果 mac 为空,那么您调用 strlen(NULL)
会触发未定义的行为。由于未定义的行为意味着它绝对可以做任何事情,所以它所做的任何事情都必须是正确的。
因此,无论调用此函数时的参数是什么,假设 mac 不为 null 的实现都是正确的。
请注意,这一切都发生在编译函数的实现时,它永远不会像您想象的那样查看您在调用函数的代码中所做的事情。
我正在为 STM32 MCU 项目使用 STM32 Cube IDE 1.8.0 版。 生成器设置是默认的。它设置为“外部构建器”。
arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for STM32 9-2020-q2-update.20201001-1621) 9.3.1 20200408 (release) Copyright (C) 2019 Free Software Foundation, Inc.
Properties->C/C++ Build->Settings->Tool Settings MCU GCC compiler command为gcc -c
,All options内容为
-mcpu=cortex-m4 -std=gnu11 '-D__weak=__attribute__((weak))' '-D__packed=__attribute__((__packed__))' -DUSE_HAL_DRIVER -DSTM32F412Rx -c -I../Drivers/STM32F4xx_HAL_Driver/Inc -I../Drivers/STM32F4xx_HAL_Driver/Inc/Legacy -I../Middlewares/Third_Party/FreeRTOS/Source/include -I../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS_V2 -I../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F -I../Middlewares/ST/STM32_USB_Device_Library/Core/Inc -I../Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc -I../Drivers/CMSIS/Device/ST/STM32F4xx/Include -I../Drivers/CMSIS/Include -Os -ffunction-sections -fdata-sections -Wall -fstack-usage --specs=nano.specs -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -mthumb
关注-Os
!
现在,如果我使用以下代码(片段)
bool ESP32_WifiCommCmds_ConnectToAP ( char* ssid, char* password, char* mac )
{
char cmd [ 96 ] = { 0 };
char *ssidEscaped, *passwordEscaped;
ESP32_ModuleCommCmds_EscapeSpecialCharacters ( ssid, &ssidEscaped );
if ( ssidEscaped == NULL )
{
return false;
}
ESP32_ModuleCommCmds_EscapeSpecialCharacters ( password, &passwordEscaped );
if ( passwordEscaped == NULL )
{
free ( ssidEscaped );
return false;
}
if ( strlen(ssidEscaped) + strlen(passwordEscaped) + strlen(mac) + 17 > sizeof(cmd) )
{
free ( ssidEscaped );
free ( passwordEscaped );
return false;
}
// THE FOLLOWING IF ELSE WILL GET OPTIMIZED !!!!!!
if ( mac == NULL )
{
// THIS WILL GET OPTIMIZED OUT
sprintf ( cmd, "AT+CWJAP=\"%s\",\"%s\",,,1", ssidEscaped, passwordEscaped );
}
else
{
// THIS WILL STAY
sprintf ( cmd, "AT+CWJAP=\"%s\",\"%s\",\"%s\",,1", ssidEscaped, passwordEscaped, mac );
}
free ( ssidEscaped );
free ( passwordEscaped );
char buffer [ 48 ] = { 0 };
// Send cmd to ESP is left out here, for simplification
}
并且在业务逻辑中,只调用这个函数:
success = ESP32_WifiCommCmds_ConnectToAP ( data->ssid, data->password, NULL );
有人建议,优化后的代码将删除 if ( mac == NULL )
,因为它始终是 true
。但事实上恰恰相反。
问题出在strlen(mac)
这一行,但我不知道为什么会这样。我需要解释,因为我担心无论我在这里做错什么,都不会影响我的其余代码。
PS:如果您想自己检查,只需搜索生成的二进制文件以包含“AT+CWJAP=...”字符串。只有其中一个在那里(除非你关闭优化)。
如果您调用 strlen(mac)
,则允许编译器假定 mac
永远不会为 null。
推理是这样的:
A) 如果 mac 不为空,那么它假设正确并且它基于该假设所做的一切都是正确的。
B) 如果 mac 为空,那么您调用 strlen(NULL)
会触发未定义的行为。由于未定义的行为意味着它绝对可以做任何事情,所以它所做的任何事情都必须是正确的。
因此,无论调用此函数时的参数是什么,假设 mac 不为 null 的实现都是正确的。
请注意,这一切都发生在编译函数的实现时,它永远不会像您想象的那样查看您在调用函数的代码中所做的事情。