【Android】自定义View组件,并实现在 Compose、Kotlin、Xml 中调用

从事 Android 开发以来,很少有过自定义 View 的相关开发需求,大部分 UI 都是可以集成某些官方组件,在组件的基础上完成能够大大缩短开发时间。但今天我要讲的是:如何使用 Android 开发一个Compose、Xml都可以调用的组件?接下来请跟随我的脚步一起去学习 View 的自定义组件开发吧。

目录

  • Android 屏幕坐标
  • 自定义 View 的方式
  • 自定义 View
    • 初始化
      • 重写构造函数
      • 自定义 XML 属性
    • 测量大小 onMeasure
    • 确定大小 onSizeChanged
    • 确定子布局位置 onLayout
    • 绘制 onDraw
  • 在Xml中引用
  • 在Activity中引用
  • 在Compose中引用
  • 最终效果

Android 屏幕坐标

自定义 View 之前,需要先了解 View 的坐标,知道哪里是起点,哪里是终点,才能更好的展开工作。

经历过九年义务教育的朋友们,相信大家都见过下面的这幅图 👇

平面直角坐标系
很眼熟吧?这张图片的东西被称之为 👉 平面直角坐标系。

在 Android 系统上,也是用的 平面直角坐标系 来确定 View 的方向、大小,只不过它是“倒”过来的平面直角坐标系,如下图👇

在这里插入图片描述
不明白?我们再把这个图片代入到 Android 屏幕上来

在这里插入图片描述
看懂了吧?在 Android 系统上,直角坐标系的原点就是屏幕的左上角,往右是 x 轴,往下是 y 轴,整个 Android 的屏幕就是处于 平面直角坐标系 的第四象限上,其坐标的单位则使用的是像素(px) 来表示。如果你的手机是 1080*1920 像素,则意味着,以原点为起点,至屏幕的右侧,共有 1080 像素 (px) ,以原点为起点,至屏幕的底部,共有 1920 个像素(px)。

自定义 View 的方式

在 Android 中,自定义 View 一般可分为两种方式:继承 ViewGroup 或 View 实现自定义。

  • ViewGroup
    自定义 ViewGroup 一般是利用现有的组件根据特定的布局方式来组成新的组件,大多继承自 ViewGroup 或各种 Layout ,包含子 View。
  • View
    在没有现成的View,需要自己实现的时候,就是用自定义 View,一般继承自 View、SurfaceView 或其它的 View。

本文只讲解通过继承 View 来实现自定义 View,通过继承 ViewGroup 实现自定义 View 的文章可参考往期文章👉【Android】实现自定义标题栏

自定义 View

View 完成自定义的过程需要经历:初始化 → onMeasure → onSizeChanged → onLayout → onDraw 五个阶段。

初始化

重写构造函数

View 的初始化方式可通过四种构造函数进行初始化,如下:

constructor(context: Context?) : this(context, null)

constructor(context: Context?, attrs: AttributeSet?) : this(context, attrs, 0)

constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : this(context, attrs, defStyleAttr, 0)

constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {}
构造函数使用场景
public View(Context context)一般在 Activity、Fragment 中使用(本文使用该构造函数):
View view = new View(context);
public View(Context context, AttributeSet attrs)当从XML文件构造视图,提供XML文件中指定的属性时,会调用此函数(本文使用该构造函数):
< View android:layout_width=“wrap_content” android:layout_height=“wrap_content”/>
public View(Context context, AttributeSet attrs, int defStyleAttr)从XML执行膨胀,并从主题属性应用特定于类的基本样式。View的这个构造函数允许子类在膨胀时使用自己的基本样式。
public View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)从XML执行膨胀,并从主题属性或样式资源应用特定于类的基本样式。View的这个构造函数允许子类在膨胀时使用自己的基本样式。(本文使用该构造函数)

注意:重写构造方法时,拥有四个参数的那个构造函数必须使用super用于访问父类的构造方法,另外三个构造方法,则需要使用this指引下一个构造方法。这样,当调用第一、二、三个构造方法时,就会执行第四个构造方法,使用该方式才能使UI渲染上,否则会出现实例化 View 无效的情况出现。具体参考上方的四个构造方法。

自定义 XML 属性

如果自定义的 View 在 XML 布局上用的到,自定义属性这一步则少不了,届时需要在 res/values/arrts.xml 文件夹添加 XML 的属性。若没有 arrts.xml 文件则需手动创建。创建完成后在其中编写的代码如下:

<resources>
    <declare-styleable name="StepView">
        <attr name="type">
	        <!--      添加枚举,在布局样式使用type属性时可直接使用以下的选项      -->
            <enum name="start" value="0" />
            <enum name="middle" value="1" />
            <enum name="stop" value="2" />
        </attr>
        <attr name="text" format="string" localization="suggested" />
        <attr name="textSize" format="dimension" />
        <attr name="style" format="integer" >
	        <!--      添加枚举,在布局样式使用style属性时可直接使用以下的选项      -->
            <enum name="selected" value="10"/>
            <enum name="not_selected" value="11"/>
        </attr>
    </declare-styleable>
</resources>

编写了名为StepView的属性样式,需要在自定义 View 类里的构造函数中使用 Context.obtainStyledAttributes函数引用,通过循环遍历的方式找到属性赋值给对应的值,这时,就完成了 XML 布局属性的自定义。

constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int, ) : super(context, attrs, defStyleAttr, defStyleRes) {
    //获取定义的一些属性
    val styleAttrs = context!!.obtainStyledAttributes(attrs, R.styleable.StepView, defStyleAttr, 0)
    //数一数有多少个属性呢
    val indexCount = styleAttrs.indexCount
    // 循环遍历的方式,找到我们所定义的一些属性
    for (i in 0..indexCount) {
        //根据索引值给java代码中的成员变量赋值
        when (val index = styleAttrs.getIndex(i)) {
            R.styleable.StepView_text -> text = styleAttrs.getString(index).toString() 
            R.styleable.StepView_textSize -> textSize = styleAttrs.getDimension(index, 2f)
            R.styleable.StepView_type -> type = styleAttrs.getInt(index, type)
            R.styleable.StepView_style -> style = styleAttrs.getInteger(index, STYLE_NOT_SELECTED)
        }
    }
    //资源文件中的属性回收
    styleAttrs.recycle()
}

测量大小 onMeasure

为什么要测量 View 大小?

View 的大小不仅由自身大小所决定,同时也受到父控件的影响,为了我们的控件能更好的适应各种情况,一般需要自己进行测量。

测量 View 大小使用的是 View 的 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法进行测量,为了更好的适配各种分辨率,自定义 View 的过程中,必须由重写该方法。

重写方法,还需要使用setMeasuredDimension(int measuredWidth, int measuredHeight)方法使测量好的宽高产生效果,如下:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    // 获取宽高的测量模式
    val widthSpecMode = MeasureSpec.getMode(widthMeasureSpec)
    val heightSpecMode = MeasureSpec.getMode(heightMeasureSpec)
    // 获取宽高的测量大小
    val widthSpecSize = MeasureSpec.getSize(widthMeasureSpec)
    val heithtSpecSize = MeasureSpec.getSize(heightMeasureSpec)
    if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
        val fl = mWidthTotal.toFloat() / 1.5f
        setMeasuredDimension(fl.toInt() + indentAndBulge.toInt(), mHeight.toInt())
    } else if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
        // 宽 / 高任意一个布局参数为= wrap_content时,都设置默认值
        val fl = mWidthTotal.toFloat() / 1.5f
        setMeasuredDimension(fl.toInt() + indentAndBulge.toInt(), heithtSpecSize)
    } else if (layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
        // 宽 / 高任意一个布局参数为= wrap_content时,都设置默认值
        setMeasuredDimension(widthSpecSize, mHeight.toInt())
    }
}

注意:若是没有重写onMeasure方法并完成测量,在给该组件定义宽高为wrap_content时,组件的宽、高度会默认跟随父类。具体原因请查看👉Android 自定义View:为什么你设置的wrap_content不起作用?

确定大小 onSizeChanged

在测量完 View 并使用 setMeasuredDimension(int measuredWidth, int measuredHeight) 函数之后,View 的大小基本上已经确定,那为什么还需要再次确定 View 的大小呢?

这是因为 View 的大小不仅由 View 本身控制,而且受父控件的影响,所以我们在确定 View 大小的时候最好使用系统提供的 onSizeChanged(int w, int h, int oldw, int oldh) 回调函数。

override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
    super.onSizeChanged(w, h, oldw, oldh)
    Log.e("onSizeChanged", "View的宽度:$w,高度$h")
}

onSizeChanged 方法的四个参数分别为:

  • w – Current width of this view.
  • h – Current height of this view.
  • oldw – Old width of this view.
  • oldh – Old height of this view.

此函数相对简单,我们只需要关注其宽度(w)、高度(h) 即可,这两个参数就是 View 的最终大小。该函数只会在 View 的大小发生改变时自动触发,例如:初始化View、界面横竖屏的切换等。

确定子布局位置 onLayout

确定子布局的函数是 onLayout(boolean changed, int left, int top, int right, int bottom) ,它用于确定子 View 在父 View 的位置。
当此视图应为其每个子级分配大小和位置时,从布局调用。具有子级的派生类应重写此方法,并在其每个子级上调用布局。

/**
 * 如果自定义的 View 有子组件时,必须重写该方法,用以确定子组件在 View 中的位置.
 * 如果有需要自定义有子 View 的组件时,应该继承 ViewGroup 而不是 View,具体看情况自己分析
 */
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    super.onLayout(changed, left, top, right, bottom)
    Log.e("onLayout","$left,$top,$right,$bottom")
}

绘制 onDraw

扯了这么多,终于来到自定义 View 的最后一步了!

在上文我们说到 Android 的 平面直角坐标系 以及 Android 屏幕在 平面直角坐标系 中的位置是位于第四象限。那接下来我们如何在 Canvas 画布上绘制出自己想要的样式呢?

Android 中,提供了 Canvas 作为画画的载体,所绘制的东西最终呈现在 Canvas 上,因此也可以理解为 Canvas 是一张纸,Paint 则是五颜六色的笔。Canvas 作为画布(白纸),提供了多个在画布上画的函数给我们调用:

Canvas FunctionDescribe
drawARGB使用 srcover porterduff 模式,用指定的 ARGB 颜色填充整个画布的位图(仅限于当前剪辑)。
drawArc绘制指定的圆弧,该圆弧将缩放以适合指定的椭圆形。
drawBitmap使用指定的矩阵绘制位图。
drawBitmapMesh通过网格绘制位图,其中网格顶点均匀分布在位图上。
drawCircle使用指定的颜料绘制指定的圆。
drawColor使用指定的颜色和混合模式填充整个画布的位图(仅限于当前剪辑)。
drawDoubleRoundRect使用指定的 paint 绘制双圆角矩形。
drawGlyphs使用指定字体绘制字形数组。
drawLine使用指定的绘图绘制具有指定起点和终点 x,y 坐标的线段。
drawMesh将网格对象绘制到屏幕上。
drawOval使用指定的颜料绘制指定的椭圆形。椭圆形将根据绘画中的样式进行填充或加框。
drawPaint用指定的绘画填充整个画布的位图(仅限于当前剪辑)。
drawPatch将指定的位图绘制为 N 面片(最常见的是 9 面片)。
drawPath使用指定的油漆绘制指定的路径。路径将根据绘画中的样式进行填充或加框。
drawPicture绘制图片,拉伸以适合 dst 矩形。
drawPoint用于绘制单个点的 drawPoints() 的帮助程序。
drawRGB使用 srcover porterduff 模式,用指定的 RGB 颜色填充整个画布的位图(仅限于当前剪辑)。
drawRect使用指定的绘制绘制指定的矩形,矩形将根据绘画中的样式进行填充或加框。
drawRenderNode绘制给定的 RenderNode。
drawRoundRect使用指定的油漆绘制指定的圆角矩形,圆角矩形将根据绘画中的样式进行填充或加框。
drawText在指定的 Paint 中绘制指定范围的文本,由开始/结束指定,其原点位于 (x,y)。
drawTextOnPath使用指定的绘画,沿着指定的路径绘制文本,原点位于 (x,y)。
drawTextRun绘制一系列文本,全部在一个方向上,并带有用于复杂文本形状的可选上下文。
drawVertices绘制顶点数组,解释为三角形(基于模式)。

此处展示的函数并非全部,详情请参考👉Canvas

本文自定义 View 绘制的 UI 只需要使用到 drawTextdrawPath 两个 Canvas 函数进行绘制。drawPath 在这里用来绘制背景,drawText绘制文字。

接下来绘制的图形:

在这里插入图片描述
使用 drawPath 进行绘制前,需要先使用 Path 定义好 drawPathcanvas 经过的几个点,这里画个图,以便参考:

在这里插入图片描述
接着写代码实现:

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    // 新建绘制的路径
    val path = Path()
    // 第一步,定原点
    path.moveTo(0f, 0f)
    // 第二步
    path.lineTo(totalWidth, 0f)
    // 第三步
    val rightBulge = totalWidth + indentAndBulge
    path.lineTo(rightBulge, halfHeight)
    // 第四步
    path.lineTo(totalWidth, mHeight)
    // 第五步
    path.lineTo(0f, mHeight)
    // 回到原点,闭合图形
    path.lineTo(0f, 0f)
    path.close()
    // 将图形绘制出来
    canvas.drawPath(path, mPaint)
}

完成了背景的绘制,接下来绘制 Text

mPaint.color = if (style == STYLE_NOT_SELECTED) context.getColor(R.color.font_black) else context.getColor(R.color.white)
canvas.drawText(text, startPointText, mHeight / 1.5f, mPaint)

至此,完成了自定义的全部过程。

在Xml中引用

在 xml 中引用和官方的 View 组件引用没有太大的差别,如下:

<com.miyue.stepdemo.StepView
    android:id="@+id/step1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

我们自定义开始时,在attrs.xml文件里自定义了一些组件的属性,添加后如下:

<com.miyue.stepdemo.StepView
    android:id="@+id/step1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:style="selected"
    app:text="第一步"
    app:type="start"/>

注意:在 xml 中初始化的组件,是无法使用Debug断点调试的,但可通过Logcat查看日志信息。

在Activity中引用

val step = StepView(this)
step.setStyle(StepView.STYLE_SELECTED)
step.setType(StepView.TYPE_START)
step.setText("第一步")
step.setTextSize(30f)
val linearLayout = findViewById<LinearLayout>(R.id.ll_step2)
linearLayout.addView(step)

在Compose中引用

如果你的 Android 项目是 Java 项目,建议你创建一个 Kotlin 项目或者 Compose 项目。如果你的项目是 Kotlin 项目,则可以通过以下 的方式创建一个 Compose 界面:鼠标右键包名 → New → Compose → Empty Activity。

在这里插入图片描述
完成上述步骤即可获得一个 Compose 的 Activity,通过以下代码即可完成调用继承自 View 的组件调用。

@Composable
fun CustomView() {
    AndroidView(
        modifier = Modifier.fillMaxSize(), // Occupy the max size in the Compose UI tree
        factory = { context ->
            // Creates view
            StepView(context).apply {
                setText("第一步")
                setTextSize(30f)
                setStyle(StepView.STYLE_SELECTED)
                setType(StepView.TYPE_START)
            }
        },
        update = { view ->
            // 视图已膨胀或此块中读取的状态已更新
            // 如有必要,在此处添加逻辑
            // 由于selectedItem在此处阅读,AndroidView将重新组合
            // 每当状态发生变化时
            // 撰写示例->查看通信

            // 更新样式
            view.setText("第二步")
        }
    )
}

最终效果

在这里插入图片描述

点击前往下载代码👉StepDemo

总结

能找美工处理的就找美工处理,能不碰 View 自定义就不要碰 View 自定义,一旦开始自定义,意味着需要花很长的时间去处理自定义产生的各种适配问题,投入的时间用与产生的收益不成正比。

参考文档

1、【扔物线】UI-1 Drawing
2、HenCoder Android 开发进阶: 自定义 View 1-1 绘制基础
3、HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解
4、【Android Developer】在 Compose 中使用 View

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[蓝桥 2023] 位乘积计数

问题描述 给定两个正整数 n 和 m&#xff0c;请你计算出&#xff0c;从 1到 n 的所有整数中&#xff0c;满足每一位上的数字乘积小于等于 m 的数字的个数。 例如&#xff0c;当 n12&#xff0c;m3 时&#xff0c;满足条件的数字有 1,2,3,10,11,12 共 6 个。 输入格式 输出格…

[LLM]大模型训练(二)--DeepSpeed使用

安装DeepSpeed与集成 DeepSpeed可以通过pip安装&#xff0c;无需指定PyTorch和CUDA的版本。DeepSpeed内包含需要自定义的CUDA算子&#xff0c;将通过即时编译的方式在运行时构建。 pip install deepspeed DeepSpeed与HuggingFace Transformers直接集成。使用者可以通过在模型…

使用WAZUH检测LD_PRELAOD劫持、SQL注入、主动响应防御

目录 1、检查后门 使用工具检测后门 1.chkrootkit 2.rkhunter 手动检查文件 检查ld.so.preload文件 2、检测LD_PRELOAD ubuntu配置 wazuh配置 3、检测SQL注入 ubuntu配置 攻击模拟 4、主动响应 wauzh的安装以及设置代理可以参考本篇&#xff1a;WAZUH的安装、设置…

自行车服务PEDALWAYS 网站bootstrap5模板

一、需求分析 自行车服务网站的作用是为骑行爱好者和自行车用户提供便捷的信息、工具和服务&#xff0c;以满足他们的需求。以下是一些常见的自行车服务网站的功能&#xff1a; 自行车租赁&#xff1a;提供自行车租赁服务&#xff0c;用户可以在线预订自行车并选择租赁期限&am…

【Java 数组解析:探索数组的奇妙世界】

数组的引入 我们先通过一段简单的代码引入数组的概念。 import java.util.Scanner; public class TestArray01{public static void main(String[] args){//功能&#xff1a;键盘录入十个学生的成绩&#xff0c;求和&#xff0c;求平均数&#xff1a;//定义一个求和的变量&…

C语言与人生:数组交换和二分查找

少年们&#xff0c;大家好。我是博主那一脸阳光&#xff0c;今天和分享数组交换和二分查找。 前言&#xff1a;探索C语言中的数组交换操作与二分查找算法 在计算机编程领域&#xff0c;特别是以C语言为代表的低级编程语言中&#xff0c;对数据结构的理解和熟练运用是至关重要的…

【小白专用】winform启动界面+登录窗口 更新2024.1.1

需求场景&#xff1a;先展示启动界面&#xff0c;然后打开登录界面&#xff0c;如果登录成功就跳转到主界面 首先在程序的入口路径加载启动界面&#xff0c;使用ShowDialog显示界面&#xff0c; 然后在启动界面中添加定时器&#xff0c;来实现显示一段时间的效果&#xff0c;等…

Spring 是如何解决循环依赖问题的方案

文章目录 Spring 是如何解决循环依赖问题的&#xff1f; Spring 是如何解决循环依赖问题的&#xff1f; 我们都知道&#xff0c;如果在代码中&#xff0c;将两个或多个 Bean 互相之间持有对方的引用就会发生循环依赖。循环的依赖将会导致注入死循环。这是 Spring 发生循环依赖…

电机(一):直流有刷电机和舵机

声明&#xff1a;以下图片来自于正点原子&#xff0c;仅做学习笔记使用 电机专题&#xff1a; 直流电机&#xff1a;直流有刷BDC&#xff08;内含电刷&#xff09;&#xff0c;直流无刷BLDC&#xff08;大疆的M3508和M2006&#xff09;,无刷电机有以下三种形式&#xff1a;&a…

超市订单管理系统

比较简单的超市订单管理系统

十大排序的个人总结之——选择排序

一、选择排序&#xff1a; 选择排序是所以用到它的时候&#xff0c;数据规模越小越好。 时间复杂度&#xff1a;无最好最坏&#xff0c;永远都是O(n) 不占用额外空间&#xff08;唯一好处&#xff09; 还不稳定&#xff08;几乎已经被淘汰了的排序算法&#xff09; 1. 算法…

基于Springboot实现天天生鲜销售电商平台

SSM毕设分享 基于Springboot实现天天生鲜销售电商平台 1 项目简介 Hi&#xff0c;各位同学好&#xff0c;这里是郑师兄&#xff01; 今天向大家分享一个毕业设计项目作品【】 师兄根据实现的难度和等级对项目进行评分(最低0分&#xff0c;满分5分) 难度系数&#xff1a;3分 …

MySQL基础笔记(1)基础理论

一.基本概念 DB&#xff1a;数据库&#xff0c;存储数据的仓库&#xff0c;数据是有组织地进行存储DBMS&#xff1a;操纵和管理数据库的大型软件 SQL&#xff1a;结构化查询语言&#xff0c;操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库的统一标准 &…

uniapp:实现手机端APP登录强制更新,从本地服务器下载新的apk更新,并使用WebSocket,实时强制在线用户更新

实现登录即更新&#xff0c;或实时监听更新 本文介绍的是在App打开启动的时候调用更新&#xff0c;点击下方链接&#xff0c;查看使用WebSocket实现实时通知在线用户更新。 uniapp&#xff1a;全局消息是推送&#xff0c;实现app在线更新&#xff0c;WebSocket&#xff0c;ap…

ECharts与Excel的火花

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、ECharts&#xff1a;现代数据可视化的利器 二、Excel&#xff1a;经典的数据处理与分析工具 三、ECharts与Excel的结合&#…

Linux软件包管理器——yum命令

如何使用yum 一、快速认识yum(简单介绍)二、快速使用yum2.1 rzsz2.2 linux命令行小游戏和彩蛋 三、yum的整个生态问题 一、快速认识yum(简单介绍) Linux中我们也要进行工具/指令/程序&#xff0c;安装&#xff0c;检查卸载等&#xff0c;需要yum的软件 安装软件的方式&#xf…

视频合并软件,重塑你的创意世界

在数字化的世界里&#xff0c;视频已经成为了我们表达自我、传递信息的重要方式。而合并视频&#xff0c;更是将这种表达推向了一个新的高度。通过简单的操作&#xff0c;我们不仅能够将不同的视频完美地融合在一起&#xff0c;更能赋予它们全新的含义。 所需工具&#xff1a;…

HarmonyOS4.0系统性深入开发11通过message事件刷新卡片内容

通过message事件刷新卡片内容 在卡片页面中可以通过postCardAction接口触发message事件拉起FormExtensionAbility&#xff0c;然后由FormExtensionAbility刷新卡片内容&#xff0c;下面是这种刷新方式的简单示例。 在卡片页面通过注册Button的onClick点击事件回调&#xff0c;…

ShuffleNet V2:高效CNN架构设计实用指南

摘要 目前&#xff0c;神经网络架构设计主要以计算复杂度的间接指标&#xff08;即 FLOPs&#xff09;为指导。然而&#xff0c;直接指标&#xff08;如速度&#xff09;还取决于其他因素&#xff0c;如内存访问成本和平台特性。因此&#xff0c;这项工作建议在目标平台上评估…

C语言之sizeof详解,5点透析,帮你真正了解它

今天也要继续坚持 前言 今天复习C语言了解到不少和她有关的知识&#xff0c;才知道之前对他了解甚少&#xff0c;于是写下博客及时记录自己的所得&#xff0c;与大家分享一下 第一点&#xff1a;sizeof不是函数 sizeof是一个关键字而不是函数&#xff01;是的&#xff0c;他…