What does idf.py menuconfig change, and how does a setting chosen there reach the C code?
It edits the project's sdkconfig file. At build time sdkconfig becomes the header sdkconfig.h, whose CONFIG_… macros the C code includes, so a menu choice turns into a compile-time constant.
* A setting travels from a component's Kconfig file through menuconfig and sdkconfig into a C macro. *
Each ESP-IDF component (FreeRTOS, Wi-Fi, LWIP, the logging library, your own) ships a Kconfig file that declares its options, their types and defaults. menuconfig gathers all of them into one terminal menu: Component config → FreeRTOS → Kernel, for example. Saving writes your choices to sdkconfig as lines like CONFIG_FREERTOS_HZ=100.
The build then generates build/config/sdkconfig.h with one #define per option. That is why the code examples start with #include "sdkconfig.h", and why the blink demo can write #define BLINK_GPIO CONFIG_BLINK_GPIO: the pin number is chosen in the menu, not hard-coded.
Because the values are compile-time macros, a change in menuconfig only takes effect after a rebuild and reflash. Options that are switched off are left out of the build, which keeps the firmware small.
Go deeper:
ESP-IDF — Project Configuration (Kconfig) — how menuconfig,
sdkconfigandsdkconfig.hfit together.ESP-IDF — Configuration options reference — every
CONFIG_option with its default.Wikipedia — Kconfig — the configuration language ESP-IDF borrowed from the Linux kernel, and its menuconfig front end.