一、一维数组传参本质
在C/C++等编程语言中,一维数组作为参数传递给函数时,实际传递的是数组首元素的地址。这一机制涉及指针和内存地址的隐式转换,理解其本质有助于避免常见错误。
1.数组名退化为指针
当数组名作为函数参数传递时,编译器会将其隐式转换为指向数组首元素的指针。例:
void func(int arr[]);
实际上等价于:
void func(int *arr);
那么函数内部就无法通过sizeof(arr)获取数组长度,因为arr已经被转换成指针,例:
在上面的代码中,我将sz的位置声明在了p1函数里,就造成了程序运行后的差异。
因为在p1函数中,arr数组已经被退化成了指向数组受元素的地址,那么计算sizeof(arr)就是在计算指针的大小,这组数据是我在x64环境下运行得出,那么计算sizeof(arr)/sizeof(arr[0])实际上就是在计算8/4=2。所以第二个程序才会只输出两个元素。
2.数组传参与指针参数的区别
虽然语法相似,但以下声明有细微差异:
int arr[]
:明确表示期望数组参数,但实际仍处理为指针。int *arr
:直接声明指针参数,更显式。
二、冒泡排序
首先,我们要先了解什么是冒泡排序:
冒泡排序是一种简单的排序算法,通过重复遍历待排序的列表,比较相邻的元素并交换它们的位置,将较大的元素逐渐“冒泡”到列表的末端。每一轮遍历都会将当前未排序部分的最大元素移动到正确的位置。
1.冒泡排序的步骤(如上图)
-
)从列表的第一个元素开始,依次比较相邻的两个元素。
-
)如果前一个元素大于后一个元素,交换它们的位置。
-
)继续遍历列表,直到到达未排序部分的末尾。
-
)重复上述过程,每次遍历减少未排序部分的长度(因为每次遍历后,最大的元素已经到位)。
2.实现排序
#include<stdio.h>
void bobble_sort(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz - 1; i++)//总循环次数总是数组个数-1
{
int j = 0;
{
for (j = 0; j < sz - 1 - i; j++)//因为每一次遍历数组都会使一个元素成功被排序,那么每次需要两个两个比较的次数也会变化,就是sz-1-i。
{
if (p[j] < p[j + 1]) //比较
{
int tmp = p[j];
p[j] = p[j + 1];
p[j + 1] = tmp;
}
}
}
}
}
void print(int* p, int sz)//打印排序好的数组
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
}
void text()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
bobble_sort(arr, sz);
print(arr, sz);
}
int main()
{
text();
return 0;
}
3.冒泡排序的适用场景
冒泡排序由于其简单性,适用于小规模数据的排序。对于大规模数据,更高效的排序算法(如快速排序或归并排序)更为合适。
三、快速排序(qsort函数的实现)
1.qsort函数简单理解
1.)qsort的第一个参数是需要排序的数据的首元素地址,需要自己提供数据的返回类型
2.)第二个参数指base中需要排序元素的个数。
3.)第三个参数size指需要比较大小的元素的‘宽度’,单位是字节。
4.)第四个参数是指向比较函数的函数指针。
正是qsort用函数指针作为参数,才能使qsort能比较任意类型的数据,所以这个比较函数需要我们自己提供。
这个函数能够返回1,0,-1即可,qsort函数能接收并将其排序。
2.qsort函数的实现
struct Stu //学⽣
{
char name[20];//名字
int age;//年龄
};
//假设按照年龄来⽐较
int cmp_stu_by_age(const void* e1, const void* e2)
{
return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}
//strcmp - 是库函数,是专⻔⽤来⽐较两个字符串的⼤⼩的
//假设按照名字来⽐较
int cmp_stu_by_name(const void* e1, const void* e2)
{
return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}
//按照年龄来排序
void test2()
{
struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_age);
}
//按照名字来排序
void test3()
{
struct Stu s[] = { {"zhangsan", 20}, {"lisi", 30}, {"wangwu", 15} };
int sz = sizeof(s) / sizeof(s[0]);
qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);
}
int main()
{
test2();
test3();
return 0;
}
3.注意事项
- 稳定性:
qsort
不保证排序的稳定性(相同元素的相对顺序可能改变)。 - 内存:排序过程直接在原数组上操作,无需额外内存空间。
四、用冒泡排序模拟qsort函数
用冒泡排序模拟qsort函数使冒泡排序也能排序任意类型的数据
1.函数实现
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct S
{
char name[20];
int age;
};
int com_char(void* p1,void*p2)
{
return strcmp(((struct S*)p1)->name,((struct S*)p2)->name) ;
}
void Swap(char*p1,char*p2,int width)
{
int k=0;
for(k=0;k<width;k++)
{
char tmp=*p1;
*p1=*p2;
*p2=tmp;
p1++;
p2++;
}
}
void bobble_sort(void* p,int sz,int width,int(*com)(const void* p1,const void* p2))
{
int i=0;
for(i=0;i<sz-1;i++)
{
int j=0;
if(com((char*)p+width*j,(char*)p+(j+1)*width)>0)
{
Swap((char*)p+width*j,(char*)p+(j+1)*width,width);
}
}
}
void print(struct S* p,int sz)
{
int j=0;
for(j=0;j<sz;j++)
{
printf("name: %s",p[j].name);
printf("\n");
}
}
void text()
{
struct S arr[3]={{"lisi",89},{"zhangsan",67},{"jisi",45}};
int sz=sizeof(arr)/sizeof(arr[0]);
bobble_sort(arr,sz,sizeof(struct S),com_char);
print(arr,sz);
}
int main()
{
text();
return 0;
}
上述代码就使用了冒泡排序的方式来排序的字符型数据,但底层仍是冒泡排序,是基于简单的比较和交换操作,其核心逻辑不依赖其他排序算法。