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

C语言之指针详解(5)(含有易错笔试题)

文章目录

  • 一、sizeof和strlen的对比
    • 1.1 sizeof
    • 1.2 strlen
    • 1.3 sizeof 和 strlen 的对比
  • 二、数组和指针笔试题
    • 2.1 一维数组
    • 2.2 字符数组
    • 2.3 二维数组
  • 三、指针运算笔试题
    • 3.1 题目1
    • 3.2 题目2
    • 3.3 题目3
    • 3.4 题目4
    • 3.5 题目5
    • 3.6 题目6
    • 3.7 题目7

一、sizeof和strlen的对比

有一个很神奇的现象,就是在C语言中sizeofstrlen是两个毫不相干的东西一个是操作符,一个是库函数。但是对于初学者来说就是很容易把两者弄混淆。下面我们就来进一步加深对这两个的学习。

1.1 sizeof

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

比如:

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

代码运行结果如下:
在这里插入图片描述

1.2 strlen

strlen是C语言库函数,功能是求字符串长度,函数原型如下:

size_t strlen(const char* str);

统计的是strlen函数的参数str中这个地址开始向后,\0之前字符串中字符的个数。strlen函数会一直向后找\0字符,直到找到为止,所以可能存在越界查找

示例代码如下:

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

运行结果:
在这里插入图片描述

1.3 sizeof 和 strlen 的对比

sizeofstrlen
1. sizeof是操作符1.strlen是库函数,使用需要包含头文件string.h
2.sizeof计算操作数所占内存的大小,单位是字节2.strlen是求字符串长度的,统计的是\0之前字符的个数
3.不关注内存中存放什么数据3.关注内存中是否有\0,如果没有\0,就会持续往后找,可能会越界

对于下面的笔试题,有少部分我做了讲解。如果有其它疑问的地方可以在底下评论区问我,我会尽可能帮助大家。

二、数组和指针笔试题

在下面的题目中sizeof的情况比较简单,对于strlen有些地方稍微有点难度,我会为大家做出相应的解释。

2.1 一维数组

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

结果如下:
在这里插入图片描述

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

结果如下:
在这里插入图片描述

代码2:

#include <stdio.h>
#include <string.h>
int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("%d\n", strlen(arr));//没有\0因此会越界,导致随机值printf("%d\n", strlen(arr + 0));//没有\0因此会越界,导致随机值//printf("%d\n", strlen(*arr));arr代表首元素地址,*arr代表数组第一个元素'a','a'的ASCLL码值是97,//因此这里strlen函数会从地址为97的地方开始找\0来计算字符串长度,但地址为97的空间访问是非法的,是禁止的。//printf("%d\n", strlen(arr[1]));arr[1]代表'b'这个元素,道理同上面一个*arr的情况只不过这里是非法访问地址为98的。printf("%d\n", strlen(&arr));//地址和arr一样,只不过是数组的地址,但没有\0因此会越界,导致随机值printf("%d\n", strlen(&arr + 1));//由于+1跳过一整个数组,因此比上一个&arr的输出结果少了一个数组的长度6,但是还是没有\0因此会越界,导致随机值printf("%d\n", strlen(&arr[0] + 1));//是第二个元素'b'的地址,因此比第一个arr的输出结果少了1但是还是没有\0因此会越界,导致随机值。return 0;
}

输出结果如下:
在这里插入图片描述

代码3:

#include <stdio.h>
int main()
{char arr[] = "abcdef";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;
}

运行结果如下:
在这里插入图片描述

代码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));arr代表首元素地址,*arr代表数组第一个元素'a','a'的ASCLL码值是97,//因此这里strlen函数会从地址为97的地方开始找\0来计算字符串长度,但地址为97的空间访问是非法的,是禁止的。//printf("%d\n", strlen(arr[1]));arr[1]代表'b'这个元素,道理同上面一个*arr的情况只不过这里是非法访问地址为98的。printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr + 1));//&arr是整个数组的地址,+1跳过整个数组,因此只能越界访问,结果就是随机值了printf("%d\n", strlen(&arr[0] + 1)); //是第二个元素'b'的地址,因此比第一个arr的输出结果少了1return 0;
}

运行结果如下:
在这里插入图片描述

代码5:

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

结果如下:
在这里插入图片描述

代码6:

经过上面的联系,我想大家都掌握的差不多了吧,最后一个留给大家自行思考,看看是否能解决。

#include <stdio.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;
}

2.3 二维数组

对于二维数组的情况需要注意的一个点就是,二维数组的数组名代表首元素地址时,这个首元素是指第一行元素的地址。

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

代码运行结果如下:
在这里插入图片描述

数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这⾥的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。

三、指针运算笔试题

3.1 题目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;
}
//程序的结果是什么?

3.2 题目2

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

3.3 题目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;
}

3.4 题目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;
}

3.5 题目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;
}

3.6 题目6

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

3.7 题目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;
}

相关文章:

  • 达梦授权某个模式给其它用户只读权限
  • ubuntu 22.04 appearance设置没有dock选项
  • 德勤:中国、印度等对ChatGPT等生成式AI应用,处领先地位
  • 27.Java中单例模式的实现方式
  • 大模型面试--大模型(LLMs)基础面
  • 史上最全的Linux常用命令、使用技巧汇总(超全面!简单明了!)
  • 头歌springboot初体验
  • RPA机器人流程自动化如何优化人力资源工作流程
  • 嵩山为什么称为五岳之尊
  • 【STM32踩坑】HAL固件库版本过高导致烧录后无法运行问题
  • mongodb5.0.5版本集群搭建
  • 27、python-第三章-6-判断语句的嵌套和猜数游戏
  • Dream
  • 单点登录(JWT实现)
  • C# 实现微信公众号生成场景二维码
  • 【RocksDB】TransactionDB源码分析
  • 07.Android之多媒体问题
  • mongodb--安装和初步使用教程
  • orm2 中文文档 3.1 模型属性
  • Perseus-BERT——业内性能极致优化的BERT训练方案
  • Python实现BT种子转化为磁力链接【实战】
  • SegmentFault 技术周刊 Vol.27 - Git 学习宝典:程序员走江湖必备
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 开源地图数据可视化库——mapnik
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 详解移动APP与web APP的区别
  • Java总结 - String - 这篇请使劲喷我
  • 关于Kubernetes Dashboard漏洞CVE-2018-18264的修复公告
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • $ git push -u origin master 推送到远程库出错
  • (06)金属布线——为半导体注入生命的连接
  • (09)Hive——CTE 公共表达式
  • (poj1.3.2)1791(构造法模拟)
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (附源码)SSM环卫人员管理平台 计算机毕设36412
  • (三) diretfbrc详解
  • (四)搭建容器云管理平台笔记—安装ETCD(不使用证书)
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .NET 8.0 中有哪些新的变化?
  • .NET delegate 委托 、 Event 事件
  • .net mvc 获取url中controller和action
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .NET/C# 获取一个正在运行的进程的命令行参数
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • .sys文件乱码_python vscode输出乱码
  • /ThinkPHP/Library/Think/Storage/Driver/File.class.php  LINE: 48
  • @Autowired自动装配
  • [BUUCTF 2018]Online Tool
  • [BZOJ 4129]Haruna’s Breakfast(树上带修改莫队)
  • [C++] cout、wcout无法正常输出中文字符问题的深入调查(1):各种编译器测试
  • [exgcd] Jzoj P1158 荒岛野人