调用mysql_init时覆盖字符数组的问题求助
这问题的核心是内存越界写入导致的未定义行为,咱们一步步拆解来看:
1. 最致命的错误:单个字符被当作字符串缓冲区使用
你在main里定义的char uid;是单个字符变量,它仅占用1字节内存,但你的get_uid函数却把它当成能存储多字符的字符串数组来写入。比如当argv[1]是389302-8232时,get_uid会往&uid(单个字符的内存地址)连续写入'3'、'8'、'9'、'3'、'0'、'2',最后再加'\0'——这一共是7字节的数据,远超uid的1字节容量,直接触发越界写入相邻内存区域。
这种越界属于C语言里的未定义行为:一开始输出正常只是运气好,相邻内存还没被修改;但当你调用mysql_init(&conn)时,MYSQL类型的conn变量刚好存在uid的相邻内存区域,初始化conn时覆盖了之前越界写入的字符,所以后续输出&uid就只剩下第一个字符了——后面的字节已经被conn的初始化数据冲掉。
2. 为什么调用mysql_close会触发段错误?
当你用单个char uid时,越界写入的内容已经破坏了MYSQL conn结构体的内部数据,导致mysql_close访问了非法内存地址,直接触发段错误。而改成malloc分配足够内存后,get_uid的写入不再越界,不会破坏其他变量的内存,自然就不会触发段错误了。
正确的修正方案
方案一:使用固定大小的字符数组(推荐,适合已知最大长度的场景)
如果能提前确定uid的最大长度,直接定义足够大的数组(要留1字节存字符串结束符'\0'):
// 假设uid最长不超过20个字符,定义21字节的数组 char uid[21]; get_uid(argv[1], uid);
方案二:动态分配内存(你当前使用的方式)
注意malloc时要额外加1字节,用来存储字符串结束符,避免再次越界:
// strlen(argv[1])是原字符串长度,+1是为了存'\0' char *uid = malloc(strlen(argv[1]) + 1); if (uid == NULL) { // 必须处理内存分配失败的情况 perror("malloc failed"); return 1; } get_uid(argv[1], uid); // 使用uid进行后续操作... free(uid);
额外优化:给get_uid加安全限制
可以给get_uid增加一个参数,限制目标缓冲区的最大长度,彻底避免即使传入数组也可能出现的越界:
void get_uid(char *src, char *dst, size_t max_dst_len) { int i, len, count = 0; len = strlen(src); // 循环条件增加缓冲区长度限制,预留1字节存'\0' for(i = 0; i != len && count < max_dst_len - 1; i++) { if(src[i] == '-') { break; } dst[count] = src[i]; count++; } dst[count] = '\0'; }
调用时传入缓冲区大小即可:
- 数组场景:
get_uid(argv[1], uid, sizeof(uid)); - 动态分配场景:
get_uid(argv[1], uid, strlen(argv[1])+1);
内容的提问来源于stack exchange,提问作者John

