// 全局/靜態變量存儲查詢結果或連接狀態
MYSQL_RES *result;
MYSQL_ROW row;
int login_ok = 0;
void handle_client(int sockfd) {
char buf[1024];
while (recv(sockfd, buf, sizeof(buf), 0) > 0) {
// 解析用戶名密碼
// ...
// 執行查詢
mysql_query(conn, sql);
result = mysql_store_result(conn); // 未檢查返回值!
row = mysql_fetch_row(result); // 若上次查詢失敗,result 可能仍指向舊結果集!
if (row && strcmp(row[0], "1") == 0) {
login_ok = 1;
send(sockfd, "OK", 2, 0);
} else {
login_ok = 0;
send(sockfd, "FAIL", 4, 0);
}
// 忘記 mysql_free_result(result) → 內存洩漏、結果集殘留
}
}
| 錯誤操作 | 後果 |
|---|---|
查詢失敗未檢查 mysql_query 返回值 | 繼續執行 mysql_store_result 返回 NULL 或舊指針 |
未調用 mysql_free_result(result) | 結果集內存洩漏,下次查詢可能讀到殘留數據 |
全局 MYSQL_RES *result 被多線程/多客戶端共用 | 線程不安全,數據交叉覆蓋 |
預處理語句 MYSQL_STMT 未 mysql_stmt_close | 參數綁定殘留,導致後續綁定錯誤 |
int verify_login(MYSQL *conn, const char *user, const char *pass) {
// 1. 使用預處理語句防注入、提高性能
const char *sql = "SELECT 1 FROM users WHERE username=? AND password=? LIMIT 1";
MYSQL_STMT *stmt = mysql_stmt_init(conn);
if (!stmt) return -1;
if (mysql_stmt_prepare(stmt, sql, strlen(sql)) != 0) {
mysql_stmt_close(stmt);
return -1;
}
// 2. 綁定參數
MYSQL_BIND bind[2];
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = (char*)user;
bind[0].buffer_length = strlen(user);
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = (char*)pass;
bind[1].buffer_length = strlen(pass);
mysql_stmt_bind_param(stmt, bind);
// 3. 執行
if (mysql_stmt_execute(stmt) != 0) {
mysql_stmt_close(stmt);
return -1;
}
// 4. 綁定結果
MYSQL_BIND res_bind;
memset(&res_bind, 0, sizeof(res_bind));
int exists = 0;
res_bind.buffer_type = MYSQL_TYPE_LONG;
res_bind.buffer = &exists;
mysql_stmt_bind_result(stmt, &res_bind);
// 5. 獲取結果
int ret = mysql_stmt_fetch(stmt); // 0=成功, 1=無數據, 其他=錯誤
// 6. **關鍵:釋放資源**
mysql_stmt_close(stmt);
return (ret == 0 && exists) ? 1 : 0;
}
mysql_query / mysql_stmt_execute **必檢返回值**。mysql_store_result / mysql_use_result **必配 mysql_free_result**。MYSQL *conn **不要跨線程共享**,每線程各自 mysql_init / mysql_real_connect。mysql_options(conn, MYSQL_REPORT_DATA_TRUNCATION, ...) 或查看服務端 general_log。gcc -fsanitize=address -g。「第一次對,第二次錯」經典癥狀指向 **MySQL 結果集/語句資源未正確釋放**,導致後續操作讀到髒數據或操作失效句柄。嚴格遵循「申請→使用→釋放」閉環,改用預處理語句,問題即刻消失。