C语言char*与void*指针互转及通用swap函数实现问题排查
问题根因
你的代码存在两个致命错误,直接触发程序崩溃,因此第二个printf完全不会执行:
- 越界内存访问触发段错误:
array是一个独立的void*类型栈变量,仅占1个指针长度的内存,值为数组s的首地址。你写的(&array)[1]相当于越过array变量的内存边界,访问栈上相邻的未分配内存,往这块内存写数据会直接破坏栈结构,操作系统会立刻终止进程,后续代码完全没有执行机会。 - 函数设计和类型转换完全不符合通用逻辑:你写的
swap只支持char**类型参数,根本达不到通用void*交换的要求;最后打印时直接把void*强转成char*重复打印,即使不崩溃也不可能得到正确的两个字符串结果。
适配快速排序的通用swap正确实现
通用交换函数的核心逻辑是不绑定任何具体数据类型,通过传入单元素的字节长度,按字节拷贝完成内存交换,和C标准库qsort的设计逻辑完全一致,可以支持任意类型数据(int、自定义结构体、字符串等)的交换需求:
#include <stdio.h> #include <stdlib.h> #include <string.h> // 通用交换函数:a/b为任意类型的元素指针,elem_size为单个元素占的字节数 static void swap(void* a, void* b, size_t elem_size) { void* temp = malloc(elem_size); memcpy(temp, a, elem_size); memcpy(a, b, elem_size); memcpy(b, temp, elem_size); free(temp); }
针对你的字符串数组测试场景,调用时需要注意:字符串数组的每个元素是char*类型,因此元素大小为sizeof(char*);由于标准C不允许直接对void*做指针偏移,需要先转成单字节步长的char*,按索引 * 元素大小计算偏移量,才能拿到对应位置元素的真实地址。修正后的测试代码如下:
int main() { char* s[2]; s[0] = "weee"; s[1] = "yooo"; void* array = s; printf("before %s %s\n", s[0], s[1]); // 传入索引0、索引1位置的元素地址,以及char*类型的字节长度 swap( (char*)array + 0 * sizeof(char*), (char*)array + 1 * sizeof(char*), sizeof(char*) ); // 打印时先转回char**类型指针,取下标对应的值才是字符串首地址 printf("after = %s %s\n", ((char**)array)[0], ((char**)array)[1] ); return 0; }
关键注意点
- 实现通用排序算法时,所有涉及元素访问、交换的逻辑,都不能硬编码具体类型,必须依赖传入的元素字节长度做内存操作,才能适配不同数据类型的排序需求。
- 对
void*做地址偏移时,必须先转为char*类型(步长为1字节),偏移量计算公式为目标索引 * 单个元素字节长度,直接对void*做加减属于GCC非标准扩展,可移植性极差。 char* arr[n]字符串数组的存储逻辑是:数组内存中连续存放n个char*指针,每个指针指向对应字符串的首地址,访问第i个字符串时需要先拿到数组中第i个char*的值,不能直接把数组首地址强转成char*当字符串使用。- 对指针做下标访问时,必须保证访问的内存属于预先分配的合法范围,越界读写属于未定义行为,大概率直接触发段错误导致程序崩溃。
内容的提问来源于stack exchange,提问作者ramaswag
相关产品推荐
相关产品推荐

