反思
【一些感悟】内存和指针
针对这篇文章中 “scanf() 和 数组” 小节存在的事实错误,及我认为的,C 中存在的一些不得已的缺陷。
我做了总结,并通过 Blog 记录下来。
本篇主要从两个点来阐述:
1.【一些感悟】内存和指针 中 “void* 空类型指针” 小节提到的代码。
2.scanf() 的参数。
判断传入的指针类型
简单的冒泡排序:
void BubbleSort(void* array,int count)//注意第一个参数,我写的是 void*
{
int i, j, temp, flag = 1;
for (j = 0; flag == 1; j++)
{
flag = 0;
for (i = 0; i < count - j; i++)
{
if (array[i] < array[i + 1])
{
memswap(array[i + 1], array[i], sizeof array[i])
//由于我并不知道传入数组的类型,交换数据只能通过操作内存
//memcpy()用来交换内存,详细定义在下方
flag = 1;
}
}
}
}
memswap()的定义:
void memswap(void* a,void* b,int size){
void* temp = malloc(size);
memcpy(temp,a,size);memcpy(a,b,size);memcpy(b,temp,size);
free(temp);
}
可以看出来,这段代码是错误的,因为我在排序函数中试图引用 void* 。
我的目的是拓宽一个函数的适用范围,使得其不仅仅只针对一种数据类型排序,然而 C 对这样的传参方式无能为力,单从一个 void* 没办法判断实参的数据类型。
老师为我提供了下图这样的解决方法:
然而,很显然,这种解决方案的移植性很差(不同环境下,某些数据类型所占字节数不同)
后来接触了标准库中的 qsort() 函数,琢磨了其思想后惊为天人——它使用了一个回调函数,通过让程序员完成比较工作来绕开 “无法判断实参数据类型” 的问题。
(详细解决方法移步.【一些感悟】内存和指针 中 “void* 空类型指针” 小节。)
至此,问题算是解决了,但解决得并不完美。
对于排序来说,使用一个回调函数并不能算是一个方便的做法,显然这个事情变得比以前更复杂了。那么,为什么 qsort() 函数不直接判断实参的数据类型而是使用更为复杂的回调函数呢?
因为C 对这件事无能为力。
也可能是认为没有必要,也可能是没考虑到,更可能的原因是,这是 C 底层性的一个表现。
我再举一个例子,用来佐证 C 对判断实参数据类型确实无能为力的事实。
从 scanf() 的参数的角度解释
这里也是对 【一些感悟】内存和指针 中 “scanf() 和 数组” 小节的事实错误的改正。
我们认为,scanf() 函数本身在设计上存在不周到的地方。
无论你传入的指针是什么类型的,它只会傻傻地按照指定的格式化格式,从传入的指针位置开始写入数据,无论是否已经超出了变量的引用范围。
尽管在号称更安全的 scanf_s() 中,也只是要求程序员多传入一个参数,来表示写入的最大长度,有没有感觉很傻?
标准库都如此做了,那么我们就更加无法实现了。
所以,C 对判断实参数据类型的的确确是无能为力的。