指针基础知识:https://blog.csdn.net/qq_47406941/article/details/109082884
一、字符指针
字符指针有以下两种使用方法:
1.字符指针“存储”单个字符
char c = 'a';
char* pc = &c;
*pc = 'a';
2.字符指针“存储”字符串
char* str = "hello world";
printf("%s\n",str);//打印结果:hello world
我们知道,当指针“存储”单个字符时,实际上指针存储的是该字符的地址,当对该字符指针进行解引用操作时直接访问该地址即可。那么,指针“存储”字符串是怎么存储的呢?
首先,我们知道指针的大小只有四个字节的空间,而且指针存储的是变量的地址,因此指针不可能真正的“存储”一个字符串。那么指针到底是如何存储字符串的呢?
实际上,指针存储字符串时存储的是字符串首元素的地址。在访问该字符串时,先通过指针找到首元素的地址,在根据首元素的地址访问字符串中的元素,直到遇到结束标志符‘\0’停止访问。
判断下面输出语句的输出结果:
这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域, 当几个指针指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始 化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4不同。
二、数组指针
数组指针是指指向数组的指针。数组指针与其他类型指针一样,每+1加类型个字节(例如int型加一加四个字节,char型加一加一个字节,数组指针每加一加数组大小个字节)。
数组指针的定义:
char (*p)[10];
由于[]的优先级高于*的优先级,所以要给*p加上括弧变成(*p)表明p是一个指针类型。而后面的[10]说明的是该类指针是一个含有10个char型变量的数组指针。
辨析数组的地址和数组首元素的地址:
有以下数组
int a[10];
a和&a各表示什么呢?哪一个是数组的地址,那一个是数组首元素的地址?
很明显,a是数组首元素的地址,&是数组的地址 。
从图中我们可以看出,a的值和&a的值相等,而a+1的值比&a+1的值小40(10个整型的大小,刚好是该数组a的大小)。所以,可以得出结论:数组的地址和数组首元素的地址的值是相等的,但是数组的地址+1加的是整个数组,首元素的地址+1加的是一个元素的大小。
三、指针数组
指针数组是指存储指针变量的数组,指针数组的定义如下:
int* arr1[10]; //整形指针的数组
char *arr2[4]; //一级字符指针的数组
char **arr3[5];//二级字符指针的数组
四、数组传参和指针传参
一维数组传参
一位数组的传参有以下几种方式:
int main()
{
int arr[10]={0};
test(arr);
}
void test(int arr[]){}//数组传参用数组接受肯定是可以的
void test(int* arr){}//数组传参传的时首元素的地址,所以也可以用指针来接受
判断下列一位数组传参是否正确:
int main()
{
int*arr[10]={0};
test(arr);
}
void test(int*arr[]){}//正确
void test(int**arr){}//正确;指针数组存储的是地址,传地址的地址用二级指针来接受也是没有问题的。
二维数组传参
规定,二维数组传参时数组的行数可以省略而列数不可以省略。所以,有以下传参方式:
void test(int arr[3][5])
void test(int arr[][5])
void test(int arr[3][])//这种省略列数的是不可行的
那么,下面这写二位数组的传参方式是否正确呢?
void test ( int * arr ) //二维数组的数组名指向的数组的首元素的地址,而二维数组的首元素是一个一维数组,所以应该用数组指针来接收{}void test ( int* arr [ 5 ]) //这里的参数是一个指针数组,因此错误{}void test ( int ( * arr )[ 5 ]) //正确{}void test ( int ** arr ) //错误,二维数组的数组名是数组指针不是二级指针{}int main(){int arr[3][5];test(arr);}
一级指针传参
一级指针传参有下面两种方式:
int main()
{
int a = 1;
int* b = 'c';
test(&a);//传变量的地址
test(b);//传指针类型的变量
}
void test(int*a){}
二级指针传参
二级指针传参有以下几种方式:
int main()
{
char c = 'b';
char*pc = &c;
char**ppc = &pc;
char* arr[10];
test(&pc);//传一级指针的地址
test(ppc);//传二级指针变量
test(arr);//传指针数组的数组名
return 0;
}
void test(char** p){}
无、函数指针
函数指针就是指指向函数的指针。例如:void test(){}对函数名test取地址(&test)就是一个函数指针,那么函数指针如何定义呢?
函数指针的定义
void (*pfun)();//变量pfun就是一个函数指针
函数指针的使用---改进的冒泡排序
冒泡排序的算法我们都很熟悉,依次遍历一组数据,每一次遍历让最小(最大)的数遍历到数组的开头,经过n-1次的遍历将数组变成一个降序(升序)的数组。那么我们有时可能既要对一组数进行升序排序又要进行降序排序,这样的话我们就要写两个函数依次进行升序和降序,但是,冒泡排序升序和降序程序大多数代码都是相同的,如果写两个代码的复用率就会大大降低,此时我们就可以采用函数指针来实现这个要求。代码如下:
void PopSort(int* nums,int numsSize,int(*p)(int,int))
{
int i = 0,j;
int mid = 0;
for (j = 0; j < numsSize - 1;j++)
for (i = 0; i < numsSize - 1;i++)
if (p(nums[i],nums[i+1]))
{
mid = nums[i];
nums[i] = nums[i+1];
nums[i + 1] = mid;
}
}
int GreaterPopSort(int x,int y)
{
return x > y ? 1 : 0;
}
int LessPopSort(int x, int y)
{
return x < y ? 1 : 0;
}
int main()
{
int i = 0;
int nums[] = {1,5,6,3,2,8,9,4,7};
//升序
PopSort(nums, 9, &GreaterPopSort);
for (i = 0; i<9; i++)
printf("%2d", nums[i]);
printf("\n");
//降序
PopSort(nums, 9, &LessPopSort);
for (i = 0; i<9; i++)
printf("%2d", nums[i]);
printf("\n");
return 0;
}
运行结果如下:
六、 函数指针数组
函数指针数组是存放函数指针的数组。定义如下:
int (*prt[10])();//表示数组ptr含有十个元素,每个元素都是一个int(*)()的指针
七、 指向函数指针数组的指针
指向函数指针数组的指针是一个指针,这个指针存储的是一个数组的地址,这个数组存的类型时函数指针类型。定义如下:
void (*(*ppfunArr)[10])(const char*)
八、回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一 个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该 函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或 条件进行响应。
例如:上面我们讲过的函数指针解决冒泡排序就是一个回调函数的应用。当我们把GreaterPopSort或者LessPopSort的地址传递给PopSort函数时,PopSort在执行到相应地方时就会去调用这两个函数。
判断下列输出语句的输出结果:
part1:
part2:
part3:
part4:
:
part5
判断下列程序运行结果:
程序1:
int main (){int a [ 5 ] = { 1 , 2 , 3 , 4 , 5 };int * ptr = ( int * )( & a + 1 );printf ( "%d,%d" , * ( a + 1 ), * ( ptr - 1 ));//2,5;*(a+1)=a[1]=2,*(ptr-1)=a[4]=5;return 0 ;}// 程序的结果是什么?
程序2:
程序3
int main (){int a [ 4 ] = { 1 , 2 , 3 , 4 };int * ptr1 = ( int * )( & a + 1 );//数组最后一个元素的下一个位置int * ptr2 = ( int * )(( int ) a + 1 );printf ( "%x,%x" , ptr1 [ - 1 ], * ptr2 );//4,2000000return 0 ;}
程序4
#include <stdio.h>int main (){int a [ 3 ][ 2 ] = { ( 0 , 1 ), ( 2 , 3 ), ( 4 , 5 ) };int * p ;p = a [ 0 ];//二维数组的第一行printf ( "%d" , p [ 0 ]);//1,逗号表达式的结果为最后一个值return 0 ;}
程序5
int main (){int a [ 5 ][ 5 ];int ( * p )[ 4 ];p = a ;//二维数组printf ( "%p,%d\n" , & p [ 4 ][ 2 ] - & a [ 4 ][ 2 ], & p [ 4 ][ 2 ] - & a [ 4 ][ 2 ]);//FFFFFFFC,-4return 0 ;}
程序6
int main (){int aa [ 2 ][ 5 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };int * ptr1 = ( int * )( & aa + 1 );int * ptr2 = ( int * )( * ( aa + 1 ));printf ( "%d,%d" , * ( ptr1 - 1 ), * ( ptr2 - 1 ));//10,5return 0 ;}
程序7
#include <stdio.h>int main (){char * a [] = { "work" , "at" , "alibaba" };char** pa = a ;pa ++ ;printf ( "%s\n" , * pa );//atreturn 0 ;}
程序8
int main (){char * c [] = { "ENTER" , "NEW" , "POINT" , "FIRST" };char** cp [] = { c + 3 , c + 2 , c + 1 , c };char*** cpp = cp ;printf ( "%s\n" , **++ cpp );printf ( "%s\n" , *--*++ cpp + 3 );printf ( "%s\n" , * cpp [ - 2 ] + 3 );printf ( "%s\n" , cpp [ - 1 ][ - 1 ] + 1 );return 0 ;}//结果POINT
ER
ST
EW