完全自学C(干货) —— 指针进阶

目录

一,字符指针

二,指针数组

三,数组指针

四,数组传参/指针传参

五,函数指针

六,函数指针数组

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

八,回调函数

九,练习


  • 字符指针;
  • 数组指针/指针数组;
  • 数组传参/指针传参;
  • 函数指针/函数指针数组;
  • 指向函数指针数组的指针;
  • 回调函数

一,字符指针

指向字符类型的指针,即字符指针(char*);

char ch = 'a';
char* pch = &ch;
int main()
{
	//注将字符串赋值予字符串指针,其实时将首元素的地址赋予指针
	char* pch = "abcd";
	printf("%c\n", *pch);
	return 0;
}
//结果:a
//参考<<剑指offer>>
int main()
{
	//str1,str2分别开辟内存空间,地址不同
	char str1[] = "abcd";
	char str2[] = "abcd";

	//pch1、pch2都是指向常量"abcd"的地址,相同
	char* pch1 = "abcd";
	char* pch2 = "abcd";

	if (str1 != str2)
		printf("str1 != str2 ");
	if (pch1 == pch2)
		printf("pch1 == pch2 ");

	return 0;
}
//结果:str1 != str2 pch1 == pch2

二,指针数组

存放指针的数组,即数组元素为指针类型;

int main()
{
	int a = 1;
	int b = 2;
	int c = 3;
	int* parr[] = { &a,&b,&c }; //指针数组
	return 0;
}
int main()
{
	int a[] = { 1,2,3,4,5 };
	int b[] = { 2,3,4,5,6 };
	int c[] = { 3,4,5,6,7 };
	int* parr[] = { a,b,c };//指针数组

	int i = 0;
	for (i; i < 3; i++)
	{
		int j = 0;
		for (j; j < 5; j++)
		{
			printf("%d ", parr[i][j]); //或*(parr[i]+j)或*(*(parr + i) + j)
		}
		printf("\n");
	}
	return 0;
}

三,数组指针

是指针,存放指向整个数组地址的指针,如整型指针、字符指针;

int arr[5] = { 1,2,3,4,5 };
int(*parr)[5] = &arr; //数组指针
char str[5] = "abcd"; //字符数组
char* arr[5] = { arr,arr + 1,arr + 2,arr + 3 }; //指针数组
char* (*parr)[5] = &arr; //数组指针

数组名含义

  • arr,数组首元素的地址;
  • &arr,取整个数组的地址;
  • sizeof arr,取的也是整个数组;

注,+1跳过的元素是不一样的;

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	printf("%p %p\n", arr, arr + 1);
	printf("%p %p\n", &arr[0], &arr[1]);
	printf("%p %p\n", &arr, &arr + 1);
	return 0;
}
//结果:
//00EFFD60 00EFFD64
//00EFFD60 00EFFD64
//00EFFD60 00EFFD74
int arr[5]; //是一个包含5个元素的数组,每个元素为整型
int* parr[10]; //是一个包含5个元素的数组,每个元素为整型指针
int(*parr)[10]; //是一个数组指针,指针一个包含10个整型元素的数组
int(*parr[5])[10]; //是一个指针数组,包含5个元素,每个元素为数组指针,该数组包含10个整型元素

四,数组传参/指针传参

数组传参,即将数组作为函数的参数;

//一维数组传参
void test1(int arr1[10]) //或int arr1[],或int* p1 
{}

void test2(int* arr2[10]) //或int* arr2[],或int** p2 
{}

int main()
{
	int arr1[10] = { 0 };
	int* arr2[10] = { 0 };
	test1(arr1); //arr1,首元素地址,即指针
	test2(arr2); //arr2,首元素地址,即指针
    return 0;
}
//二维数组传参
void test(int arr[3][5]) //或int arr[][5],或int(*arr)[5]
{}

int main()
{
	int arr[3][5] = { 0 };
	test(arr); //arr,首元素地址,即指针
    return 0;
}

指针传参,即将指针作为函数的参数;

//一级指针传参
void print(int* p, int sz) 
{
	int i = 0;
	for (i; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
}

int main()
{
	int arr[] = { 1,2,3,4,5,6 };
	int* p = arr;
	int sz = sizeof arr / sizeof arr[0];
	print(p, sz);
	return 0;
}
//二级指针传参
void print(int(**pp)[6], int sz)
{
	int i = 0;
	for (i; i < sz; i++)
	{
		printf("%d ", *(**pp + i));
	}
}

int main()
{
	int arr[6] = { 1,2,3,4,5,6 };
	int(*p)[6] = &arr;
	int(**pp)[6] = &p;
	int sz = sizeof arr / sizeof arr[0];
	print(pp, sz);
	return 0;
}

五,函数指针

  • 是指向函数的指针;
  • &函数名 == 函数名,都是函数的地址;
void test()
{
	;
}

int main()
{
	printf("%p ", test);
	printf("%p ", &test);
    return 0;
}
//结果:006A1253 006A1253
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	//函数指针
	int(*pfun)(int, int) = Add; //或 &Add
	printf("%d ", Add(1, 2));
	printf("%d ", (*pfun)(1, 2)); //调用函数(*可省略或写多个*均可);
	printf("%d ", pfun(1, 2));
	return 0;
}
//结果:3 3 3
  • 如上案例,*可省略,pfun == add,add == pfun;
  • 可重定义函数指针类型,如 typedef int( *pfun_t )(int,int) ;

注:《C陷阱和缺陷》—— 案例;

//void(*)() - 函数指针
//(void(*)())0 - 强制将整型0,转换为函数指针类型
//*(void(*)())0 - 解引用函数指针
//(*(void(*)())0)() - 函数调用
(*(void(*)())0)();
//void(*)(int) - 函数指针类型,返回类型为void,参数类型为int
//signal - 函数返回类型void(*)(int),也是函数指针
//signal - 函数参数类型int, void(*)(int)
void(*signal(int, void(*)(int)))(int);

//可重定义函数指针,typedef void(*pfun_t)(int) 
//则可简写为,pfun_t signal(int, pfun_t)

六,函数指针数组

存放同类型函数指针的数组,即函数指针数组;

//parr有10个元素,每个元素类型为int(*)()
int(*parr[10])();

函数指针数组的用途:转移表 ——《C和指针》

void menu()
{
	printf("===========================\n");
	printf("==1,Add 2,Sub 3,Mul 4,Div==\n");
	printf("===========================\n");
}
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 input = 0;
	int x = 0, y = 0;
	int(*pfun[])(int, int) = { 0,Add,Sub,Mul,Div }; //函数指针数组
	do
	{
		menu();
		printf("请选择:");
		scanf("%d", &input);
		if (input >= 1 && input <= 4)
		{
			printf("请输入待运算的数:");
			scanf("%d%d", &x, &y);
			printf("%d\n", pfun[input](x, y));
		}
		else if (0 == input)
			printf("退出\n");
		else
			printf("输入错误重新输入!\n");
	} 	while (input);
}

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

  • 是一个指针;
  • 指针指向一个数组 ;
  • 数组的元素为函数指针;
//函数指针
void(*pfun)(int);

//函数指针数组
void(*pfunArr[5])(int);

//指向函数指针数组的指针
void(*(*pfunArr)[5])(int);

八,回调函数

  • 是指通过函数指针调用的函数;
  • 函数指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,即称为回调函数;
  • 不是直接调用函数,而是特定事件或条件发生时由另一方调用;
void menu()
{
	printf("===========================\n");
	printf("==1,Add 2,Sub 3,Mul 4,Div==\n");
	printf("===========================\n");
}
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;
}
void calc(int(*pfun)(int, int)) //回调函数
{
	int x = 0, y = 0;
	printf("请输入待运算数:");
	scanf("%d%d", &x, &y);
	printf("%d\n", pfun(x, y));
}
int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请输入:");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出!\n");
			break;
		case 1:
			calc(Add); 
			break;
		case 2:
			calc(Sub); 
			break;
		case 3:
			calc(Mul); 
			break;
		case 4:
			calc(Div); 
			break;
		default:
			printf("输入错误重新输入!\n");
			break;
		}
	} 	while (input);
    return 0;
}

九,练习

  • 数组名代表首元素的地址;
  • 但sizeof arr,&arr,其中arr表示整个数组;
//一维数组
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //16,整个数组大小
printf("%d\n", sizeof(a + 0)); //4/8,首元素地址大小
printf("%d\n", sizeof(*a)); //4,首元素大小
printf("%d\n", sizeof(a + 1)); //4/8,第二元素地址大小
printf("%d\n", sizeof(a[1])); //4,第二元素大小

printf("%d\n", sizeof(&a)); //4/8,整个数组地址大小
printf("%d\n", sizeof(*&a)); //16,整个数组大小
printf("%d\n", sizeof(&a + 1)); //4/8,整个数组地址的下一个地址大小
printf("%d\n", sizeof(&a[0])); //4/8,首元素地址大小
printf("%d\n", sizeof(&a[0] + 1)); //4/8,第二元素地址大小
//字符数组
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); //6,整个数组大小
printf("%d\n", sizeof(arr + 0)); //4/8,首元素地址大小
printf("%d\n", sizeof(*arr)); //1,首元素大小
printf("%d\n", sizeof(arr[1])); //1,第二元素大小
printf("%d\n", sizeof(&arr)); //4/8,整个数组地址大小
printf("%d\n", sizeof(&arr + 1)); //4/8,整个数组地址的下一个地址大小
printf("%d\n", sizeof(&arr[0] + 1)); //4/8,第二元素地址大小

//size_t strlen( const char *string ),参数为字符指针
printf("%d\n", strlen(arr)); //随机,首元素地址
printf("%d\n", strlen(arr + 0)); //随机,首元素地址
printf("%d\n", strlen(*arr)); //err,首元素
printf("%d\n", strlen(arr[1])); //err,第二元素
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)); //7,整个数组大小
printf("%d\n", sizeof(arr + 0)); //4/8,首元素地址大小
printf("%d\n", sizeof(*arr)); //1,首元素大小
printf("%d\n", sizeof(arr[1])); //1,第二元素大小
printf("%d\n", sizeof(&arr)); //4/8,整个数组地址大小
printf("%d\n", sizeof(&arr + 1)); //4/8,整个数组地址的下一个地址大小
printf("%d\n", sizeof(&arr[0] + 1)); //4/8,第二元素地址大小

//size_t strlen( const char *string ),参数为字符指针
printf("%d\n", strlen(arr)); //6,首元素地址
printf("%d\n", strlen(arr + 0)); //6,首元素地址
printf("%d\n", strlen(*arr));  //err,首元素
printf("%d\n", strlen(arr[1])); //err,第二元素
printf("%d\n", strlen(&arr)); //6,首元素地址(由整个数组地址类型转换)
printf("%d\n", strlen(&arr + 1)); //随机,整个数组地址的下个地址
printf("%d\n", strlen(&arr[0] + 1)); //5,第二元素地址
//字符指针
char* p = "abcdef";
printf("%d\n", sizeof(p)); //4/8,字符指针大小
printf("%d\n", sizeof(p + 1)); //4/8,字符指针+1大小
printf("%d\n", sizeof(*p)); //1,字符'a'大小
printf("%d\n", sizeof(p[0])); //1,字符'a'大小
printf("%d\n", sizeof(&p)); //4/8,字符指针的地址大小
printf("%d\n", sizeof(&p + 1)); //4/8,字符指针地址的下个地址大小
printf("%d\n", sizeof(&p[0] + 1)); //4/8,字符'b'的地址大小

//size_t strlen( const char *string ),参数为字符指针
printf("%d\n", strlen(p)); //6,从字符'a'开始
printf("%d\n", strlen(p + 1)); //5,从字符'b'开始 
printf("%d\n", strlen(*p)); //err,首元素
printf("%d\n", strlen(p[0])); //err,首元素
printf("%d\n", strlen(&p)); //随机,字符指针的地址
printf("%d\n", strlen(&p + 1)); //随机,字符指针+1的地址
printf("%d\n", strlen(&p[0] + 1)); //5,字符'b'地址
//二维数组
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); //48,整个数组的大小
printf("%d\n", sizeof(a[0][0])); //4,首元素的首元素的大小

printf("%d\n", sizeof(a[0])); //16,首元素的大小,a[0]代表整个首元素数组

printf("%d\n", sizeof(a[0] + 1)); //4/8,首元素的第二个元素地址大小
printf("%d\n", sizeof(*(a[0] + 1))); //4,首元素的第二个元素大小

printf("%d\n", sizeof(a + 1)); //4/8,第二元素的地址大小
printf("%d\n", sizeof(*(a + 1))); //16,第二元素的大小

printf("%d\n", sizeof(&a[0] + 1)); //4/8,第二元素地址大小
printf("%d\n", sizeof(*(&a[0] + 1))); //16,第二元素的大小

printf("%d\n", sizeof(*a)); //16,首元素的大小

printf("%d\n", sizeof(a[3])); //16,第四元素的大小(越界访问)
//虽然看起来是越界访问,但sizeof只计算类型属性,括号内表达式并未实际访问或计算;
//a[3]的类型为int [4];
//sizeof只计算类型属性,括号内表达式并未实际访问或计算
short s = 5;
int a = 4;
printf("%d\n", sizeof(s = a + 6)); //2
printf("%d\n", s); //5

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值