关于C语言指针相减时用size_t替代ptrdiff_t的可行性问询
嘿,咱们来好好唠唠这个问题——很多人在处理指针相减、计算数组索引的时候,都会纠结能不能用size_t代替ptrdiff_t,先结合你贴的这段代码,把事儿说清楚:
size_t array_len = SIZE_MAX; int *my_array = calloc(array_len, sizeof(int)); int compar(const void *n_to_search, const void *arr_element) { if(*(int *)n_to_search > *(int *)arr_element) return 1; else if(*(int *)n_to_search < *(int *)arr_element) return -1; else return 0; } void search(int n_to_search, size_t *idx) { int *result = bsearch(&n_to_search, my_array, array_len, sizeof(int), compar); // 假设这里要做指针相减:*idx = result - my_array; }
首先得明确两个核心点:
- 指针相减的标准行为:C标准规定,两个指向同一数组(或数组末尾后一个位置)的指针相减,结果的类型是
ptrdiff_t——这是一个有符号整数类型,因为相减可能得到负数(比如用数组前面的指针减后面的指针,结果就是负的)。 size_t是无符号整数类型,专门用来表示对象的大小或索引,它的取值范围是非负的。
那回到问题:用size_t替代ptrdiff_t可行吗?得分情况说,但整体不推荐:
1. 直接替代会带来潜在的逻辑bug
如果指针相减的结果是负数,把它转成size_t会触发无符号类型的转换规则——负数会被转换成一个超大的正数(比如-1转成size_t就是SIZE_MAX)。比如你代码里如果bsearch没找到元素,result是NULL,这时候NULL - my_array本身就是未定义行为(因为两个指针不属于同一数组);就算是合法的指针相减得到负数,转成size_t后赋值给*idx,得到的索引完全不符合预期,后续逻辑肯定会崩。
2. 极端场景下的“可行”不代表安全
只有当你能100%保证指针相减的结果是非负的时候,转成size_t才不会出问题——比如你确定result一定指向my_array里的元素,或者数组末尾的位置,这时候result - my_array是正的,转成size_t没问题。但这种情况其实也没必要强行替换,用ptrdiff_t先存储结果,再做合法性检查,最后转成size_t赋值给索引,反而更安全——万一哪天代码逻辑变了,出现了负数结果,ptrdiff_t能帮你及时发现问题,而size_t会悄无声息地把负数变成超大正数,埋个难以排查的坑。
给你这段代码的小建议
你这段代码本身还有个大问题:array_len设成SIZE_MAX,calloc根本不可能分配这么大的内存,my_array大概率是NULL,后续调用bsearch也是未定义行为。另外如果要计算索引,正确的做法应该是:
void search(int n_to_search, size_t *idx) { // 先检查数组是否有效 if (my_array == NULL || array_len == 0) { *idx = (size_t)-1; // 用特殊值表示未找到或数组无效 return; } int *result = bsearch(&n_to_search, my_array, array_len, sizeof(int), compar); if (result == NULL) { *idx = (size_t)-1; return; } // 先用ptrdiff_t存储结果,检查是否非负且合法 ptrdiff_t diff = result - my_array; if (diff < 0 || (size_t)diff >= array_len) { *idx = (size_t)-1; return; } *idx = (size_t)diff; }
总结一下:除非你能绝对把控指针相减的结果是非负的,否则别用size_t替代ptrdiff_t做指针相减的结果存储,老老实实用ptrdiff_t先承接,再做转换和检查,能避免很多莫名其妙的bug。
内容来源于stack exchange

