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

【C语言】深入理解指针(五):sizeof、strlen与数组指针的那些事儿

前言

在C语言的学习中,指针始终是一个让人又爱又恨的话题。它强大而灵活,但同时也充满了陷阱。今天,我们就来深入探讨一下指针相关的几个重要知识点:sizeof和strlen的区别,以及数组和指针在笔试题中的那些常见问题。希望通过这篇文章,能帮你更好地理解指针的精髓。
sizeof 与 strlen:你真的了解它们的区别吗?
sizeof:内存空间的度量衡
sizeof是一个操作符,它的作用是计算变量或类型所占用的内存空间大小,单位是字节。它只关心内存空间的大小,而不关心内存中存储了什么数据。比如下面的代码:

#include <stdio.h>
int main()
{
    int a = 10;
    printf("%d\n", sizeof(a)); // 输出变量a所占内存大小
    printf("%d\n", sizeof a);  // sizeof后可省略括号
    printf("%d\n", sizeof(int)); // 输出int类型变量所占内存大小
    return 0;
}

无论变量a中存储的是什么值,sizeof(a)的结果都是固定的,取决于int类型在当前系统中所占用的字节数。
strlen:字符串长度的探针
strlen是C语言库函数,用于计算字符串的长度。它的函数原型是size_t strlen(const char *str);,它从参数str指向的地址开始,统计到第一个\0字符之前的字符个数。需要注意的是,strlen会一直向后查找\0字符,如果字符串没有以\0结尾,可能会导致越界查找。比如:

#include <stdio.h>
#include <string.h>
int main()
{
    char arr1[3] = {'a', 'b', 'c'};
    char arr2[] = "abc";
    printf("%d\n", strlen(arr1)); // 可能会越界查找
    printf("%d\n", strlen(arr2)); // 输出字符串长度
    return 0;
}

这里arr1没有以\0结尾,所以strlen(arr1)可能会导致越界查找,而arr2是一个以\0结尾的字符串,strlen(arr2)会正确输出字符串长度。
sizeof vs strlen:本质区别
sizeof:操作符,计算操作数所占内存大小,单位是字节,不关注内存中存储的数据。
strlen:库函数,计算字符串长度,统计\0之前的字符个数,关注内存中是否有\0,可能会越界。
数组与指针:笔试题中的常客
一维数组的sizeof与指针操作
数组名在大多数情况下表示首元素的地址,但在sizeof和&操作中,数组名表示整个数组。比如:

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])); // 计算数组第二个元素的大小

这里sizeof(a)计算的是整个数组的大小,而sizeof(a+0)计算的是指针的大小,因为a+0是一个指针表达式。
字符数组的sizeof与strlen
字符数组在sizeof和strlen操作中表现得尤为复杂。比如:

char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
printf("%d\n", sizeof(arr)); // 计算整个字符数组的大小
printf("%d\n", strlen(arr)); // 计算字符串长度,可能会越界

这里sizeof(arr)计算的是整个字符数组的大小,包括所有字符占用的空间。而strlen(arr)会从数组首地址开始查找\0字符,计算字符串长度。但如果数组中没有\0字符,strlen可能会越界。
二维数组的sizeof操作
二维数组的sizeof操作同样需要注意数组名的意义。比如:

int a[3][4] = {0};
printf("%d\n", sizeof(a)); // 计算整个二维数组的大小
printf("%d\n", sizeof(a[0])); // 计算二维数组第一行的大小
printf("%d\n", sizeof(a[0][0])); // 计算二维数组第一个元素的大小

这里sizeof(a)计算的是整个二维数组的大小,sizeof(a[0])计算的是二维数组第一行的大小,sizeof(a[0][0])计算的是二维数组第一个元素的大小。
指针运算笔试题解析:挑战你的思维极限
题目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;
}

这个题目中,&a + 1表示数组a的地址加上一个int数组的大小,然后强制转换为int指针。*(ptr - 1)实际上访问的是数组a的最后一个元素。
题目2:结构体指针的偏移

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

这个题目中,p + 0x1会按照结构体Test的大小进行偏移,而(unsigned long)p + 0x1和(unsigned int*)p + 0x1则会按照unsigned long和unsigned int的大小进行偏移。
题目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;
}

这个题目中,a[0]是一个数组,直接赋值给指针p,p指向的是数组a[0]的首地址。p[0]访问的是数组a[0]的第一个元素。
题目4:指针数组的偏移

#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是一个指向int[4]的指针,&p[4][2]和&a[4][2]的偏移量计算需要注意指针类型和数组维度。
题目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;
}

这个题目中,&aa + 1表示整个二维数组aa的地址加上一个int[2][5]的大小,*(aa + 1)表示访问二维数组aa的第二行。
题目6:指针数组的递增

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

这个题目中,pa是一个指向指针的指针,pa++会按照指针数组的大小进行偏移,*pa访问的是指针数组中的下一个字符串。
题目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;
}

这个题目中,cpp是一个指向指针数组的指针,**++cpp、++cpp + 3等操作需要仔细分析指针的偏移和解引用顺序。
总结
通过今天的学习,我们深入探讨了sizeof和strlen的区别,以及数组和指针在笔试题中的常见问题。希望这些内容能帮助你在学习C语言指针时少走弯路。指针的学习需要多练习、多思考,只有通过不断地实践,才能真正掌握它的精髓。如果你对这些内容还有疑问,欢迎在评论区留言,我们一起探讨!

http://www.dtcms.com/a/106559.html

相关文章:

  • 【学Rust写CAD】26 图形像素获取(pixel_fetch.rs)
  • 红日靶场一实操笔记
  • C++ QT 如何生成dll提供給python使用
  • 星途​(小说)
  • 零欧姆电阻的作用、使用场景及注意事项详解
  • PyTorch 核心详解
  • 第六章、Isaacsim中的资产(usd)
  • 【嵌入式系统设计师】知识点:第1章 计算机系统基础知识
  • 方案精读:IPD业务流程体系构建(中)【附全文阅读】
  • 介绍一点metric self-join和复合索引笔记
  • 数组中两个字符串的最小距离
  • 【深度学习量化交易19】行情数据获取方式比测(1)——基于miniQMT的量化交易回测系统开发实记
  • CCF CSP 第34次(2024.06)(2_矩阵重塑(其二)_C++)(二维矩阵 -> 一维矩阵 -> 二维矩阵)
  • 【万字总结】前端全方位性能优化指南(九)——FSP(First Screen Paint)像素级分析、RUM+合成监控、Lighthouse CI
  • 蓝桥杯C++基础算法-最大公约数
  • 论文阅读:基于增强通用深度图像水印的混合篡改定位技术 OmniGuard
  • 电池自动点焊机:智能制造的得力助手|深圳比斯特自动化
  • 普通链式二叉树(习题版)
  • 脑影像分析软件推荐| SimTB
  • WireShark安装
  • BigMusic来了:火山引擎AI音乐模型的技术革新与应用实践
  • MySQL 错误 报错:Table ‘performance_schema.session_variables’ Doesn’t Exist
  • Doris:打破 SQL 方言壁垒,构建统一数据查询生态
  • TensorFlow 字符串操作
  • Uubuntu20.04复现SA-ConvONet步骤
  • 【2025】物联网发展趋势介绍
  • 制造业数字化转型:智能招聘系统破解蓝领用工匹配难题?
  • MySQL学习笔记集--简单介绍以及下载途径
  • EdgeOne Pages 上线「DeepSeek R1 模板」,1分钟快速部署对话型 AI 类网站
  • MySQL表的增删改查(进阶)