前言
上一篇我们讲了指针的基础篇,这一篇我们讲述指针的进阶篇。
一.字符指针
指针类型中有一种类型是char*,基本使用如下:
int main()
{
char a = 'x';
char* p = &a;
*p = 'a';
printf("%c", a);
}
这里还有另外一种使用方式:
int main()
{
const char* pstr = "hello world";
printf("%s\n", pstr);
return 0;
}
这里我们定义了一个常量字符串,我们这里并不是将整个字符串放在pstr里,而是将字符串的首字符地址放进pstr里。
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
这段代码的结果如上,为什么会如此,常量字符串和普通字符串有什么区别?常量字符串是存储在内存的静态区是不可修改的,也可以理解为只读,而普通字符串是可以修改的,存放在内存的栈区,这里str1和str2虽然内容相同,但完全是开辟了不同的两个空间,而在静态区的str3和str4编译器发现他们相同,直接存在了一个相同的空间。
二.指针数组
整型数组是用来存放整型的,那指针数组顾名思义就是用来存放指针的,例如:
char* arr[];//一级指针数组
int* arr[];
char** arr[];//二级指针数组
arr是指针名,而char* []才是指针类型。
int main()
{
int* a=NULL;
int* b=NULL;
int* arr[2] = { a,b };
}
三.数组指针
很容易将指针数组和数组指针弄混,指针有整型指针,字符指针,那数组指针就是一个指向数组的指针,它的基本形式如下:
int (*p)[10];//指向一个含有10个元素的数组,指针类型为int(*)[10]
这里如果不加()那么p会先和[ ]结合,就会变成指针数组。
四.数组指针的使用
数组指针的使用大多数用在二维数组传参上,下面是数组指针最基本的使用:
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int(*p)[10] = &arr;
printf("%d", *(*p + 1));//*p=arr,*(arr+1)=arr[1]
}
这里必须使用&arr将数组的地址赋给p,如果这里只是arr的话,只表示数组的首地址。
二维数组传参:
void print(int(*p)[4],int row,int col)
{
int i;
int j;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%-2d ", p[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
print(arr,3,4);
}
这里二维数组的数组名arr实际上数组第一行的地址,也就是说是一个数组,数组里有4个元素,所以这里我们使用数组指针进行传参。
五.函数指针
函数指针指的是指针指向的是一个函数,基本形式如下:
int (*p)(int, int);//指针类型为int(*)(int, int)
*p后面的()内即是函数参数类型。
int main()
{
int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
void (*p)(int(*)[4], int, int);
p = print;//&print=print
p(arr,3,4);
}
上例就是运用函数指针调用函数,这里&print和print其实是一样的,函数名即地址。
大家是否好奇函数指针的运用呢?比如我们经常会使用的qsort函数
void qsort (void* base, size_t num, size_t size,
int (*compar)(const void*,const void*));
qsort的最后一个参数即是函数指针。
qsort函数参数介绍:
- base,排序起始位置的地址
- num,需要排序的元素个数
- size,排序元素每个的大小
- 排序规则,>1则交换元素位置
int cmp_num(const void* a, const void* b)
{
return *((int*)a) - *((int*)b);
}
int main()
{
int arr[10] = { 5,6,11,55,21,3,7,9,1,2 };
qsort(arr, 10, sizeof(arr[0]), cmp_num);
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
}
这里我们就是使用函数指针进行传参,将cmp_num传入了qsort函数,决定了排序的规则。
用冒泡排序实现qsort
首先我们需要将大问题化成小问题,我们先将普通的冒泡函数写出来。
void bubble_sort(int* base, int num)
{
int i;
int j;
int tmp;
for (i = 0; i < num - 1; i++)
{
for (j = 1; j < num - i - 1; j++)
{
if (base[j]>base[j-1])//判断就是我们需要换成函数的地方
{
tmp = base[j];
base[j] = base[j + 1];
base[j + 1] = tmp;
}
}
}
}
前面我们说到qsort的第四个参数是用来决定排序规则的,冒泡排序里的判断语句我们就需要用第四个参数来替换他,从而决定什么条件下我们需要调换两个元素的位置,那么这里我们就需要更多的参数。
void bubble_sort(const void* base,int num,int sz,int(*p)(const void*,const void*))
将判断语句替换成判断函数,由于我们不清楚传入的数组类型,我们只能使用void来接收,我们传入函数的指针不知道每次应该跳过多少个字节,所以我们这里要先将base强转成char*,因为char的指针一次只跳过一个字节,在后面再加上jsize(每个元素的大小),就相当于跳过了j个元素。
if (p((char*)base+j*sz, (char*)base+(j+1)*sz)>0)/*将base强转成char*类型,
+j*sz就是跳过j个相同类型的元素*/
当我们满足调换条件的时候,我们需要考虑怎么调换两个元素,因为你不清楚他的元素类型。这里采用一个字节一个字节的交换,比如int四个字节,两个元素一个字节一个字节的交换,就不需要去考虑是什么数据类型,只需要知道占几个字节。
void swap(const void* e1, const void* e2, int sz)
{
int i;
char tmp;
for (i = 0; i < sz; i++)//因为类型不定,这里一个字节一个字节交换
{
tmp = *(char*)e1;
*(char*)e1= *(char*)e2;
*(char*)e2 = tmp;
((char*)e1)++;
((char*)e2)++;
}
}
最后就是应用,和qsort的应用基本一样,完整代码如下。
void swap(const void* e1, const void* e2, int sz)
{
int i;
char tmp;
for (i = 0; i < sz; i++)//因为类型不定,这里一个字节一个字节交换
{
tmp = *(char*)e1;
*(char*)e1= *(char*)e2;
*(char*)e2 = tmp;
((char*)e1)++;
((char*)e2)++;
}
}
void bubble_sort(const void* base,int num,int sz,int(*p)(const void*,const void*))
{
int i;
int j;
for (i = 0; i < num - 1; i++)
{
for (j = 0; j < num - i - 1; j++)
{
if (p((char*)base+j*sz, (char*)base+(j+1)*sz)>0)//将base强转成char*类型,+j*sz就是跳过j个相同类型的元素
{
swap((char*)base + j * sz, (char*)base + (j + 1) * sz, sz);
}
}
}
}
//int cmp(const void* e1, const void* e2)//排序整型
//{
// return *(int*)e1 - *(int*)e2;
//}
int cmp(const void* e1, const void* e2)//排序字符串
{
return strcmp(*(char**)e1 , *(char**)e2);//strcmp(const char*,const char*),str的类型是char**,这里的e1,e2的类型是char**。
}
int main()
{
int i;
//int arr[] = { 4,2,6,7,1,2,3 };
char* str[] = { "lyh","cgf","zjc","xhj" };
//bubble_sort(arr,sizeof(arr)/sizeof(arr[0]),4,cmp);
bubble_sort(str, sizeof(str) / sizeof(str[0]), 4, cmp);
//for (i = 0; i < 7; i++)
//{
// printf("%d ", arr[i]);
//}
for (i = 0; i < 4; i++)
{
printf("%s ", str[i]);
}
}