如何正确打印指针指向的用户输入值?解决UART输入乱码问题
问题描述
尝试打印指针temp指向的键盘输入单字符时出现额外乱码,例如输入“p”会返回p��?4��?。需要解释原因并修改代码,使其仅打印输入的字符。涉及的代码来自ESP-IDF蓝牙BLE SPP客户端示例中的uart_task函数:
void uart_task(void *pvParameters) { uart_event_t event; char userInput [20]; for (;;) { //Waiting for UART event. if (xQueueReceive(spp_uart_queue, (void * )&event, (TickType_t)portMAX_DELAY)) { switch (event.type) { //Event of UART receving data case UART_DATA: if (event.size && (is_connect == true) && (db != NULL) && ((db+SPP_IDX_SPP_DATA_RECV_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))) { uint8_t * temp = NULL; temp = (uint8_t *)malloc(sizeof(uint8_t)*event.size); if(temp == NULL){ ESP_LOGE(GATTC_TAG, "malloc failed,%s L#%d\n", __func__, __LINE__); break; } memset(temp, 0x0, event.size); sprintf(userInput, "%s", temp); printf("User input =" "%s" "\n", temp); uart_read_bytes(UART_NUM_0,temp,event.size,portMAX_DELAY); esp_ble_gattc_write_char( spp_gattc_if, spp_conn_id, (db+SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle, event.size, temp, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); free(temp); } break; default: break; } } } vTaskDelete(NULL); }
原因分析
- 打印时机错误:
printf调用在uart_read_bytes之前,此时temp刚完成内存分配和清零,没有实际读取到用户输入的数据。用%s格式打印时,因为没有字符串结束符\0,printf会持续读取后续内存区域直到遇到\0,导致输出垃圾数据。 - 缺少字符串结束符:即使读取数据后,
temp的内存大小仅为event.size,刚好存输入的字符,没有额外空间存放\0,直接用%s打印依然会越界读取内存。 - 冗余代码:
sprintf(userInput, "%s", temp)完全无用,此时temp是空数据,且后续未使用userInput变量。
修复方案
方案1:添加字符串结束符(推荐)
- 将
uart_read_bytes移到printf之前,确保打印的是已读取的用户输入。 - 分配内存时多申请1字节空间,用于存放字符串结束符
\0,避免越界。 - 移除无用的
sprintf语句。 - 读取数据后手动添加
\0,保证%s打印正常。
修改后的代码:
void uart_task(void *pvParameters) { uart_event_t event; for (;;) { // 等待UART事件 if (xQueueReceive(spp_uart_queue, (void *)&event, (TickType_t)portMAX_DELAY)) { switch (event.type) { // UART接收数据事件 case UART_DATA: if (event.size && is_connect && db && ((db + SPP_IDX_SPP_DATA_RECV_VAL)->properties & (ESP_GATT_CHAR_PROP_BIT_WRITE_NR | ESP_GATT_CHAR_PROP_BIT_WRITE))) { // 多分配1字节存字符串结束符 uint8_t *temp = (uint8_t *)malloc(sizeof(uint8_t) * (event.size + 1)); if (!temp) { ESP_LOGE(GATTC_TAG, "malloc failed, %s L#%d\n", __func__, __LINE__); break; } memset(temp, 0x0, event.size + 1); // 初始化包括结束符位置 // 先读取UART数据 uart_read_bytes(UART_NUM_0, temp, event.size, portMAX_DELAY); // 添加字符串结束符 temp[event.size] = '\0'; // 打印输入内容 printf("User input = %s\n", temp); esp_ble_gattc_write_char(spp_gattc_if, spp_conn_id, (db + SPP_IDX_SPP_DATA_RECV_VAL)->attribute_handle, event.size, // 发送时用原始长度,不需要带结束符 temp, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE); free(temp); } break; default: break; } } } vTaskDelete(NULL); }
方案2:指定打印长度(无需修改内存分配)
如果不想额外分配内存,可直接用printf的指定长度格式打印,避免越界读取:
// 读取数据后,替换原printf语句为以下内容 printf("User input = %.*s\n", event.size, temp);
%.*s表示用event.size作为字符串的打印长度,无需添加\0即可正常输出输入内容。
内容的提问来源于stack exchange,提问作者Espcoder88
相关产品推荐
相关产品推荐

