目录
8.2练习求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字,
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;
}