[C]基础13.深入理解指针(5)

  • 博客主页:向不悔
  • 本篇专栏:[C]
  • 您的支持,是我的创作动力。

文章目录

  • 0、总结
  • 1、sizeof和strlen的对比
    • 1.1 sizeof
    • 1.2 strlen
    • 1.3 sizeof和strlen的对比
  • 2、数组和指针笔试题解析
    • 2.1 一维数组
    • 2.2 字符数组
      • 2.2.1 代码1
      • 2.2.2 代码2
      • 2.2.3 代码3
      • 2.2.4 代码4
      • 2.2.5 代码5
      • 2.2.6 代码6
    • 2.3 二维数组
  • 3、指针运算笔试题解析
    • 3.1 题目1
    • 3.2 题目2
    • 3.3 题目3
    • 3.4 题目4
    • 3.5 题目5
    • 3.6 题目6
    • 3.7 题目7


0、总结

在这里插入图片描述

1、sizeof和strlen的对比

1.1 sizeof

sizeof计算变量所占内存内存空间大小的,单位是字节。如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。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));  // unknownprintf("%d\n", strlen(arr2));  // 3printf("%d\n", sizeof(arr1));  // 3printf("%d\n", sizeof(arr2));  // 4return 0;
}
运行:
15
3
3
4

1.3 sizeof和strlen的对比

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

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

2.1 一维数组

做题之前,先理解概念:

  • 1、 数组名的理解:数组名是数组首元素(第一个元素的地址)
  • 2、 但有两个例外:
    • sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节。
    • &数组名 - 数组名表示整个数组,取出的是整个数组的地址。
  • 3、 除此之外,所有的数组名是数组首元素的地址。
#include <stdio.h>int main()
{int a[] = { 1,2,3,4 };printf("01:%zd\n", sizeof(a));          printf("02:%zd\n", sizeof(a + 0));      printf("03:%zd\n", sizeof(*a));         printf("04:%zd\n", sizeof(a + 1));     printf("05:%zd\n", sizeof(a[1]));       printf("06:%zd\n", sizeof(&a));         printf("07:%zd\n", sizeof(*&a));        printf("08:%zd\n", sizeof(&a + 1));               printf("09:%zd\n", sizeof(&a[0]));      printf("10:%zd\n", sizeof(&a[0] + 1));  return 0;
}
运行(32位环境):
01:16
02:4
03:4
04:4
05:4
06:4
07:16
08:4
09:4
10:4
运行(64位环境):
01:16
02:8
03:4
04:8
05:4
06:8
07:16
08:8
09:8
10:8

解析:

解析:
//01 : 16     because:sizeof(数组名)->求数组的大小。
//02 : 4/8    because:不是单独数组名,所以a是首元素的地址,类型为int*,a+0还是首元素地址。
//03 : 4      because:*a == *(a + 0) == a[0],a是首元素的地址,*a就是首元素。
//04 : 4/8    because:a是首元素的地址,a+1跳过1个整型,a+1就是第二个元素的地址。
//05 : 4      because:a[1]就是第二个元素。
//06 : 4/8    because:&a是数组的地址,所以是4/8。再次总结,sizeof(数组名)是数组的大小,sizeof(&数组名)是数组的地址。
//07 : 16     because:*&相互抵消,所以等价于sizeof(a)。
//08 : 4/8    because:&a+1是跳过整个数组后的那个位置的地址。
//09 : 4/8    because:首元素的地址。
//10 : 4/8    because:数组第二个元素的地址。

2.2 字符数组

2.2.1 代码1

#include <stdio.h>int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("01:%zd\n", sizeof(arr));printf("02:%zd\n", sizeof(arr + 0));printf("03:%zd\n", sizeof(*arr));printf("04:%zd\n", sizeof(arr[1]));printf("05:%zd\n", sizeof(&arr));printf("06:%zd\n", sizeof(&arr + 1));printf("07:%zd\n", sizeof(&arr[0] + 1));return 0;
}
运行(32位):
016
024
031
041
054
064
074
运行(64位):
016
028
031
041
058
068
078

解析:

解析:
//01:6     because:数组名单独放在sizeof内部,计算的是数组的大小。  
//02:4/8   because:arr是数组名表示首元素的地址,arr+0还是首元素的地址。
//03:1     because:arr是首元素的地址,*arr就是首元素。
//04:1     because:arr[1]就是第二个元素。
//05:4/8   because:&arr是数组地址。
//06:4/8   because:跳过整个数组,指向了数组后边的空间。
//07:4/8   because:第二个元素的地址。

2.2.2 代码2

#include <stdio.h>int main()
{char arr[] = { 'a','b','c','d','e','f' };printf("01:%zd\n", strlen(arr));printf("02:%zd\n", strlen(arr + 0));//printf("03:%zd\n", strlen(*arr));//printf("04:%zd\n", strlen(arr[1]));printf("05:%zd\n", strlen(&arr));printf("06:%zd\n", strlen(&arr + 1));printf("07:%zd\n", strlen(&arr[0] + 1));return 0;
}
运行:
0142
0242
0542
0636
0741

解析:

解析:
//01:unknown because:arr   是首元素的地址,数组中没有\0,结果随机。
//02:unknown because:arr+0 是首元素的地址,数组中没有\0,结果随机。
//03:err     because:arr是首元素的地址,*arr是首元素,就是'a','a'的ascii码值是97
// 相当于把97作为地址传递给了strlen,strlen得到的就是野指针,代码有问题。
//04:err     because:arr[1]->'b'->98,传递给strlen也是错误的。
//05:unknown because:&arr是数组的地址,起始位置是数组的第一个元素的位置,结果随机。
//06:unknown because:随机值 x-6
//07:unknown because:随机值 x-1

2.2.3 代码3

#include <stdio.h>int main()
{char arr[] = "abcdef";printf("01:%zd\n", sizeof(arr));printf("02:%zd\n", sizeof(arr + 0));printf("03:%zd\n", sizeof(*arr));printf("04:%zd\n", sizeof(arr[1]));printf("05:%zd\n", sizeof(&arr));printf("06:%zd\n", sizeof(&arr + 1));printf("07:%zd\n", sizeof(&arr[0] + 1));return 0;
}
运行(32位):
017
024
031
041
054
064
074
运行(64位):
017
028
031
041
058
068
078

解析:

解析:
//01:7     because:数组总大小,7个字节。
//02:4/8   because:首元素地址。
//03:1     because:首元素。
//04:1     because:第二个元素。
//05:4/8   because:数组地址。
//06:4/8   because:跳过整个数组的地址。
//07:4/8   because:第二个元素的地址。

2.2.4 代码4

#include <stdio.h>int main()
{char arr[] = "abcdef";printf("01:%zd\n", strlen(arr));printf("02:%zd\n", strlen(arr + 0));//printf("03:%zd\n", strlen(*arr));//printf("04:%zd\n", strlen(arr[1]));printf("05:%zd\n", strlen(&arr));printf("06:%zd\n", strlen(&arr + 1));printf("07:%zd\n", strlen(&arr[0] + 1));return 0;
}
运行:
016
026
056
0626
075

解析:

解析:
//01:6        because:6
//02:6        because:arr首元素的地址,arr+0还是首元素的地址。
//03:err      because:'a' ->97,err
//04:err      because:'b' ->98,err
//05:6        because:数组地址。
//06:unknown  because:跳过整个数组的地址。
//07:5        because:5

2.2.5 代码5

#include <stdio.h>int main()
{char* p = "abcdef";printf("01:%zd\n", sizeof(p));printf("02:%zd\n", sizeof(p + 1));printf("03:%zd\n", sizeof(*p));printf("04:%zd\n", sizeof(p[0]));printf("05:%zd\n", sizeof(&p));printf("06:%zd\n", sizeof(&p + 1));printf("07:%zd\n", sizeof(&p[0] + 1));return 0;
}
运行(32位):
014
024
031
041
054
064
074
运行(64位):
018
028
031
041
058
068
078

解析:

解析:
//01:4/8      because:p是指针变量,计算指针变量的大小。
//02:4/8      because:b的地址。
//03:1        because:*p是char类型,1个字节。
//04:1        because:p[0] -> *(p+0) -> *p -> 'a' 大小1个字节。
//05:4/8      because:取出的是p的地址。
//06:4/8      because:跳过p指针变量后的地址。
//07:4/8      because:取出字符串首字符的地址,+1是第二个字符的地址。

2.2.6 代码6

#include <stdio.h>int main()
{char* p = "abcdef";printf("01:%zd\n", strlen(p));printf("02:%zd\n", strlen(p + 1));//printf("03:%zd\n", strlen(*p));//printf("04:%zd\n", strlen(p[0]));printf("05:%zd\n", strlen(&p));printf("06:%zd\n", strlen(&p + 1));printf("07:%zd\n", strlen(&p[0] + 1));return 0;
}
运行:
016
025
053
0611
075

解析:

解析:
//01:6       because:传入首元素的地址。
//02:5       because:传入第二个元素的地址。
//03:err     because:'a' -> 97,err
//04:err     because:'a' -> 97,err
//05:unknown because:&p是指针变量p的地址,和字符串"abcdef"关系不大
// 从p这个指针变量的起始位置开始向后数的,p变量存放的地址是什么,不知道,因此随机值。
//06:unknown because:随机值
//07:5       because:第二个字符的地址。

2.3 二维数组

#include <stdio.h>int main()
{int a[3][4] = { 0 };printf("01:%zd\n", sizeof(a));printf("02:%zd\n", sizeof(a[0][0]));printf("03:%zd\n", sizeof(a[0]));printf("04:%zd\n", sizeof(a[0] + 1));printf("05:%zd\n", sizeof(*(a[0] + 1)));printf("06:%zd\n", sizeof(a + 1));printf("07:%zd\n", sizeof(*(a + 1)));printf("08:%zd\n", sizeof(&a[0] + 1));printf("09:%zd\n", sizeof(*(&a[0] + 1)));printf("10:%zd\n", sizeof(*a));printf("11:%zd\n", sizeof(a[3]));return 0;
}
运行(32位):
0148
024
0316
044
054
064
0716
084
0916
1016
1116
运行(64位):
0148
024
0316
048
054
068
0716
088
0916
1016
1116

解析:

解析:
//01:48       because:数组的大小,48 = 3*4*sizeof(int)
//02:4        because:第一行第一个元素。
//03:16       because:第一行的数组名,单独放在sizeof内部了。
//04:4/8      because:a[0]第一行的数组名,但是a[0]并没有单独放在sizeof内部,
// 所以这里的数组名a[0]是数组首元素的地址,+1就是a[0][1]的地址。
//05:4        because:第一行第二个元素。
//06:4/8      because:a作为数组名并没有单独放在sizeof内部,a表示数组首元素的地址,
// 是二维数组首元素的地址,也就是第一行的地址,a+1跳过一行,指向第二行。
//07:16       because:a+1是第二行的地址,*(a+1)就是第二行,计算的是第二行的大小。
// *(a+1) == a[1],a[1]是第二行数组名。
//08:4/8      because:a[0]是第一行的数组名,&a[0]取出的是数组的地址,&a[0]+1就是第二行的地址。
//09:16       because:对第二行的地址解引用。
//10:16       because:a作为数组名并没有单独放在sizeof内部,所以a表示数组首元素的地址,是
// 二维数组首元素的地址,也就是第一行的地址,*a就是第一行,计算的是第一行的大小。
// *a == *(a+0) == a[0]
//11:16       because:a[3]无需真实存在,可以通过类型推断算出长度。a[3]是第四行的数组名。

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;
}
运行:
2,5

画图解析:

3.2 题目2

//在X86环境下  
//假设结构体的大小是20个字节  
//程序输出的结构是啥?  
#include<stdio.h>
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;
}
运行:
00100014
00100001
00100004

解析:

1、struct Test* p = (struct Test*)0x100000; // 将指针p强制指向内存地址 0x100000

2、printf("%p\n", p + 0x1);

  • 当对指针进行加法(如p+0x1)时,实际地址的偏移量 = 指针指向类型的大小 * 加数。
  • p是struct Test*类型,0x100000 + 0x1 * 20 = 0x100000+0x14 = 0x100014

3、printf("%p\n", (unsigned long)p + 0x1);

  • p被强制转化为unsigned long,此时是普通整数加法。

4、printf("%p\n", (unsigned int*)p + 0x1);

  • p被强制转化为unsigned int*,指针运算的单位是unsigned int的大小。

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;
}
运行:
1

解析:

int* p = a[0];        // p 指向第一行首地址(即 &a[0][0])
printf("%d", p[0]);   // 访问 p[0] = a[0][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;
}
运行:
FFFFFFFC,-4

解析:

  • a[4][2]是第四行第2列的元素:
地址 = a基址 + 4行偏移 + 2列偏移  = a + (4×5 + 2)×4 = a + 88 字节
  • p[4][2]是第4个int[4]数组的第2个元素:
地址 = p基址 + 4个数组偏移 + 2列偏移  = a + (4×4 + 2)×4  = a + 72 字节
  • 地址差
&p[4][2] - &a[4][2] = (a + 72) - (a + 88) = -16 字节
  • int为单位的:
-16 字节 / sizeof(int) = -4

输出解释:

  • 在%p格式中,-4的补码表示为0xFFFFFFFC(32位系统)。

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;
}
运行:
10,5

3.6 题目6

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

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;
}
运行:
POINT
ER
ST
EW


完。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78368.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

赛灵思 XCKU115-2FLVB2104I Xilinx Kintex UltraScale FPGA

XCKU115-2FLVB2104I 是 AMD Xilinx Kintex UltraScale FPGA&#xff0c;基于 20 nm 先进工艺&#xff0c;提供高达 1 451 100 个逻辑单元&#xff08;Logic Cells&#xff09;&#xff0c;77 721 600 bit 的片上 RAM 资源&#xff0c;以及 5 520 个 DSP 切片&#xff08;DSP48E…

CAPL编程_03

1_文件操作的相关函数&#xff1a; 读文本文件内容 读取文本文件操作的三部曲 1&#xff09;打开文件 —— openFileRead ( ) 2&#xff09;逐行读取 —— fileGetString ( ) 、fileGetStringSZ ( ) 3&#xff09;关闭文件 —— fileClose ( ) char content[100];…

2025年江西建筑安全员A证适合报考人群

江西建筑安全员A证适合报考人群 江西省建筑安全员A证&#xff08;建筑施工企业主要负责人安全生产考核合格证书&#xff09;主要面向建筑行业管理人员&#xff0c;适合以下人员报考&#xff1a; 1. 企业主要负责人 法人代表、总经理、分管安全副总&#xff1a;依法需持A证&a…

Docker安装(Ubuntu22版)

前言 你是否还在为Linux上配置Docker而感到烦恼&#xff1f; 你是否还在为docker search&#xff0c;docker pull连接不上&#xff0c;而感到沮丧&#xff1f; 本文将解决以上你的所有烦恼&#xff01;快速安装好docker&#xff01; Docker安装 首先&#xff0c;我们得先卸载…

Ubuntu18.04配置C++环境和Qt环境

Ubuntu18.04配置C环境和Qt环境 1、前言3.2 安装其他库3.3 查看有没有安装成功3.4测试C环境 4、配置Qt环境4.1 安装相关的库4.2 测试 5、总结 1、前言 记录一下Ubuntu18.04配置C环境和Qt环境的过程&#xff0c;方便自己日后回顾&#xff0c;也可以给有需要的人提供帮助。 # 2…

ACWing——算法基础课

置顶思考&#xff1a; 算法的本质是什么样的思想&#xff1f; 这种思想可以解决哪类问题&#xff1f; 有没有其他的解决思路&#xff1f; 关注数值范围&#xff0c;思考可不可以针对性解决问题&#xff1f; 目录 https://leetcode.cn/circle/discuss/RvFUtj/ 滑动窗口与双指针…

私钥连接服务器(已经有服务器私钥

前言&#xff1a;假设我们已经有了服务器的私钥&#xff0c;我们怎么配置呢&#xff1f; 下面我会从vsc的配置角度来写 ✅ 步骤一&#xff1a;准备工作 安装 VS Code&#xff08;如果还没装&#xff09; &#x1f449; https://code.visualstudio.com/ 安装插件&#xff1a;Re…

Redis LFU 策略参数配置指南

一、基础配置步骤‌ 设置内存上限‌ 在 redis.conf 配置文件中添加以下指令&#xff0c;限制 Redis 最大内存使用量&#xff08;例如设置为 4GB&#xff09;&#xff1a; maxmemory 4gb选择 LFU 淘汰策略‌ 根据键的作用域选择策略&#xff1a; # 所有键参与淘汰 maxmemory-…

嵌入式 C 语言面试核心知识点全面解析:基础语法、运算符与实战技巧

在嵌入式面试中&#xff0c;C 语言基础是重中之重。本文针对经典面试题进行详细解析&#xff0c;帮助新手系统掌握知识点&#xff0c;提升面试应对能力。 一、数据结构逻辑分类 题目 在数据结构中&#xff0c;从逻辑上可以把数据结构分为&#xff08; &#xff09;。 A、动态…

11.AOP开发

十一、AOP开发 1、Spring Boot实现 AOP 11.1.1、SpringBootAop简介 Spring Boot的AOP编程和Spring框架中AOP编程的唯一区别是&#xff1a;引入依赖的方式不同,其他内容完全一样 Spring Boot中AOP编程需要引入aop启动器&#xff1a; <!--aop启动器--> <dependency…

【网络入侵检测】基于源码分析Suricata的PCAP模式

【作者主页】只道当时是寻常 【专栏介绍】Suricata入侵检测。专注网络、主机安全,欢迎关注与评论。 1. 概要 👋 本文聚焦于 Suricata 7.0.10 版本源码,深入剖析其 PCAP 模式的实现原理。通过系统性拆解初始化阶段的配置流程、PCAP 数据包接收线程的创建与运行机制,以及数据…

.NET 10 中的新增功能

.NET 运行时 .NET 10 运行时引入了新功能和性能改进。 关键更新包括&#xff1a; 数组接口方法反虚拟化&#xff1a;JIT 现在可以取消虚拟化和内联数组接口方法&#xff0c;从而提高数组枚举的性能。数组枚举去抽象化&#xff1a;改进功能以通过枚举器减少数组迭代的抽象开销…

盲注命令执行(Blind Command Execution)

一、核心原理 1. 无回显命令执行的本质 盲命令执行&#xff08;Blind Command Execution&#xff09;是一种攻击形式&#xff0c;攻击者通过注入系统命令到Web应用或后端系统中&#xff0c;但无法直接获取命令执行结果。盲命令执行的本质在于攻击者无法直接看到执行结果&#x…

Linux多线程技术

什么是线程 在一个程序里的多执行路线就是线程。线程是进程中的最小执行单元&#xff0c;可理解为 “进程内的一条执行流水线”。 进程和线程的区别 进程是资源分配的基本单位&#xff0c;线程是CPU调度的基本单位。 fork创建出一个新的进程&#xff0c;会创建出一个新的拷贝&…

计算机组成原理实验(1) 算术逻辑运算单元实验

实验一 算术逻辑运算单元实验 一、实验目的 1、掌握简单运算器的数据传输方式 2、掌握74LS181的功能和应用 二、实验内容 1、不带进位位逻辑或运算实验 2、不带进位位加法运算实验 3、实验指导书2.15实验思考 三、实验步骤和结果 实验内容一&#xff1a;不带进位…

Android将启动画面实现迁移到 Android 12 及更高版本

如果在 Android 11 或更低版本中实现自定义启动画面&#xff0c;请迁移应用迁移到 SplashScreen API 以获取帮助 确保其在 Android 12 及更高版本中正确显示。 从 Android 12 开始&#xff0c;在所有应用的冷启动和温启动期间&#xff0c;系统都会应用 Android 系统的默认启动…

692. 前K个高频单词(map的练习)

目录 1、题目分析 2.解题思路 3.代码实现 4.总结 1、题目分析 2.解题思路 首先它给出我们一个string&#xff0c;让我们提取出它们中出现次数最多的。利用map将word一个一个存入其中&#xff0c;没有就插入&#xff0c;有了就1&#xff0c;这样我们就得到了key_value&#…

如何创建极狐GitLab 议题?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 创建议题 (BASIC ALL) 创建议题时&#xff0c;系统会提示您输入议题的字段。 如果您知道要分配给议题的值&#xff0c;则可…

day32 学习笔记

文章目录 前言一、霍夫变换二、标准霍夫变换三、统计概率霍夫变换四、霍夫圆变换 前言 通过今天的学习&#xff0c;我掌握了霍夫变换的基本原本原理及其在OpenCV中的应用方法 一、霍夫变换 霍夫变换是图像处理中的常用技术&#xff0c;主要用于检测图像中的直线&#xff0c;圆…

图解YOLO(You Only Look Once)目标检测(v1-v5)

1. YOLO系列整体介绍 YOLO属于深度学习经典检测方法中的单阶段&#xff08;one - stage&#xff09;类型&#xff0c;与两阶段&#xff08;two - stage&#xff0c;如Faster - rcnn、Mask - Rcnn系列&#xff09;方法相对。 不同模型性能 单阶段方法的最核心优势是速度非常快…