当前位置: 首页 > news >正文

深入理解指针(5)

目录

1. sizeof和strlen 的对比
2. 数组和指针笔试题解析
3 . 指针运算笔试题解析
———————————————————————————————————————————

1.sizeof和strlen的对比

1.1sizeof

sizeof计算变量所占内存内存空间大小的,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

sizeof只关注占用内存空间的大小,不在乎内存中存放什么数据。

1.2strlen

strlen是C语言库函数,功能是求字符串长度。

函数原型:

1.size_t strlen ( const char * str )

统计的是从strlen函数的参数str中这个地址开始向后,\0之前的字符串中字符的个数。

strlen函数会一直向后找\0字符,直到找到为止,所以可能存在越界查找。

1.3sizeof和strlen的对比

sizeof:

  1. sizeof是操作符
  2. sizeof计算操作数所占内存的大小,单位是字节
  3. 不关注内存中存放什么数据

strlen:

  1. strlen是库函数,使用需要包含头文件string.h
  2. strlen是求字符串长度的,统计的是\0之前的字符的个数
  3. 关注内存中是否有\0,如果没有\0,就会持续往后找,可能会越界

2.数组和指针笔试题解析

2.1一维数组

#include <stdio.h>
int main()
{int a[] = { 1,2,3,4 };printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(a + 0));printf("%zd\n", sizeof(*a));printf("%zd\n", sizeof(a + 1));printf("%zd\n", sizeof(a[1]));printf("%zd\n", sizeof(&a));printf("%zd\n", sizeof(*&a));printf("%zd\n", sizeof(&a + 1));printf("%zd\n", sizeof(&a[0]));printf("%zd\n", sizeof(&a[0] + 1));return 0;
}

 

数组名是数组首元素的地址,但是有两个例外:

  1. sizeof(数组名)
  2. &数组名--这里的数组名表示整个数组,取出的是数组的地址

int a[] = { 1,2,3,4 };
1.printf("%zd\n", sizeof(a));//16--数组名a单独放到sizeof内部,a表示整个数组,计算的是整个数组的大小,单位是字节
2.printf("%zd\n", sizeof(a + 0));//4/8--这里的a表示首元素的地址,a+0还是首元素的地址
3.printf("%zd\n", sizeof(*a));//4--这里的a表示首元素的地址,*a就是首元素--a[0]
4.printf("%zd\n", sizeof(a + 1));//4/8--这里的a表示首元素的地址,a+1是第二个元素的地址
5.printf("%zd\n", sizeof(a[1]));//4--第二个元素,第二个元素大小是4个字节
6.printf("%zd\n", sizeof(&a));//4/8--这里的数组名表示整个数组,&a是整个数组的地址,数组的地址也是地址
7.printf("%zd\n", sizeof(*&a));//16--*和&相互抵消 => sizeof(a),a表示整个数组,计算的是整个数组的大小,单位是字节
8.printf("%zd\n", sizeof(&a + 1));//4/8--&a是数组的地址。&a+1是跳过整个数组后那个位置的地址,但它也是地址
9.printf("%zd\n", sizeof(&a[0]));//4/8--a[0]是首元素,对首元素取地址也是地址
10.printf("%zd\n", sizeof(&a[0] + 1));//4/8--第二个元素的地址

2.2字符数组

代码1:

#include <stdio.h>
int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr + 0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr + 1));printf("%d\n", sizeof(&arr[0] + 1));return 0;
}

char arr[] = { 'a','b','c','d','e','f' };
1.printf("%d\n", sizeof(arr));//6--这里的数组名表示整个数组,单位字节,没有\0
2.printf("%d\n", sizeof(arr + 0));//4/8--这里的arr表示首元素的地址,arr+0还是首元素的地址
3.printf("%d\n", sizeof(*arr));//1--这里的arr表示首元素的地址,*arr就是首元素,首元素的大小是1个字节
4.printf("%d\n", sizeof(arr[1]));//1--arr[1]是数组的第二个元素,第二个元素的大小是1个字节
5.printf("%d\n", sizeof(&arr));//4/8--这里的arr表示整个数组,取出整个数组的地址
6.printf("%d\n", sizeof(&arr + 1));//4/8--这里的arr表示整个数组,取出数组的地址+1,跳过整个数组,但也是地址
7.printf("%d\n", sizeof(&arr[0] + 1));//4/8--arr[0]是首元素,&arr[0]是首元素地址,+1表示第二个元素的地址

代码2:

#include <stdio.h>
#include <string.h>
int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%zd\n", strlen(arr));printf("%zd\n", strlen(arr + 0));printf("%zd\n", strlen(*arr));printf("%zd\n", strlen(arr[1]));printf("%zd\n", strlen(&arr));printf("%zd\n", strlen(&arr + 1));printf("%zd\n", strlen(&arr[0] + 1));return 0;
}

strlen是计算字符串中‘\0’之前的字符的个数的 

char arr[] = { 'a','b','c','d','e','f' };
1.printf("%zd\n", strlen(arr));//随机值--这里的arr是首元素的地址,arr中没有\0,所以strlen会一直往后找,直到找到\0
2.printf("%zd\n", strlen(arr + 0));//随机值--这里的arr是首元素的地址,arr+0还是首元素的地址,没有\0,所以会一直往后找
3.printf("%zd\n", strlen(*arr));//程序崩溃--这里的arr是首元素的地址,*arr是首元素,‘a’的ACCLL码值是97,strlen是地址,我们把97传进去,strlen就会把97当成地址进行访问,程序直接崩溃
4.printf("%zd\n", strlen(arr[1]));//程序崩溃--arr[1]是第二个元素,‘b’的ACCLL码值是98,把98传给strlen,strlen会把98当做地址进行访问,程序崩溃
5.printf("%zd\n", strlen(&arr));//随机值--这里的arr是整个数组,取整个数组的地址,strlen还是会往后找\0
6.printf("%zd\n", strlen(&arr + 1));//随机值--这里的arr表示整个数组,arr+1跳过整个数组,后面不知道有没有\0,随机值,和前面从首元素开始找随机值差6,所以跳过了整个数组
7.printf("%zd\n", strlen(&arr[0] + 1));//随机值--取出首元素地址+1,第二个元素地址开始往后找\0,比从首元素地址开始的少1

代码3: 

#include <stdio.h>
int main()
{char arr[] = "abcdef";printf("%zd\n", sizeof(arr));printf("%zd\n", sizeof(arr + 0));printf("%zd\n", sizeof(*arr));printf("%zd\n", sizeof(arr[1]));printf("%zd\n", sizeof(&arr));printf("%zd\n", sizeof(&arr + 1));printf("%zd\n", sizeof(&arr[0] + 1));return 0;
}

char arr[] = "abcdef";
1.printf("%zd\n", sizeof(arr));//7--arr整个数组的大小
2.printf("%zd\n", sizeof(arr + 0));//4/8--arr数组首元素的地址,arr+0还是首元素的地址
3.printf("%zd\n", sizeof(*arr));//1--arr是数组首元素的地址,*arr首元素
4.printf("%zd\n", sizeof(arr[1]));//1--数组第二个元素
5.printf("%zd\n", sizeof(&arr));//4/8--arr表示整个数组,&arr整个数组的地址
6.printf("%zd\n", sizeof(&arr + 1));//4/8--arr表示整个数组,&arr整个数组的地址,&arr+1跳过整个数组的地址,还是地址
7.printf("%zd\n", sizeof(&arr[0] + 1)); //4/8--&arr[0]是首元素的地址,&arr[0]+1第二个元素的地址

代码4:

#include <stdio.h>
#include <string.h>
int main()
{char arr[] = "abcdef";printf("%d\n", strlen(arr));printf("%d\n", strlen(arr + 0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr + 1));printf("%d\n", strlen(&arr[0] + 1));return 0;
}

char arr[] = "abcdef";
1.printf("%d\n", strlen(arr));//6--arr首元素的地址,有\0
2.printf("%d\n", strlen(arr + 0));//6--arr表示首元素的地址,arr+0还是首元素
3.printf("%d\n", strlen(*arr));//程序崩溃--arr表示首元素的地址,*arr首元素,‘a’的ACCLL码值是97,将97传给strlen,strlen会将97当成地址访问,程序崩溃
4.printf("%d\n", strlen(arr[1]));//程序崩溃--arr[1]表示第二个元素,‘b’的ACCLL码值是98,将98传给strlen,strlen会将98当成地址访问,程序崩溃
5.printf("%d\n", strlen(&arr));//6--arr整个数组,&arr整个数组的地址传给strlen
6.printf("%d\n", strlen(&arr + 1));//随机值--&arr+1跳过整个数组,会跳过\0
7.printf("%d\n", strlen(&arr[0] + 1)); //5--取第一个元素+1

代码5:

#include <stdio.h>
int main()
{char* p = "abcdef";printf("%zd\n", sizeof(p));printf("%zd\n", sizeof(p + 1));printf("%zd\n", sizeof(*p));printf("%zd\n", sizeof(p[0]));printf("%zd\n", sizeof(&p));printf("%zd\n", sizeof(&p + 1));printf("%zd\n", sizeof(&p[0] + 1));return 0;
}

 

char* p = "abcdef";
1.printf("%zd\n", sizeof(p));//4/8--p是指针变量,计算的是指针变量的大小,指针变量就是存放地址的
2.printf("%zd\n", sizeof(p + 1));//4/8--p+1是第二个元素的地址
3.printf("%zd\n", sizeof(*p));//1--p的类型是char*,*p只能访问一个字节
4.printf("%zd\n", sizeof(p[0]));//1--p[0]->*(p+0)->*p,和上面一样
5.printf("%zd\n", sizeof(&p));//4/8--&p是指针变量的地址,&p->char**->二级指针
6.printf("%zd\n", sizeof(&p + 1));//4/8--&p是p的地址,&p+1是跳过p变量,但还是地址
7.printf("%zd\n", sizeof(&p[0] + 1));//4/8--p[0]是a,取出a的地址+1,指向b的地址

代码6:

#include <stdio.h>
#include <string.h>
int main()
{char* p = "abcdef";printf("%d\n", strlen(p));printf("%d\n", strlen(p + 1));printf("%d\n", strlen(*p));printf("%d\n", strlen(p[0]));printf("%d\n", strlen(&p));printf("%d\n", strlen(&p + 1));printf("%d\n", strlen(&p[0] + 1));return 0;
}

 

char* p = "abcdef";
1.printf("%d\n", strlen(p));//6--p里面存放的是a的地址,将a的地址传给strlen
2.printf("%d\n", strlen(p + 1));//5--p+1里面存放的是b的地址
3.printf("%d\n", strlen(*p));//程序崩溃--p里面存放的是a的地址,*p是‘a’,‘a’的ACCLL码值是97,将97传给strlen,strlen会将97当成地址访问,程序崩溃
4.printf("%d\n", strlen(p[0]));//程序崩溃--p[0]->*(p+0)->*p
5.printf("%d\n", strlen(&p));//随机值--&p取出变量p的地址,类型是char**
6.printf("%d\n", strlen(&p + 1));//随机值--&p取出变量p的地址,&p+1跳过一个指针变量
7.printf("%d\n", strlen(&p[0] + 1));//5--&p[0]->p,p+1指向b的地址

2.3二维数组

#include <stdio.h>
int main()
{int a[3][4] = { 0 };printf("%zd\n", sizeof(a));printf("%zd\n", sizeof(a[0][0]));printf("%zd\n", sizeof(a[0]));printf("%zd\n", sizeof(a[0] + 1));printf("%zd\n", sizeof(*(a[0] + 1)));printf("%zd\n", sizeof(a + 1));printf("%zd\n", sizeof(*(a + 1)));printf("%zd\n", sizeof(&a[0] + 1));printf("%zd\n", sizeof(*(&a[0] + 1)));printf("%zd\n", sizeof(*a));printf("%zd\n", sizeof(a[3]));return 0;
}

int a[3][4] = { 0 };
1.printf("%zd\n", sizeof(a));//48--a表示整个数组,计算的是整个数组的大小,单位是字节
2.printf("%zd\n", sizeof(a[0][0]));//4--a[0][0]是第一行第一个元素
3.printf("%zd\n", sizeof(a[0]));//16--a[0]表示一整个一维数组的大小
4.printf("%zd\n", sizeof(a[0] + 1));//4/8--a[0]是第一行的数组名,但是没有单独放在sizeof内部,那么只能是数组首元素的地址,那就是第一行第一个元素的地址,+1则是第一行第二个数的地址
5.printf("%zd\n", sizeof(*(a[0] + 1)));//4--是第一行第二个元素
6.printf("%zd\n", sizeof(a + 1));//4/8--a是二维数组的数组名,a表示首元素的地址,也就是第一行的地址,a+1则是第二行的地址
7.printf("%zd\n", sizeof(*(a + 1)));//16--第二行整个数组的大小
8.printf("%zd\n", sizeof(&a[0] + 1));//4/8--a[0]是第一行的数组名,&a[0]是第一行的地址,&a[0]+1是地址
9.printf("%zd\n", sizeof(*(&a[0] + 1)));//16--&a[0]+1第二行的地址,*(&a[0]+1)是第二行整个数组的大小
10.printf("%zd\n", sizeof(*a));//16--a是第一行的地址,*a第一行的值
11.printf("%zd\n", sizeof(a[3])); //没有越界,a[3]是第四行的数组名,int[4]sizeof不会去真实计算,根据类型推算

3.指针运算笔试题解析

题目1:

#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:

#include <stdio.h>
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥?
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{printf("%p\n", p + 0x1);printf("%p\n", (unsigned long)p + 0x1);printf("%p\n", (unsigned int*)p + 0x1);return 0;
}

 

1.printf("%p\n", p + 0x1);//p是结构体指针,+1就是跳过结构体,跳过20个字节,转十六进制0x14
2.printf("%p\n", (unsigned long)p + 0x1);//整数+1,0x100001
3.printf("%p\n", (unsigned int*)p + 0x1); //=1是跳过一个unsigned int*类型变量,是4个字节

题目3:

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

 

题目4:

//假设环境是x86环境,程序输出的结果是啥?
#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][2]-&a[4][2]=-4 

 1000000 0000000 0000000 0000100 --原码

 11111111 11111111 11111111 11111011 --反码

 11111111 11111111 11111111 11111100 --补码

       FF           FF         FF             FC

题目5:

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

 

题目6:

#include <stdio.h>
int main()
{char* a[] = { "work","at","alibaba" };char** pa = a;pa++;printf("%s\n", *pa);return 0;
}

 

题目7:

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

 

 


http://www.mrgr.cn/news/33013.html

相关文章:

  • Vue3 -- 环境变量的配置【项目集成3】
  • java中await方法和wait方法区别
  • 小程序服务商常见问题
  • MDBook 使用指南
  • Redis8:商户查询缓存2
  • test 是 JavaScript 中正则表达式对象 (RegExp) 的一种方法,用于测试一个字符串是否匹配某个正则表达式
  • 使用Django 搭建自动化平台
  • 求1000以内所有恰好能分解成10组两个素数之和
  • Python爬虫-Post请求中,参数只有value没有key,如何正确处理?
  • 初始网络编程(下)
  • 常见的中间件漏洞
  • MySQL高阶1907-按分类统计薪水
  • 华为摄像机/NVR主动注册协议接入SVMSP平台
  • 基于SpringBoot+Vue+MySQL的手机销售管理系统
  • 秩一的等价转化
  • 有关elementui form验证问题,有值却仍然显示不通过
  • HtmlCss 基础总结(基础好了才是最能打的)三
  • ★ C++进阶篇 ★ 二叉搜索树
  • 机器之心 | 阿里云Qwen2.5发布!再登开源大模型王座,Qwen-Max性能逼近GPT-4o
  • OpenGL 原生库6 坐标系统
  • 【漏洞复现】泛微OA E-Office jx2_config.ini 敏感信息泄漏漏洞
  • Wireshark学习使用记录
  • IPsec-VPN中文解释
  • Vue极简入门
  • 不要死磕技术,还是要产品化
  • 图像生成大模型 Imagen:重塑创意的未来