//test.c中定义char arr[]="";//引入变量char *arr;int main(){ ("%sn",arr); main.c 中的 0;}
运行程序会报错,arr在test.c中占用7个字节。 在main.c中声明为指针类型,占用4个字节,所以arr只能访问test.c中的前4个字节,系统肯定不会同意,所以操作肯定会出错。
(2)定义指针,引入并声明数组:
//在test.c中定义char *p="";//在main.c中引入变量char p[];int main(){ ("%sn",p); 0; }
它可以编译并通过,但输出是一个随机值。
因为test.c中定义的p占用4个字节,而main.c中的p访问的是指针的4个字节,所以它是一个随机值。
5、函数指针:
函数的函数名
首先看下面的代码:
## test(){}int main(){("%pn", test);("%pn", &test);("暂停"); 0;}
运行结果:
上面操作的结果是一样的,因为函数名代表了函数的地址。
>函数的地址也需要存储。 我们知道地址是一个指针,所以只有指针才能存储地址,所以函数指针就是用来存储函数的地址的。
如下例所示:
无效测试(){}; 那么用来保存函数地址的指针就是:void (*p)(); //p首先与*结合,p是一个指针,指向一个无参数且返回类型为void的函数所指向的函数
> 下面两个有趣的代码:
(*(void(*)())0)();void(*(int,void(*)(int)))(int);
第一段代码:将0转为void(*)()函数指针,然后解引用,即函数调用
第二段代码:是一个函数,参数为int和void(*)(int),函数返回类型为:void(*)(int);
6. 函数指针数组:
> 我们知道函数是存储同一类型数据的存储空间。 如果函数的地址存储在数组中,那么这个数组就是函数指针数组。
函数指针数组定义为:
无效(*arr[10])(); //arr先与[]结合,所以arr是一个数组,数组内容为:void(*)()类型的函数指针
函数指针数组的实例:传输表
计算器应用:
## add(int x, int y){ x + y;}int sub(int x, int y){ x – y;}int nul(int x, int y){ x*y;}int drv(int x, int y){ x / y;}int main(){int x, y;int input = 1;int ret = 0;int(*p[5])(int x, int y) = { 0, add, sub, nul, drv };//转表 while (input){("************************n");( " 1:add2:sub n");(" 3:nul4:div n");("**************************** ****************************************************** ****************n" );("请选择:");("%d", &input);if ((input=1)){("输入操作数:");("%d %d", &x, &y); ret = (p[input])(x, y);}("输入错误n");("%dn", ret);}("暂停"); 0;}
指向函数指针数组的指针:
我们看一段代码:
## test(const char*str){}int main(){void(*p)(const char*) = test;//p是函数指针 void(*p1[5])(const char*) =测试 ; //p1 是函数指针数组 void(*(*p2[5]))(const char*) = test;//p2 是指向函数数组的指针("pause"); 0;}
指向函数指针数组的指针是一个函数。
该指针指向一个数组,其元素是函数指针。
7、回调函数:
回调函数是通过函数指针调用的函数。 将一个函数的指针作为参数传递给另一个函数,这个指针可以用来调用它所指向的函数。
回调函数不是由函数的实现者直接调用,而是在特定事件或条件发生时由对方调用,以响应该事件或条件。 简单来说,就是在别人的函数运行过程中,回调自己实现的函数。
例如;
## test(){ //定义回调函数("holle world!");}void test1(void(*test2)()){ //定义实现回调函数的调用函数test2();}int main() {测试1(测试); //实现回调函数(“暂停”); 0;}
回调函数的实例:
模拟qsort的实现:qsort无类型快速排序 void qsort(void*base, num, width, int(*)(const void*eleml, const void*elem2)); 第一个参数是要排序的内存空间的首地址,第二个参数是元素的个数,第三个参数是元素的宽度,即元素的类型大小,第四个参数传递到比较元素的函数。
使用冒泡实现qsort:
# ## int (void *p1, void *p2)//字符串比较 { (*(char **)p1, *(char**)p2);//强制转为二级字符指针,这样就级别引用或者指针,这样就可以得到4个字节的内容,即得到第一个数组的某个元素的整个地址,得到的地址就是整个字符串的第一个元素的地址,传递给strcm 函数进行比较。 }int (void *p1, void *p2)//整数数字的比较 {int a = *(int*)p1;int b = *(int*)p2;if (a > b) 1;else if (a < b) -1; 0; }int (void *p1, void *p2)//字符比较 { *((char*)p1) – *((char*)p2);}int (void *p1, void *p2)//单精度浮点比较 { a = *(*)p1; b = *(*)p2;如果 (a > b) 1;否则如果 (a < b) -1; 0;}void Swap(char *x, char *y, int size){while (size–)//交换变量的每个字节 {*x ^= *y;*y ^= *x;*x ^ = *y;x++;y++;}}void (void *arr, int num, int size, int(*cmp)(void *, void *))//void*类型指针保证类型兼容性,传入什么类型内部全部可以排序,指针转成char*类型,交换各个字节,不需要考虑参数类型{int i = 0; for (i = 0; i < size – 1; i++) {int flag = 0;//有序标志 int j = 0;for (j = 0; j < num – 1 – i; j++){ //对于指针数组,存储的地址转换为char*类型指针读取,读取只读取1个字节的内容,第二个参数加上类型长度指向数组下一个元素的地址,只读取一个字节的内容byte if (cmp((char *)arr + j * size, (char *)arr + (j + 1) * size) > 0){ //与Swap函数类似,对于指针数组,转换为char类型指针,交换一个字节的内容,并且没有一次性交换4个字节(整个字符串的第一个元素) flag = 1; //排序一次,flag置1,表示序列本身无序 Swap((char *)arr + j * size , (char *)arr + (j + 1) * size, size);}} if (flag==0)//第一次循环时,没有交换,说明序列本身是有序的break;}} int main(){//char *p[] = { "hijs", " sdfd", "fgdg", "abcd", "defg", "abc" };//这是一个指针数组,是存储指针的数组,不是指针 int arr[] = { 12,623,8,2 ,6,234};//char ch[] = { 'a','f','y','s','b', 'd' };// db[] = { 2.4,4.5,1.3, 7.8,1.2 };int i = 0;int size = (arr) / (arr[0]);(arr, size, (int) , );//整数排序//(p, size, (char* ), );//字符串数组排序//(ch, size, (char), );//字符排序//(db , size, (), );//浮点排序 for (; i < size; i++){("%d ", arr[i]);}("暂停"); 0;}
8、与指针和数组相关的练习;
## main(){int a[5]={1,2,3,4,5};int *ptr=(int *)(&a+1); //这里a代表整个数组,取地址加1代表下一个数组的地址,下一个数组是一个整数("%dn",*(a+1)); //2作为右值a代表第一个元素的地址,加1代表加上元素类型的大小下一个元素的地址,("%dn",*(ptr-1)); //5 ptr 指向下一个数组第一个元素的地址,减1表示数组a最后一个元素的地址("pause" ); 0;} > 测试{int Num;char *;short sDate;char cha[2];short Sba[4];}*p;
结构体的大小为20。假设p的值为,以下值为:
p+0*1=//指针p加1表示加其类型的大小,即加20(long) p+0*1= //这里p是 long类型,加1表示加the value 1 ( int*)p+0*1= //p是int *类型指针,加1表示增加类型大小 4>int main(){int a[4]={1,2,3, 4};int * ptr1=(int *)(&a+1);int *ptr2=(int *)((int)a+1);("%xn",ptr1[-1]); //4相当于*(ptr1-1)("%xn",*ptr2);("pause"); 0;}
读*ptr2就是从ptr2开始连续读取4个字节,考虑大小端。
int main(){int a[2][3]={(0,1),(2,3),(4,5)}; //数组实际上是 {1,3,5,0,0, 0}int *p;p=a[0];("%dn",p[0]);("pause"); 0;}
结果是:1
这道题中,应该考虑到数组中的()是逗号表达式,即最右边的是主要的,所以数组应该是:{1,3,5,0,0,0};
## main(){int a[5][5];int(*p)[4];p = a;("%p,%pn", &a[4][2], &p[4 ][2]);("%p,%dn", &p[4][2] – &a[4][2], &p[4][2] – &a[4][2]); (“暂停”); 0;}
运行结果:
分析:p[4][2]相当于*(*(p+4)+2),找到对应的位置,因为同一个数组的指针相加和相减代表的是两个指针的元素个数,因为&p [ 4][2]小于&a[4][2],所以%d的输出结果为-4,用%p输出时按无符号输出,即:
内存中: 1 0 0000 0100 反转: 1 1 1111 1100F >## main(){char *c[] = { "ENTER", "NEW", "POINT", "FIRST" };char ** cp[ ] = { c + 3, c + 2, c + 1, c };char ***cpp = cp;("%sn", **++cpp);("%sn" , * –*++cpp + 3);("%sn", *cpp[-2]+3);("%sn", cpp[-1][-1] + 1) ;( “暂停”); 0;}
运行结果:
分析:
字符串的地址放在指针数组c中,c的闪回放在指针数组cp中,cp的地址放在指针cpp中。
**++cpp:
*–*++cpp+3:
*cpp[-2]+3:
cpp[-1][-1]+1:
好了,今天的主题就讲到这里吧,不管如何,能帮到你我就很开心了,如果您觉得这篇文章写得不错,欢迎点赞和分享给身边的朋友。