在写完基数排序后,对于一些函数的写法和语言的理解更加深入了,有一些感想想写下了。
在基数排序中,我写下的整个过程的代码如下,我添加了一些注释来说明每个函数的目的,理清楚过程后写起来其实并不难。
void LSDRadixSort(int a[], int N) {
PtrToNode List;
List = ArrToList(a, N);//ArrToList函数:将数组转换成一个链表
int D,Di;
Bucket B;
/*排序开始,D要一直循环,直到完成所有关键字为止*/
for (D = 1; D <= MaxDigit; D++) {
/*按照D,将链表中的数插入到桶中*/
ListToBucket(List,B,D);
/*将桶插入链表;为了方便链表插入,这里从最后一个桶开始往链表插入*/
List = NULL;
for (Di = Radix-1; Di>=0; Di--) {
BucketToList(List,B,Di);
}
}
ListToArr(List, a, N);//ListToArr函数:将链表转换为数组
}
但是在具体书写每一个函数的时候,遇到了一些小问题,也值得我思考。
1. C语言的值传递特性:
C语言中的函数是通过值传递的,即使是指针也是如此。
在书写ListToBucket(List,B,D)这个函数的时候,我是这么写的。
那么这里就带来了第一个问题:为什么PtrToNode& List这个东西要使用引用而不使用值传递?
毕竟PtrToNode原本就是一个指针了。
/*按照D,将链表中的数插入到桶中*/
ListToBucket(List,B,D);
void ListToBucket(PtrToNode& List, Bucket B, int D) {
/*根据D,将List中的值插入到Bucket B中*/
PtrToNode& Ptr2ListNode=List;
int d;
while (Ptr2ListNode != NULL) {
/*将这个节点插入到对于的桶中,并且Ptr2ListNode指针要进行更新*/
d = GetDigit(Ptr2ListNode->key, D);
/*将这个节点插入到对于的桶中,并且Ptr2ListNode指针要进行更新*/
InsertNodeToBucket(Ptr2ListNode, B, d);
}
}
void InsertNodeToBucket(PtrToNode& List,Bucket B,int D) {
/*将节点List插入到Bucket B的第D个桶*/
PtrToNode tmp;
tmp=List->next;
List->next = B[D].head;
B[D].head = List;
List = tmp;
}
于是我们的问题就变成了,如果函数声明为如下,那么能否更新指针呢?从而使得外部的循环
while (Ptr2ListNode != NULL) {
…
}是有意义的?
void ListToBucket(PtrToNode List, Bucket B, int D);
void InsertNodeToBucket(PtrToNode List,Bucket B,int D);
答案是不行!
因为在参数传递的过程中,指针进行的是值的传递,虽然传递的是指针,但是外部的指针的值不会跟着改变。也就是pt_f可以在函数中随便指,也能改变pt_f指向的对应的节点中的Key的值和Next的值;但是在函数的外部,pt1是不会变化的,也就是说对应的while循环无法正常的迭代。
简单点来说,传入的pt1是按值传递的,它对应的地址的值(key,next)可以被修改,但是pt1本身不会被修改,和我们的目的不同。
对应的解决方法:
1.考虑使用引用,因为引用在使用的时候也会改变相应的值,而且这样写起来的话更加方便理解一些。也可以考虑使用指针传递,这样的话函数就会变成
void ListToBucket(PtrToNode* Ptr2List, Bucket B, int D);
void InsertNodeToBucket(PtrToNode* Ptr2List,Bucket B,int D);
使用指针的话,在外部调用的时候,我们创建了链表后,就要这么调用函数
PtrToNode* Ptr2List
*Ptr2List=ArrToList(a,N);
ListToBucket(Ptr2List,B,D);
对应的在函数内部,应该需要这么写(以插入节点为例子)
void InsertNodeToBucket(PtrToNode* Ptr2List,Bucket B,int D) {
/*将节点List插入到Bucket B的第D个桶*/
PtrToNode tmp,List;//这里相当于把原来传递的List值当成一个新增的变量
List=*Ptr2List;
tmp=List->next;
List->next = B[D].head;
B[D].head = List;
*Ptr2List = tmp;
}
总结1:
简单来说,在写函数之前,除了考虑需要哪些变量,要实现哪些功能之外,还需要考虑输入的变量,哪些是需要改变的,需要改变的变量,要么在函数外部已经声明好一个指针,然后在函数使用的时候通过指针传递;要么可以考虑使用引用。在函数中如果出现指针也要改变的情况,要尤其注意这个问题!
关于结构体数组,typedef以及函数处理结构体
正确的写法
/*定义一个新结构*/
struct NewStr {
int x;
int y;
};
typedef NewStr NS_10[10];//以新结构定义一个数组
//void test(NS_10 b)//正确写法1
//void test(NewStr* b)//正确写法2
//void test(NS_10 b[])//错误写法
void test(NS_10 b) {
for (int i = 0; i < 10; i++) {
b[i].x = 2 * i;
}
}
int main() {
NS_10 b;
/*初始化赋值*/
for (int i = 0; i < 10; i++) {
b[i].x = i;
}
test(b);//函数处理数组
for (int i = 0; i < 10; i++) {
cout << b[i].x << endl;
}
return 0;
}
test函数这样写也可以
void test(NewStr* b) { }//最终输出和上面相同
错误的写法
void test(NS_10 b[]) {//误以为要改变b[i]中的值,那么就需要传递b的地址,考虑到int a[]==int* a,于是这里就这么写了
for (int i = 0; i < 10; i++) {
b[i]->x = 2 * i;
}
}
int main() {
NS_10 b;
for (int i = 0; i < 10; i++) {
b[i].x = i;
}
test(&b);//考虑到这里要改变b[i]中的x的值,感觉这里这么写似乎也没有什么错
for (int i = 0; i < 10; i++) {
cout << b[i].x << endl;
}
return 0;
}
反思:
不能使用void test(NS_10 b[]) ;有下面几个原因:
1.b[i]的基本单位是NS_10,取b[i]的每个跨距为NS_10,即10个int的距离,这是不符合逻辑的,因为我们想取的是b里面的第i个元素,而不是以b为起点的第10*i个元素。
2.为什么这里似乎只是值传递也达到了效果?这里不需要指针传递吗?
答:这里的NS_10 b[] 相当于 NS_10 * Ptrb,相当于b是一个NS_10类型的指针变量,那么我们传进去的参数是&b,在取了b[i]后是一个NS_10的变量,而这个变量也是一个Newstr指针,这个时候取值需要用"->x"。
但是这种写法希望解决的是这个Ptrb它可的值发生变换,也就是说它可能指到另一个NS_10的时候,那么用这个是可以考虑的。或者换句话说,当你有很多个NS_10变量,比如NS_10 a,b,c的时候,你这么用是合理的。但是现在只有一个NS_10 变量b,这么用就不太合理了。
那么最后要思考的是,为什么只传进去一个b就可以改变你需要改变的数的值呢?
第一点:
根据C传递的特性,简单来说就是如果要改变一个值,那么需要输入这个值的地址,从而通过地址来改变这个值。如果你想改变值x,而你输入的变量也是这个x,由于函数值传递是无法改变外部的值的。
第二点
那么需要传进去一个地址,那么可以传b,也可以传&b,传哪个合适,这就要考虑到进位的大小和取值,这个和二维数组a[][]有点像。
typedef Newstr NS_10[10];
NS_10 b;
所以这里的b的变量虽然是NS_10 b,但同时也是 Newstr*,它也是一个指针类型
思考两个问题:
1.如何改变一个结构体/数组中的某个值?
fun(Newstr* a);//a+1时,sizeof(Newstr)++
fun(int a[]);//a+1时,sizeof(int)++
fun(int* a);//a+1时,sizeof(int)++
fun(NS_10 b[]);//sizeof(NS_10)++
fun(NS_10 b);//同Newstr*a,sizeof(Newstr)++
fun(PtrToSrtuct b);//同NS_10 b
简单来说,在函数声明为fun(ClassX A[]);的时候,传进的参数是一个ClassX类的指针,或者说传进的是ClassX类的一个数组A[](array of classX),或者说是多个classX类的对象(multi objects of classX)
2.那么如何改变结构体数组中的值呢?
这里我做了一个图,用来表示声明的时候不同的方法,会导致B[i]向内取元素和向外取元素的区别。
即
void func(NS_10 B);//B[i]取的是B里面的第i个结构体,向内取
void func(NS_10 B[]);//B[i]取的是B之后的第i个结构体数组,向外取
最后补充一个知乎上关于typedef的回答
作者:婉儿飞飞
链接:https://www.zhihu.com/question/29798061/answer/144423125
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。