(C语言)进阶指针

一、字符指针

1、字符指针的一般使用:

int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'w';
	return 0;
}

2、字符指针的另一种使用方式

int main()
{
	char* ps = "hello world";
		printf("%s\n",ps);
	return 0;
}

代码分析:

①ps中存放的是字符串“hello world”第一个字符的地址。

②解引用打印的是h,打印字符串是打印的一整串字符。

③不能解引用改掉字符串“hello world”中的字符。因为“hello world”是字符常量。如果解引用改的话,会让程序挂掉。所以一般在这种使用方式之前加上const

④如果两个指针都存放的是同一个字符常量,那么其中存放的首字符地址也是相同的。

⑤与数组的区别:数组是开辟一块空间,将字符串存储在里面。而字符指针只是保存的字符常量sh首字符的地址。

二、指针数组

1、指针数组是存放指针变量的数组

2、指针数组的使用

int main()
{
	int a[] = { 1, 2, 3, 4, 5 };
	int b[] = { 2, 3, 4, 5, 6 };
	int c[] = { 3, 4, 5, 6, 7 };
	int* arr[3] = { a, b, c };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5;j++)
		{
			printf("%d ", *(arr[i] + j));
			//printf("%d ",arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

代码分析:上述代码类似于二维数组,但是不是二维数组。

三、数组指针

1、数组指针是一种指针,其中存放的是一个数组的地址,数组名表示的是数组首元素的地址,而&数组名表示的是整个数组的地址。

2、数组指针的表示

#include<stdio.h>
int main()
{ int arr[10]={1,2,3,4,5};
  int (*parr)[10]=&arr;//数组指针指向的是一个数组
    return 0;
}

int  (*parr)[10];   (*parr)表示一个存放地址的指针变量,(*parr)[10]表示的是指针变量指向一个包含十个元素的数组,int (*parr)[10]表示的是这个指针变量所指向的数组,数组中的元素类型为int类型。

☆☆[ ]中的数字不能省略

3、数组指针的使用(一般用在二维数组中)

①在一维数组中(在一维数组中使用有点南辕北辙)

int main()
{
	int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int(*parr)[10] = &arr;
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *((*parr) + i));
	}
	return 0;
}

②在二维数组中

void print(int (*parr)[5], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("%d ", *(*(parr + i) + j));
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { { 1, 2, 3, 4, 5 }, { 2, 3, 4, 5, 6 }, { 3, 4, 5, 6, 7 } };
	print(arr, 3, 5);
	return 0;
}

二维数组的数组名表示的是首元素的地址,二维数组的首元素为{1,2,3,4,5},所以首元素的地址为一个数组的地址。

4、分析

int(*parr)[10];数组指针,该指针指向一个数组,数组中有十个元素,每个元素是int类型。

int (*parr[10])[5];parr是一个可以存放十个数组指针的数组,存放的每个数组指针可以指向一个数组,数组中有五个元素,每个元素是int类型。

四、数组传参和指针传参

1、一维数组传参

void test(int arr[])//数组传参方式一
{};
void test(int arr[10])//数组传参方式二
{};
void test(int *arr)//指针传参
{};
void test2(int* arr[20])
{};
void test2(int* *arr)
{};
int main()
{
	int arr[10] = { 0 };
	int *arr2[20] = { 0 };
	test(arr);
	test2(arr2);
	return 0;
}

2、二维数组传参

void test(int arr[3][5])//数组传参方式一
{};
void test(int arr[][5])//数组传参方式二
{};
void test(int arr[][])//❌
{};
void test(int *arr)//❌
{};
void test(int *arr[5])//指针数组,❌
{};
void test(int(*arr)[5])//数组指针,指针传参方式
{};
void test(int **arr)//❌
{};
int main()
{
	int arr[3][5] = { 0 };
	test(arr);
	return 0;
}

3、一级指针传参

void test1(int* p)//函数都用一级指针来接收
{};
void test2(int *p)
{};
void test3(int *pa)
{};
void test4(int* pa)
int main()
{
	int arr[10];
	int a = 100;
	int* pa = &a;
	int *parr = arr;
	test1(arr);//可以直接传地址过去
	test2(parr);//可以传指针变量过去
	test3(&a);
	test4(pa);
	return 0;
}//当传递地址给函数时,函数需用指针来接收。
//当函数用指针来接收时,需要传递地址给它或者传递指针变量给它。

4、二级指针传参

void test1(int** pp)//函数都会用二级指针来接收
{};
void test2(int** pp)
{};
void test3(int** pp)
{};
int main()
{
	int a = 10;
	int* arr[10];
	int* pa = &a;
	int** ppa = &pa;
	test1(ppa);//可以传递二级指针的变量名
	test2(&pa);//可以传递一级指针的地址
	test3(arr);//可以传递一级指针数组的数组名
	return 0;
}//当一个一级指针地址传给函数时,函数需要用二级指针来接收。
//当函数用一个二级指针来接收时,传递给它的参数可以是二级指针的变量名、一级指针的地址和一级指针数组的数组名。

五、函数指针

1、存放函数地址的指针叫做函数指针。&函数名和函数名都表示的是函数的地址。

2、函数指针的表示

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*padd)(int, int) = Add;
	return 0;
}

①int (*)(int,int)这是函数指针类型

②padd是变量-->*padd是一个指针变量-->int (*padd)(int,int)一个指针,其中存放的是一个参数为两个int,返回类型为int的函数的地址。

3、函数指针的使用

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*padd)(int, int) = Add;//可以得到padd==Add
	int ret = (*padd)(3, 5);//一般的函数调用是Add(3,5)-->padd(3,5)   所以这三种方法是完全等价的
	printf("%d\n",ret);
	return 0;
}

①函数指针的使用可以写成三种方式:(*padd)(3,5)、padd(3,5)、Add(3,5)

4、分析

void (*signal(int,void(*)(int)))(int);

代码分析:signal(int,void(*)(int))说明signal是一个函数名,而()内的为函数参数,一个参数为int类型,另一个参数为函数指针类型(此函数指针类型为参数为int类型,返回值为void类型的),而返回类型也为函数指针类型。

易于理解的书写方式(但是不和语法的):void(*)(int)  signal(int,void(*)(int))

但是可以用typedef进行类型重定义,重定义函数指针类型的书写方式为:

typedef  void (*pfun_t)(int);    //正确的书写方式

typedef void(*)(int)   pfun_t;  //错误的书写方式

六、函数指针数组

1、存放函数指针的数组,本质是一个数组。

2、函数指针数组的表示方式

int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int main()
{
	int(*pf1)(int, int) = Add;//分开存放的方式
	int(*pf2)(int, int) = Sub;
	int(*pf[2])(int, int) = {Add,Sub};//用函数指针数组进行存放
	return 0;
}

代码分析:pf先和[ ]结合-->pf[2]说明是一个数组,int(*)(int,int)是一个函数指针类型-->所以

int(*pf[2])(int,int)是一个函数指针类型的数组。

3、函数指针数组的使用

int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x*y;
}
int Div(int x, int y)
{
	return x / y;
}
int main()
{
	int a = 0;
	int b = 0;
	int input = 0;
	scanf("%d",&input);
	scanf("%d%d",&a,&b);
	int(*ppp[5])(int, int) = {NULL,Add,Sub,Mul,Div};
	int ret = (ppp[input])(a, b);
	printf("%d\n",ret);
	return 0;
}

这个数组被称为转移表。转移表是函数指针数组的应用。

七、指向函数指针数组的指针

1、指针取出的是函数指针数组的地址。

2、指向函数指针数组的指针的表示方式

int(*p)(int,int)函数指针-->int(*p[2])(int,int)函数指针的数组-->int(*(*p)[2])(int,int)指向函数指针数组的指针。

int (*pfArr[5])(int,int)={NULL,Add,Sub,Mul,Div};
int (*(*p)[5])(int,int)

八、回调函数

1、回调函数就是一个通过函数指针调用的函数,如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

2、回调函数的理解

int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x*y;
}
int Div(int x, int y)
{
	return x / y;
}
int Calc(int(*pf)(int, int))
{
	int a = 0;
	int b = 0;
	int input = 0;
	scanf("%d", &input);
	scanf("%d%d", &a, &b);
	return pf(a, b);
}
int main()
{
	int input = 0;
	scanf("%d", &input);
	int res = Calc(Add);
	printf("%d\n",res);
	return 0;
}

利用回调函数实现的库函数,如qsort(快排序),现用冒泡排序模仿qsort的实现:

void qsort(void*base,size_t num,size_t size,int(*compar)(const void*,const void*))

第一个参数为待排序数据中第一个元素的地址。

第二个参数为待排序数据的个数。

第三个参数为待排序数据一个元素的大小。

第四个参数为比较两个数据的函数。

      第四个参数两个参数,第一个参数-第二个参数,返回值>0说明前一个数比后一个数大,那么进行交换,返回值为0说明两个数一样大,返回值<0说明第一个数小于第二个数,不进行交换。所以就将数据升序排列了。想要降序排列就让第四个参数的第二个参数-第一个参数,就完成了降序排列。

九、指针和数组面试题的解析

表达式有两个属性,一个是值属性,一个是类型属性。值属性是得到它的值,而类型属性是得到它的类型。

sizeof(),()中的内容是不计算的。

​
#define INT_A int*
typedef int* int_a;
INT_A a, b;
int_a c, d;

​

上述四个变量b是整型,其余三个都是int*类型的。

因为#define只是简单的替换,将int*替换成了INT_A因此,相当于int* a,b;

这个代码就相当于是int  *a;int b;

要想定义两个int*类型就应该写成int *a,*b;

而typedef   是将int*  ,int_a等价,所以int_a c,d;

就相当于int *a,*b;

可以与此类比的如float类型,float   e,f;那么e、f都是float类型,int_a与此相同。

int main()
{
	short s = 5;
	int a = 4;
	printf("%d\n",sizeof(s=a+6));//2  因为两个整型计算完之后放到了short类型里面
	printf("%d\n",s);// 5  因为sizeof()里面的表达式不参与运算。
	return 0;
}
 
// 一维数组
int a [] = { 1 , 2 , 3 , 4 };
printf ( "%d\n" , sizeof ( a ));
printf ( "%d\n" , sizeof ( a + 0 ));
printf ( "%d\n" , sizeof ( * a ));
printf ( "%d\n" , sizeof ( a + 1 ));
printf ( "%d\n" , sizeof ( a [ 1 ]));
printf ( "%d\n" , sizeof ( & a ));
printf ( "%d\n" , sizeof ( *& a ));
printf ( "%d\n" , sizeof ( & a + 1 ));
printf ( "%d\n" , sizeof ( & a [ 0 ]));
printf ( "%d\n" , sizeof ( & a [ 0 ] + 1 ));
// 字符数组
char arr [] = { 'a' , 'b' , 'c' , 'd' , 'e' , 'f' };
printf ( "%d\n" , sizeof ( arr ));
printf ( "%d\n" , sizeof ( arr + 0 ));
printf ( "%d\n" , sizeof ( * arr ));
printf ( "%d\n" , sizeof ( arr [ 1 ]));
printf ( "%d\n" , sizeof ( & arr ));
printf ( "%d\n" , sizeof ( & arr + 1 ));
printf ( "%d\n" , sizeof ( & arr [ 0 ] + 1 ));
printf ( "%d\n" , strlen ( arr ));
printf ( "%d\n" , strlen ( arr + 0 ));
printf ( "%d\n" , strlen ( * arr ));
printf ( "%d\n" , strlen ( arr [ 1 ]));
printf ( "%d\n" , strlen ( & arr ));
printf ( "%d\n" , strlen ( & arr + 1 ));
printf ( "%d\n" , strlen ( & arr [ 0 ] + 1 ));
char arr [] = "abcdef" ;
printf ( "%d\n" , sizeof ( arr ));
printf ( "%d\n" , sizeof ( arr + 0 ));
printf ( "%d\n" , sizeof ( * arr ));
printf ( "%d\n" , sizeof ( arr [ 1 ]));
printf ( "%d\n" , sizeof ( & arr ));
printf ( "%d\n" , sizeof ( & arr + 1 ));
printf ( "%d\n" , sizeof ( & arr [ 0 ] + 1 ));
printf ( "%d\n" , strlen ( arr ));
printf ( "%d\n" , strlen ( arr + 0 ));
printf ( "%d\n" , strlen ( * arr ));
printf ( "%d\n" , strlen ( arr [ 1 ]));
printf ( "%d\n" , strlen ( & arr ));
printf ( "%d\n" , strlen ( & arr + 1 ));
printf ( "%d\n" , strlen ( & arr [ 0 ] + 1 ));
char * p = "abcdef" ;
printf ( "%d\n" , sizeof ( p ));
printf ( "%d\n" , sizeof ( p + 1 ));
printf ( "%d\n" , sizeof ( * p ));
printf ( "%d\n" , sizeof ( p [ 0 ]));
printf ( "%d\n" , sizeof ( & p ));
printf ( "%d\n" , sizeof ( & p + 1 ));
printf ( "%d\n" , sizeof ( & p [ 0 ] + 1 ));
printf ( "%d\n" , strlen ( p ));
printf ( "%d\n" , strlen ( p + 1 ));
printf ( "%d\n" , strlen ( * p ));
printf ( "%d\n" , strlen ( p [ 0 ]));
printf ( "%d\n" , strlen ( & p ));
printf ( "%d\n" , strlen ( & p + 1 ));
printf ( "%d\n" , strlen ( & p [ 0 ] + 1 ));
// 二维数组
int a [ 3 ][ 4 ] = { 0 };
printf ( "%d\n" , sizeof ( a ));
printf ( "%d\n" , sizeof ( a [ 0 ][ 0 ]));
printf ( "%d\n" , sizeof ( a [ 0 ]));
printf ( "%d\n" , sizeof ( a [ 0 ] + 1 ));
printf ( "%d\n" , sizeof ( * ( a [ 0 ] + 1 )));
printf ( "%d\n" , sizeof ( a + 1 ));
printf ( "%d\n" , sizeof ( * ( a + 1 )));
printf ( "%d\n" , sizeof ( & a [ 0 ] + 1 ));
printf ( "%d\n" , sizeof ( * ( & a [ 0 ] + 1 )));
printf ( "%d\n" , sizeof ( * a ));
printf ( "%d\n" , sizeof ( a [ 3 ]));
数组名的意义:
1. sizeof( 数组名 ) ,这里的数组名表示整个数组,计算的是整个数组的大小。
2. & 数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。

题目一:

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}
//程序的结果是什么?

题目二:

//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
 int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
 printf("%p\n", p + 0x1);
 printf("%p\n", (unsigned long)p + 0x1);
 printf("%p\n", (unsigned int*)p + 0x1);
 return 0;
}

题目三:

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);
    return 0;
}

题目四:

#include <stdio.h>
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;
    p = a[0];
    printf( "%d", p[0]);
 return 0;
}

题目五:

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]);
    return 0;
}

题目六:

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));
    return 0;
}

题目七:

#include<stdio.h>
int main()
{
	char* a[] = {"work","at","alibaba"};
	char** pa = a;
	pa++;
	printf("%s\n",*pa);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不会算法的笨小孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值