C语言拯救者 (指针初阶--7)

目录

1.1 指针是什么

2.1 指针和指针类型

2.2 指针类型的意义

 3.1 野指针

3.4 规避野指针

4.1 指针运算

5. 指针和数组

6. 二级指针

7. 指针数组

8.1练习:写一个函数,可以逆序一个字符串的内容。

8.2练习求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字,

例如:2+22+222+2222+22222

8.4 打印菱形


1.1 指针是什么

1. 指针是内存中一个最小单元的编号,也就是地址(每块空间都有地址)

2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量,地址是唯一标示一块地址空间的

3:指针在32位平台上通过cpu生成32个bit位:4个字节,在64位平台上通过cpu生成32个bit位:8个字节。

#include <stdio.h>
int main()
{
 int a = 10;//在内存中开辟一块空间
 int *p = &a;//对变量a,取出它的地址,可以使用&操作符。
 //a变量占用4个字节的空间,a的4个字节的第一个字节的地址存放在p变量中,p就是一个指针变量
 return 0;
}

2.1 指针和指针类型

变量有不同的类型,整形,浮点型等。那指针有没有类型呢?

指针变量的大小都是4/8字节,都是用来存放地址


int main()
{
	int a = 10;
	int* pa = &a;

	char ch = 'w';
	char* pc = &ch;

	printf("%d\n", sizeof(pa));
	printf("%d\n", sizeof(pc));


	return 0;
}

2.2 指针类型的意义

2.3 指针类型决定了指针在解引用时有多大权限

整形指针解引用访问4个字节,字符指针引用访问1个字节


int main()
{
	int a = 0x11223344;  //44 33 22 11
	int* pa = &a;
	*pa = 0;             //00 00 00 00  操作了4个字节
	char* pa = &a;
	*pa = 0;             //00 33 22 11  操作了1个字节


	return 0;
}
2.4 指针的类型决定了指针向前或者向后走一步有多大(距离)
int main()
{
	int a = 10;
	int*pa = &a;
	char* pc = &a;

	printf("%p\n", pa);
	printf("%p\n", pc);

	printf("%p\n", pa+1);
	printf("%p\n", pc+1);

	return 0;
}


 3.1 野指针

野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

3.2 成因:指针未初始化

int main()
{
	int* p;//指针变量没有初始化,放的是随机值,随机值中的空间不属于我们,非法操作
	
	*p = 20;

	return 0;
}

3.3 成因:指针越界访问

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int i = 0;

	int* p = arr;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", *p);//指针所指向的空间越界访问
		p++;
	}

	return 0;
}



int* test()
{
	int a = 10;
	printf("%d\n", a);
	return &a;      //函数结束空间销毁,a的地址未知,非法访问内存
}
int main()
{
	int*p = test();
	*p = 100;

	return 0;
}

3.4 规避野指针

1. 指针初始化

2. 小心指针越界

3. 指针指向空间置NULL (NULL为0地址,不使用只初始化)

4. 避免返回局部变量的地址

5. 指针使用之前检查有效性(assert断言)


4.1 指针运算

4.2 指针+-整数   指针的关系运算

int values[5];
int *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)//地址有大有小,指针也有关系运算
{
     *vp++ = 0;
}

4.3  指针-指针  (前提:两个指针必须指向同一块空间)

int main()
{
	int arr[10] = { 0 };

	printf("%d\n", &arr[0] - &arr[9]);//答案是9,指针-指针得到两个指针之间的元素的个数

	return 0;
}

指针-指针实现strlen


 int my_strlen(char* str)
{
	char* start = str;
	while (*str)
	{
		str++;
	}
	return str - start;
}

int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);

	return 0;
}

5. 指针和数组

数组名表示的是数组首元素的地址

但是有2个例外:

sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小

&数组名,数组名表示整个数组,取出的是整个数组的地址

#include <stdio.h>
int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}



int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };          
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));//指针+-整数写法
	}

	return 0;
}

int main()
{
	int arr[10] = { 0 };
	printf("%p\n", arr);
	printf("%p\n", arr+1);

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);//是一个整形指针类型,+1跳过一个整形

	printf("%p\n", &arr);
	printf("%p\n", &arr+1);//+1跳过一个数组,是一个数组指针类型


	return 0;
}


6. 二级指针

int main()
{
	int a = 10;
	int* pa = &a;//pa是指针变量(一级指针) *是指针,int是类型
	int** ppa = &pa;//ppa是一个二级指针,存放一级指针变量的地址
    //int* 是一级指针变量的类型,*代表是一个指针
	
	int*** pppa = &ppa;//pppa就是三级指针.....

    //ppa如何找到a,*ppa=pa,*pa=a  ->可以写成 **ppa找到a;


	return 0;
}


7. 指针数组

指针数组是指针还是数组?我们学过 整型数组:int arr[5],存放5个整形的数组

指针数组便是存放指针的数组,例如 int* arr[5]

int main()
{
	//int arr[5];//指针数组 - 存放整型的数组
	//char ch[6];//字符数组 -存放字符的数组

	int a = 10;
	int b = 11;
	int c = 12;
	int d = 13;
	int e = 14;
	int* arr2[5] = {&a, &b, &c, &d, &e};//指针数组

	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", *(arr2[i]));  //法很难看,在一级指针中不会这么用
	}
	return 0;
}
int main()
{
	int data1[] = { 1,2,3,4,5 };
	int data2[] = { 2,3,4,5,6 };
	int data3[] = { 3,4,5,6,7 };

	//arr就是一个指针数组
	int* arr[3] = { data1 ,data2, data3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}

	return 0;
}

8.1练习:写一个函数,可以逆序一个字符串的内容。

思路:用指针的方式找到字符串的起始地址和结束地址,用临时变量交换

#include <assert.h>
#include <stdio.h>
void reverse(char* str)
{
	assert(str);//检查要使用的指针是否为NULL
	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[100];
    scanf("%s",arr);
	reverse(arr);
	printf("%s", arr);
	return 0;
}

8.2练习求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字,

例如:2+22+222+2222+22222

思路:
该表达式的第i项中有i个a数字,第i项为ret因此:可以推导出ret*10+a

int main()

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

8.3 求出0~100000之间的所有“水仙花数”并输出。

“水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=1^3+5^3+3^3,则153是一个“水仙花数”。

思路
1. 求位数
2. 获取每个位置上的数据,并对其进行立方求和
3. 求和完成后,检测结果是否与其相等

#include <math.h>
//153
int main()
{
	int i = 0;
	for (i = 0; i <= 100000; i++)
	{
		int tmp = i;
		int n = 1;
		//第一步判断是几位数
		while (tmp / 10)
		{
			n++;
			tmp = tmp / 10;
		}
		//计算每一位次方和
		tmp = i;
		int sum = 0;
		while (tmp)
		{
			sum += pow(tmp % 10, n);
			tmp = tmp / 10;
		}
		//3.判断
		if (i == sum)
		{
			printf("%d ", i);
		}
	}
	return 0;
}

8.4 打印菱形

思路
1.为了让第一行*来到中间,其他地方要打印空格,我们先打印上面7行的*,再打印下面6行
2.第一个*左边6个空格,往下一行-1

int main()
{
    int n = 0;
    scanf("%d", &n);
    //输入打印上半部分 n,例如上半部分为7,下半部分自动为6,方便打印成菱形
    int i = 0;
    for (i = 0; i < n; i++)
    {
        //打印一行
        //打印空格
        int j = 0;
        for (j=0; j<n-1-i; j++)
        {
            printf(" ");
        }
        //打印*  *号是1,3,5,7...往下增,i为0时打印一个*,i为1时打印三个*
        for (j = 0; j < 2 * i + 1; j++)
        {
            printf("*");
        }
        printf("\n");
    }

    //打印下半部分 n-1
    for (i = 0; i < n-1; i++)
    {
        //打印一行
        //打印空格
        int j = 0;
        for (j = 0; j <= i; j++)
        {
            printf(" ");
        }
        //打印*
        for (j = 0; j < (n-1-i)*2-1; j++)
        {
            printf("*");
        }
        printf("\n");
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_63543274/article/details/123600874