C语言指针进阶

1.以下程序输出的值为:

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;
}

答案是:10/5;

首先:初始化二维数组;切记:二位数组的首元素地址是第一行元素;初始化的二维数组是两行五列的数组;

&aa+1表示在首元素地址的基础之上跳过一个数组;来到下一个数组首元素的地址;强制类型转换为整型,储存在整型指针ptr1中;需要特别注意:&aa和&aa[0][0];所以  *(ptr1-1)表示下一数组的首元素地址-1,也就是第一个数组最后元素的地址;解引用得到10

aa是一个二维数组,aa表示二维数组的首元素,也就是二维数组第一行的元素;aa+1表示二维数组第二行的元素;解引用强制类型转换为整型得到数组第二行元素的数组名,来到第二行首元素的地址;ptr2-1来到第一行最后一个元素的地址;解引用得到对应的元素;强制类型转换为整型,储存在整型指针ptr2中。所以*(ptr2-1)等于第一行最后一个元素的大小,也就是5

注意:*(aa+1)等价于aa[1];

2.以下程序输出的结果是:

int main()
{
	char *a[] = { "work", "at", "alibaba" };
	char **pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

答案是:at;

首先:初始化指针数组,数组中包含三个字符串work、at、alibaba;然后把一维指针数组a赋给*pa,类型为char*;数组传参,传过去的是首元素的地址;也就是a接收的是三个字符串的首元素,也就是w、a、a;pa++;表示在首元素的基础之上+1;来到第二个字符串,所以%s打印字符串得到的结果是at;

二维数组:取两次首地址;

3.以下程序输出的结果是:

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;

仔细斟酌上述三维指针所对应的数组关系:----------%s表示打印字符串;

首先初始化指针数组得到四个字符串;数组传参,传过去的是首元素的地址;也就是char *类型指针接收到的是E/N/P/F;

二维数组 cp[] 中含有四个元素,c+0/c+1/c+2/c+3 , c 表示一维数组,char**收到的是首元素的地址,也就是c+0对应char*----ENTER;以此类推c+1、c+2、c+3,在此一定要注意:首元素的对应关系;

三维数组cpp接收到的是二维数组cp的首元素地址位置;

前置++表示先加再运算;只要加上了就对以后的程序永久适用;

printf("%s\n", **++cpp);----------表示三维指针cpp前置++;char***最早处于c+3的位置,++之后来到c+2的位置;解引用得到c+2对应的一维指针char*,再次解引用得到一维指针char*对应的元素POINT;
printf("%s\n", *--*++cpp + 3);----------表示三维指针cpp前置++;在上一个基础之上进行+1,来到c+1的地址,解引用得到c+1对应的一维指针char*;然后--;来到上一个元素的位置,解引用得到ENTER的首元素E;然后再基础之上+3;在字符串ENTER上从首元素开始+3;到达第四个元素E;%s打印以后的字符,打印的结果是ER;

特别注意:*cpp[-2]等价于*(cpp-2);cpp[-1][-1]等价于*(*(cpp-1)-1);
printf("%s\n", *cpp[-2] + 3);-----------表示cpp-2向后-2,来到c+3的位置上,解引用得到c+3对应的一维数组char*;也就得到了字符串FIRST的首元素F;然后+3,向后移三个字符;来到S的地址;打印S以后的字符,得到ST;
printf("%s\n", cpp[-1][-1] + 1);----------表示三维指针-1;向后移动一个位置;来到c+2的位置上,解引用得到c+2对应的一维数组char*,然后在一维数组的基础之上-1;来到上一个一维数组char*,得到一维数组对应的字符串NEW的首元素N,然后在基础之上+1;来到E字符的地址,%s打印E之后的字符;得到结果EW;

4.以下程序输出的结果是:

int main()
{
	unsigned long pulArray[] = { 6, 7, 8, 9, 10 };
	unsigned long *pulPtr;

	pulPtr = pulArray;
	*(pulPtr + 3) = *(pulPtr + 3) + 3;
	printf("%d,%d\n", *pulPtr, *(pulPtr + 3));
	return 0;
}

答案是:6 , 12;

首先:数组传参,传过去的是首元素的地址;所以在初始化数组的指针以后,把数组赋值给指针,指针得到的是数组的首元素地址,pulPtr+3表示向后移动三个地址,得到9 的地址;解引用以后得到数字9;9+3=12赋值给*(pulPtr+3),所以最后*(pulPtr+3)=12;

*pulPtr是数组直接传参以后的结果,得到的是首元素的地址;解引用得到数字6;

5.写一个函数,可以逆序字符串的内容:

思路:首先初始化数组,记住,用char;用大括号0;scanf进行手动输入;给定函数reverse,把arr数组传过去,因为定义的是字符char,所以可以不用求sz,用strlen也可以求字符串长度;记住是用%s打印字符串;

因为定义的是char字符,所以函数用char*指针来接收;交换字符串的顺序,只需要对第一个和最后一个交换,依次向前向后循环即可;需要注意的是:定义right需要用char* 来定义;参数+len-1;然后进行交换,切记left++;right--;然后在while语句中进行循环;

用指针来接收时,一定注意所有都用定义的变量来表示,即用str来表示:之所以定义char* left=str;是因为数组传参,传过去的是首元素的地址;所以定义左边是首元素;右边的定义相应定义即可;

void reverse(char *str)
{
	int len = strlen(str);
	char* left = str;
	char* right = str + len - 1;
	while (left < right)
	{
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}
}
int main()
{
	char arr[256] = { 0 };
	scanf("%s", arr);
	reverse(arr);
	printf("%s\n", arr);
	return 0;
}

6.计算求和:求Sn=a+aa+aaa+aaaa+aaaaa的和;其中a是一个数字;

原理:两个未知数,我定义a,然后定义n;每个后边的一项都是前一项*10+a以后的结果;所以每每出现一个,我就加给sum; ret = ret * 10 + a 的意思是后一项等于前一项*10+a(每后一项都和前一项是有基本运算规律的);

int main()
{
	int a = 0;
	int n = 0;
	scanf("%d%d", &a, &n);
	int sum = 0;
	int i = 0;
	int ret = 0;
	for (i = 0; i < n; i++)
	{
		ret = ret * 10 + a;
		sum = sum + ret;
	}
	printf("%d\n", sum);
}

7.用C语言打印菱形的形状;

需要注意:两个循环语句一个打印菱形的上半部分,一个打印菱形的下半部分;定义line为菱形上半部分的行数,则下半部分循环line-1;菱形一定是奇数行;

两个 i 循环打印的是行;分别打印上半部分的行和下半部分的行;

上半部分的行循环中加入两个 j 循环,分别打印 空格* 号 ,但要明确,每个循环只是打印其中的一行,最后要加上换行\n;每个 j 循环只是为了打印出该行所需要打印几个 空格 * 号,每循环一次打印一次;所以j<   ;是为了循环打印出所需的符号;

int main()
{
	int line = 0;
	scanf("%d", &line);
	int i = 0;
	for (i = 0; i <line ; i++)
	{
		int j = 0;
		for (j = 0; j <line-1-i ; j++)
		{
			printf(" ");
		}
		for (j = 0; j <2*i+1 ; j++)
		{
			printf("*");
		}
		printf("\n");
	}
	for (i = 0; i <line-1 ; i++)
	{
		int j = 0;
		for (j = 0; j <=i; j++)
		{
			printf(" ");
		}
		for (j = 0; j <2 *(line-1-i)-1; j++)
		{
			printf("*");
		}
		printf("\n");
	}
	return 0;
}

8.编 程 实 现:喝汽水,1元一瓶汽水,每两个空瓶子可以换回一瓶汽水,20元可以喝多少瓶汽水?

思想:20块钱可以买20瓶水,喝完20瓶水会得到20个空瓶子,所以花多少钱就会得到多少个空瓶子,花多少钱就会喝多少瓶水。只要空瓶子大于等于2,我就可以拿去换水,换来水喝掉又会得到空瓶子。

循环体中首先定义喝掉的总数花的钱+第一次喝掉的空瓶子/2;

然后定义下一次循环体中的空瓶子数:这时需要特别注意:如果除以2留下来的空瓶子会在以后有用,所以需要计算上(如果是5瓶,除以2余1瓶;这个空瓶会和下一次换水得到的空瓶相加起来一起去换水);

int main()
{
	int money = 0;
	int empty = 0;
	int total = 0;
	scanf("%d", &money);
	total = money;
	empty = money;
	while (empty >= 2)
	{
		total =  empty / 2+total;
		empty = empty / 2 + empty % 2;
	}
	printf("total=%d\n", total);
	return 0;
}

9.编程实现:调整数组中数字的顺序使得数组中所有的奇数位于数组的前半部分,所有偶数位于数组的后半部分;

思想:从定义的数组左边去找偶数,从数组右边去找奇数,一旦找到,交换两个数,最终达到所有奇数都被交换都左边;但要注意,从左往右找偶数可能会出现找到数组的最后一直找不到偶数,从而出现栈溢出的现象;所以在从左往右找偶数和从右往左找奇数的过程中一定需要限制  left小于right。

主函数定义数组,求出数组元素个数,打印数组,定义交换函数,将arr和sz(数组元素个数)传过去;

交换函数:从左往右找偶数-----while循环,对二取余等于1表示是奇数,只要是奇数,left++跳过,循环结束后,默认我已经找到了偶数;从右往左找奇数是一样的;

两个循环结束后默认找到了相应的奇数偶数,但为了防止出现栈溢出的现象,left<right;然后进行交换,注意整个交换过程肯定不止一次,所以整个程序需要在循环体中进行;

void swap(int arr[], int sz)
{
	int left = 0;
	int right = sz - 1;
	while (left < right)
	{
		while ((left < right) && (arr[left] % 2 == 1))
		{
			left++;
		}
		while ((left < right) && (arr[right] % 2 == 0))
		{
			right--;
		}
		if (left < right)
		{
			int tmp = arr[left];
			arr[left] = arr[right];
			arr[right] = tmp;
		}
	}
}
int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	swap(arr, sz);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

10.输出以下程序的结果:

答案是: 1,6 ;

思想:该数组表示二维数组,&aa+1表示跳过整个数组的大小,来到下一个数组首元素的地址;强制类型转化为整型,存储在指针 ptr1 中,所以ptr1-1表示来到第一个数组最后一个元素的地址上,解引用得到最后一个元素的大小--------1;

aa+1表示在首元素的地址基础之上+1;二位数组首元素表示第一行元素的数组名,所以aa+1表示第二行元素,解引用得到第二行元素的数组名;强制类型转换为整型,传给指针ptr2,ptr2-1表示来到第一行最后一个元素的地址,解引用得到第一行最后一个元素-----6;

int main()
{
	int aa[2][5] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
	int *ptr1 = (int*)(&aa + 1);
	int *ptr2 = (int*)(*(aa + 1));
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

11.实现一个函数,可以左旋字符串中的K个字符;(意思就是例如ABCD左旋一个字符,就是把A放到最后-----BCDA;左旋两个同理)

思路:首先初始化字符串,定义左旋函数,进行传参。

函数中因为定义的是 char 型数组,所以用 char* 指针去接收;定义字符串长度strlen,第一个for循环的意思是左旋的次数;然后循环体中是首先将第一个元素拿出来,数组传参,将首元素的地址取出来,然后进行循环,将第三个元素放到第二个元素的位置上,依次类推,注意循环的次数是字符串长度-1(可以用少的字符串去列一下);然后将拿出来的第一个元素放在最后一个元素的位置上;arr表示数组首元素的位置,arr+len-1解引用表示数组最后一个元素的位置;

void left_move(char *arr, int k)
{
	int i = 0;
	int len = strlen(arr);
	for (i = 0; i < k; i++)
	{
		char tmp = *arr;
		int j = 0;
		for (j = 0; j < len - 1; j++)
		{
			*(arr + j) = *(arr + j + 1);
		}
		*(arr + len - 1) = tmp;
	}
}
int main()
{
	char arr[] = "abcdef";
	left_move(arr,3);
	printf("%s", arr);
	return 0;
}

优化算法:三步翻转法:

思想:先把前k个逆序;然后把后面的几个逆序,最后整体进行逆序;abcdef前两个逆序是:bacdef;后面四个逆序是:bafedc;整体进行逆序是:cdefab-------得到预期的结果;

注意:left++和right-- ;是为了保证逆序完一对以后,进行下一对;切记,切记!

void reverse(char* left, char* right)
{
	while (left < right)
	{
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}
}
void left_(char* arr, int k)
{
	int len = strlen(arr);
	reverse(arr, arr + k - 1);
	reverse(arr + k, arr + len - 1);
	reverse(arr, arr + len - 1);
}
int main()
{
	char arr[] = "abcdef";
	left_(arr, 4);
	printf("%s", arr);
	return 0;
}

12.写一个函数,判断一个字符串是否为另外一个字符串旋转之后的字符串:

思想:定义两个字符串进行传参;开启for循环进行判断,每次左旋1个字符,strcmp函数进行比较,一旦有左旋之后的结果相同的,strcmp输出0;return值返回1;输出YES;反之亦然;

void reverse(char *left, char* right)
{
	while (left < right)
	{
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}
}
void left_move(char *s1, int k)
{
	int len = strlen(s1);
	reverse(s1, s1 + k - 1);
	reverse(s1 + k, s1 + len - 1);
	reverse(s1, s1 + len - 1);
}
int is_left_move(char* s1, char* s2)
{
	int len = strlen(s1);
	int i = 0;
	for (i = 0; i < len; i++)
	{
		left_move(s1, 1);
		int ret=strcmp(s1, s2);
		if (ret == 0)
			return 1;
	}
	return 0;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "cdefar";
	int ret = is_left_move(arr1, arr2);
	if (ret == 1)
		printf("Yes\n");
	else
		printf("No\n");
	return 0;
}

优化算法思想:abcdefgabcdefg(2个数组)中包含了该数组左旋以后的所有可能性;

主要思想:首先初始化两个数组;但需要注意一定要给定arr1足够的存储空间,否则会导致追加字符串时无空间;定义函数is_left_move进行传参;

数组传参,用char* 指针进行接收,计算str1的字符串长度;

strcat:字符串追加函数;strcat只能进行不同字符串的追加,也就是可以把B字符串追加到A的字符串上面,但不能实现自身追加到自身的情况,也就是不能把A字符串追加到A上面;

strncat:字符串追加函数;相比于strcat函数,strncat可以把自身追加到自身上面,语法结构是strncat(A,A,len)---------把字符串A追加到A字符串后,第三个元素是追加字符串的长度;

strstr:找子串的函数;语法架构是strstr(A,B)-------判断B是否是A的子串,如果是,返回值是B的首元素地址(A----abcdef    B-----bcd),则返回值是‘b’;如果不是,返回值是NULL----空指针;(NULL的头文件是   #include<stdio.h>   )

注意:判断两个字符串的长度,如果两个字符串的长度明显不相等,那么一定不是左旋得来的,直接返回0;

int is_left_move(char *str1, char* str2)
{
	int len1 = strlen(str1);
	int len2 = stelen(str2);
	if (str1 != str2)
		return 0;
	strncat(str1, str1, len1);
	char *ret = strstr(str1, str2);
	if (ret == NULL)
	{
		return 0;
	}
	else
		return 1;
}
int main()
{
	char arr1[30] = "abcdef";
	char arr2[] = "cdefac";
	int ret = is_left_move(arr1, arr2);
	if (ret == 1)
		printf("Yes\n");
	else
		printf("No\n");
	return 0;
}



  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值