深度理解指针(5)----指针完结

hello,各位小伙伴们我们现在已经对指针有了深刻的理解,指针来到了收尾环节!让我们来做几题例题来复习之前学习的内容吧!

最近爆火的黑神话悟空不知道小伙伴们体验了没有,小编对八戒还有蜘蛛精的凄惨爱情深深打动特意找了一张他们的动漫合影!

一、sizeof和strlen的对比

1、sizeof

sizeof 计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。sizeof 只关注占用内存空间的大小,不在乎内存中存放什么数据。

例如:

cpp 复制代码
#include<stdio.h>
int main ()
{
    int a = 10;
    printf("%d\n", sizeof(a));
    printf("%d\n",sizeof(int));
    return 0;
}

sizeof是一个操作数!

2、strlen

strlen是C语言库函数,是用来求字符串的长度的·。格式:

cpp 复制代码
size_t strlen (const char*str);

strlen统计的是字符串中\0之前的个数。

cpp 复制代码
#include<stdio.h>
int main ()
{
    char arr1[3] = {'a','b','c'};
    char arr2[]  = "abcdef";
    printf("%d\n",strlen(arr1));
    printf("%d\n",strlen(arr2));
    return 0;
}

在字符数组中不包含\0而在字符串数组中末尾包含\0,同时要注意strlen只能用来统计字符数组的元素个数,不能统计整型数组元素个数!

二、数组和指针笔试题解析​

1、一维数组​

cpp 复制代码
#include<stdio.h>
int main ()
{
    int a[] = {1,2,3,4};
    printf("%d\n",sizeof(a));// 16  此时传递的是整个数组,所以大小为4*sizeof(int)
    printf("%d\n",sizeof(a+0));// 4或8   a没有单独的放在sizeof中,所以a是数组首元素地址!,地址的大小为4或8
    printf("%d\n",sizeof(*a));// 4 同样a没有单独的放在sizeof中,a是数组首元素地址,解引用后为数组首元素!
    printf("%d\n",sizeof(a+1));// 4或8 a是数组首元素地址,+1后为数组第2个元素的地址
    printf("%d\n",sizeof(a[1]));// 4  a[1]是数组的第二个元素,为int类型
    printf("%d\n",sizeof(&a));// 4或8 此时&a是数组的整个地址,仍然是指针变量!
    printf("%d\n",sizeof(*&a));// 16  取出整个数组的地址后在进行解引用操作,为整个数组的元素
    printf("%d\n",sizeof(&a+1))// 4 或者8  取出整个数组的地址后,+1会跳过整个数组,虽然越界,但sizeof不会真实访问
    printf("%d\n",sizeof(&a[0]));// 4 或者8 表示的是数组首元素的地址
    printf("%d\n",sizeof(&a[0]+1));// 4 或者8  +1后表示数组第二个元素的地址
    return 0; 
}

2、字符数组​

cpp 复制代码
#include<stdio.h>
int main ()
{
    char arr[] = {'a','b','c','d','e','f'};
    printf("%d\n", sizeof(arr));//6 一共6个字符,每个字符的大小为一个字节
    printf("%d\n", sizeof(arr+0));// 4或者8 传递的是数组首元素地址
    printf("%d\n", sizeof(*arr));// 1 arr没有单独的放入sizeof中,即表示数组首元素地址,解引用后为数组首元素!类型为char
    printf("%d\n", sizeof(arr[1]));// 1 表示数组中第二个元素,类型为char
    printf("%d\n", sizeof(&arr));// 4或者8  &arr传递的是整个数组的地址
    printf("%d\n", sizeof(&arr+1));// 4或者8 &arr传递整个数组后进行+1,会跳过整个数组,会越界但不会进行访问
    printf("%d\n", sizeof(&arr[0]+1));//4或者8  表示的是第二个元素的地址
    return 0;
}
cpp 复制代码
#include<stdio.h>
int main ()
{
    char arr[] = {'a','b','c','d','e','f'};
    printf("%d\n", strlen(arr));// 随机值 strlen统计的是字符中\0之前字符的个数,但在字符数组中不存在\0,随机值
    printf("%d\n", strlen(arr+0));// 随机值 arr表示的是数组首元素的地址,从该地址向后统计\0之前元素的个数
    printf("%d\n", strlen(*arr));// 报错!arr仍然表示的是数组首元素地址,解引用以后为数组首元素,strlen的传参为指针类型,与规定传递类型不同!
    printf("%d\n", strlen(arr[1]));//  报错!
    printf("%d\n", strlen(&arr));// 随机值  &arr表示的是整个数组的地址,但仍然是从数组的首元素开始!,向后统计\0之前元素的个数!
    printf("%d\n", strlen(&arr+1));// 随机值  &arr表示的是整个数组的地址+1后会跳过整个数组!,之后在向后统计\0之前的元素个数!,但后面的内存中\0的位置未知,会是随机值
    printf("%d\n", strlen(&arr[0]+1));// 随机值 数组首元素地址+1后变为第二个元素的地址,从此次位置开始进行统计\0之前的元素个数
    return 0;
}
cpp 复制代码
#include<stdio.h>
int main ()
{
    char arr[] = "abcdef";
    printf("%d\n", sizeof(arr));// 7 arr传递的是整个数组,统计整个数组中元素个数,包含\0(易错)
    printf("%d\n", sizeof(arr+0));// 4或者8   数组首元素地址
    printf("%d\n", sizeof(*arr));// 1  arr为数组首元素地址,解引用后为数组首元素类型为char
    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
    return 0;
}
cpp 复制代码
#include<stdio.h>
int main ()
{
   char arr[] = "abcdef";
    printf("%d\n", strlen(arr));// 6 arr表示数组首元素的地址,从当前的位置开始统计\0之前的字符个数 
    printf("%d\n", strlen(arr+0));// 6 同理
    printf("%d\n", strlen(*arr));// 报错 arr表示数组首元素的地址,解引用后为数组首元素,传递给strlen的参数错误会报错
    printf("%d\n", strlen(arr[1]));//  报错
    printf("%d\n", strlen(&arr));// 6 &arr虽然表示的数组的地址,但仍然是从数组的起始位置开始统计
    printf("%d\n", strlen(&arr+1));// 随机值 &arr+1后会从数组的首元素地址跳过整个数组,但后面的\0的位置未知
    printf("%d\n", strlen(&arr[0]+1));// 5  数组首元素地址+1为数组第二个元素的地址
    return 0;

}
cpp 复制代码
#include<stdio.h>
int main ()
{
    char *p = "abcdef";//p是指针变量, 是数组首元素地址
    printf("%d\n", sizeof(p));// 4或8  p是指针变量
    printf("%d\n", sizeof(p+1));//  4或8  p是数组首元素地址,+1后变为数组第二个元素的地址
    printf("%d\n", sizeof(*p));//*p表示的是数组首元素,类型为char
    printf("%d\n", sizeof(p[0]));// 可以翻译为*(p+0),为数组首元素,类型为char
    printf("%d\n", sizeof(&p));//  4或8  p为数组首元素地址,将此地址取出放入到二级指针变量中,仍为指针
    printf("%d\n", sizeof(&p+1));// 4或8 二级指针变量+1后会跳过一个char*类型变量的大小
    printf("%d\n", sizeof(&p[0]+1));// 4或8 p[0]为a,&p[0]后为a的地址+1后b为的地址
    return 0;
}
cpp 复制代码
#include<stdio.h>
int main ()
{
    char *p = "abcdef";
    printf("%d\n", strlen(p));//6
    printf("%d\n", strlen(p+1));//5
    printf("%d\n", strlen(*p));//err
    printf("%d\n", strlen(p[0]));//err
    printf("%d\n", strlen(&p));//随机值
    printf("%d\n", strlen(&p+1));//随机值
    printf("%d\n", strlen(&p[0]+1));//5

    return 0;
}

小伙伴自己尝试进行分析。

3、二维数组​

cpp 复制代码
#include<stdio.h>
int main ()
{
    int a[3][4] = {0};
    printf("%d\n",sizeof(a));//  48 代表着整个数组,3*4*sizeof(int)
    printf("%d\n",sizeof(a[0][0]));//  4  代表数组的第一个元素
    printf("%d\n",sizeof(a[0]));// 16 16  a[0]是第一行的数组名,现在单独放在sizeof内部,计算的是一行的大小
    printf("%d\n",sizeof(a[0]+1));// 4或者8   a[0]是第一行的数组名,没有单独放在sizeof中即为数组第一行首元素地址,+1后为数组第二行首元素地址
    printf("%d\n",sizeof(*(a[0]+1)));//  4  数组第二行首元素地址解引用后为数组第二行首元素类型为int
    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)));//
    printf("%d\n",sizeof(*a));
    printf("%d\n",sizeof(a[3]));
    return 0;
}

4、指针运算

cpp 复制代码
#include <stdio.h>
int main()
{
 int a[5] = { 1, 2, 3, 4, 5 };
 int *ptr = (int *)(&a + 1);
 printf( "%d,%d", *(a + 1), *(ptr - 1));
 return 0;
}

在做指针类型的题目的时候要注意画图!

画完图后答案一目了然:2 5

cpp 复制代码
//在X86环境下​
//假设结构体的大小是20个字节​
//程序输出的结果是啥?​
#include<stdio.h>
struct Test
{

 int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p = (struct Test*)0x100000;//将16进制数强制类型转化成struct Test*指针类型,并赋值给p
int main()
{
 printf("%p\n", p + 0x1);//加1后要加上20个字节!要将十进制位转化成16进制位,即为0x100014
 printf("%p\n", (unsigned long)p + 0x1);//此时将p强制类型转化成(unsigned long)类型,为十进制数+1后为0x100001
 printf("%p\n", (unsigned int*)p + 0x1);//p转化为(unsigned int *)类型,+1后会加上4个字节即为0x100004
 return 0;
}
cpp 复制代码
#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;
}

让我们一起来动手画一画!

小伙伴们要注意啦!花括号里面是逗号表达式,只会保留最后一位数字。这里非常容易忽出错!

cpp 复制代码
#include <stdio.h>
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;
}

画图:

画完图后逻辑关系一目了然!要注意的是指针减去指针为中间元素的个数,同时指针是从底地址到高地址。第一个占位符是%p要将-4转化成补码后在转化成16进制的数!

运行结果为FFFFFFFFFFFFFFFC,-4

如果你已经看到这里,证明你非常优秀呢!加油。

cpp 复制代码
#include <stdio.h>
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;
}
cpp 复制代码
#include <stdio.h>
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
}

值得注意的是在指针数组中,存储的只是首字母!
运行结果为at。
终于我们迎来了最后一题,各位小伙伴们加油!

cpp 复制代码
#include <stdio.h>
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;
}


这道笔试题还是非常有难度的,这里小编只给大家画了草图,具体分析细节供给大家取分析!
今天所有关于指针的内容就结束啦,但小伙伴们要好好复习呀!拜拜,我们下期再见。

相关推荐
草莓base9 分钟前
【手写一个spring】spring源码的简单实现--bean对象的创建
java·spring·rpc
drebander33 分钟前
使用 Java Stream 优雅实现List 转化为Map<key,Map<key,value>>
java·python·list
乌啼霜满天24937 分钟前
Spring 与 Spring MVC 与 Spring Boot三者之间的区别与联系
java·spring boot·spring·mvc
tangliang_cn42 分钟前
java入门 自定义springboot starter
java·开发语言·spring boot
莫叫石榴姐42 分钟前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
程序猿阿伟43 分钟前
《智能指针频繁创建销毁:程序性能的“隐形杀手”》
java·开发语言·前端
Grey_fantasy1 小时前
高级编程之结构化代码
java·spring boot·spring cloud
弗锐土豆1 小时前
工业生产安全-安全帽第二篇-用java语言看看opencv实现的目标检测使用过程
java·opencv·安全·检测·面部
Elaine2023911 小时前
零碎04 MybatisPlus自定义模版生成代码
java·spring·mybatis
小小大侠客1 小时前
IText创建加盖公章的pdf文件并生成压缩文件
java·pdf·itext