草庐IT

一文入门Qt Quick

ENG八戒 2023-03-28 原文

以下内容为本人的著作,如需要转载,请声明原文链接 微信公众号「englyf」https://mp.weixin.qq.com/s/dvamU6q5lZQb5hztfD2zNg


初识Qt Quick

很高兴可以来到这一章,终于可以开始讲讲最近几年Qt的热门技术Quick这一块了。

啥是Qt?

哦,这是一个宣称可以跨任意平台,开发各种场景应用软件的开发框架。从三个维度来讲,就是开发库framework,集成开发平台IDE,以及成熟的开发思维模式。

Qt Quick最早出现在Qt的4.7版本中,目标是在UI设计者与开发者之间搭建一个更高效合作平台,给开发者更好的UI开发体验。虽然几经易手,Qt在digia公司这些年的努力迭代更新下,Qt Quick终于迎来了成熟稳定的版本(这也是我愿意在最近的项目里转用它的原因)。

至于Qt Quick和老一套开发核心Qwidget的区别,其中最重点的就是提供了新的UI描述语言QML(Qt Meta-object Language,Qt元对象描述语言)。QML乍看起来有点像json,但是核心思想却是模仿web页面。没错,在QML文件中允许搭配Javascript代码,就可以辅助实现丰富的UI交互逻辑。

如果你以往习惯QWidget开发,那么Qt Quick真的非常值得上手试试。

好了,口水吐多了招人厌,下面直入庐山一窥真面目!

手剥一个简单的功能程序开发栗子

在Qt开发过程中,Qt官方IDE(Qt Creator)提供了好几种工程构建工具,比如简单易懂的qmake,火上天的cmake,还有貌似没人听说过的Qbs。而目前Qt主推的构建方式就是cmake,下面要讲的例子也是用cmake。

1.开发环境配置

Win10
Qt 6.2.4
Qt Creator 8.0.1
Mingw 11.2.0 64bit
Cmake 3.23.2

这里选用的Qt版本是写作时最新的LTS版本,LTS意思就是官方长期支持更新。比如说,一两年内还会发布一下补丁和安全更新,至于新功能特性就别想了。

2.创建Qt Quick工程

先用Qt Creator创建一个简单的quick工程,工程构建描述的内容就保存在工程根目录的配置文件CMakeLists.txt中,如下:

cmake_minimum_required(VERSION 3.16)

project(instance VERSION 0.1 LANGUAGES CXX)

set(CMAKE_AUTOMOC ON)
#set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 6.2 COMPONENTS Quick REQUIRED)

file(GLOB_RECURSE SOURCE_FILES
    ./src/*.cpp
    ./src/*.h
)

qt_add_resources(SOURCE_FILES instance.qrc)

qt_add_executable(instance
    ${SOURCE_FILES}
)

set_target_properties(instance PROPERTIES
    MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
    MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
    MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
    MACOSX_BUNDLE TRUE
    WIN32_EXECUTABLE TRUE
)

target_link_libraries(instance
    PRIVATE Qt6::Quick)

install(TARGETS instance
    BUNDLE DESTINATION .
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

cmake_minimum_required用于声明当前的配置文件适用于的cmake最低版本,同时为了防止使用过于低级的版本来构建当前工程,避免某些指令不支持或者不兼容。

project用于声明当前工程名称,和开发语言。CXX代表了C++。

CMAKE_AUTOMOC用于标记是否开启自动MOC。Qt不仅仅是开发库,它同时也对开发语言(比如C++)做了拓展,那么源码文件中就会多多少少会包含有通用编译器无法识别的部分内容。MOC就是用于对Qt的扩展内容进行转换的工具。

CMAKE_AUTORCC用于标记是否开启自动RCC。在Qt工程中会包含有被最终输出的执行程序所需要的资源内容,比如音视频,图片等等。那么为了高效调用这些资源,势必需要对原本的资源文件进行处理再保存到额外的二进制文件中,甚至内嵌到执行文件中。RCC就是对这些资源文件进行处理和再输出工具。

由于我的例子工程中需要用到MOC和RCC,所以CMAKE_AUTOMOCCMAKE_AUTORCC都打开。

CMAKE_AUTOUIC用于标记是否开启自动UIC。如果开发界面用的技术栈是QWidget,那么在Qt工程中就需要创建.ui文件并保存设计内容到其中,编译的时候也需要用UIC把.ui文件转换成.h文件。不过,这里用Quick技术栈开发界面,因此无需打开CMAKE_AUTOUIC,在最前面添加#表示注释掉该行语句(该行语句会被解析器忽略)。

find_package导入Qt的Quick模块。

由于本工程需要用到多个C++源文件,所以这里采用了递归引用文件的方式把特定文件夹下面的所有.cpp、.h等文件都囊括进来,需要辅以通配符*。所有被囊括的文件路径被追加到动态数组SOURCE_FILES中,方便后边引用。语法格式如下:

file(GLOB_RECURSE <variable> [FOLLOW_SYMLINKS]
     [LIST_DIRECTORIES true|false] [RELATIVE <path>]
     [<globbing-expressions>...])

格式里的variable实际使用SOURCE_FILES代替。

qt_add_resources的作用是调用RCC对资源文件(.qrc)编译成qrc_开头的源文件再输出,并且把输出的源码文件路径追加到动态数组SOURCE_FILES中。

当然,动态数组SOURCE_FILES这个名字可以按照需求自定义设定,这里取名为源文件。

qt_add_executable指明构建的目标是二进制文件instance,引用的源文件来自于动态数组SOURCE_FILES。

target_link_libraries用于指明构建时链接Qt6::Quick的相关库。

剩余的语句都是Qt Creator创建工程时自动添加的内容,这里略过。

然后看看我的工程目录结构在Qt Creator中的展示:

如果用VSCODE打开工程目录,可以看到:

3.使用元对象描述文件(QML)描述界面

使用Qt Creator自动创建的Quick应用,除了会自动生成配置文件CMakeLists.txt之外,还包含了main.cpp和main.qml文件,源码只实现了启动之后弹出一个窗口。

这里稍作修改,实现简单的文件选择,以及将选中的文件路径名显示出来。这个功能用QWidget技术栈来实现其实是很简单的啦,不过我们这里目的是演示Quick技术框架怎么用,所以下面来具体看看界面这块怎么玩:

1)主页面

// main.qml

import QtQuick

Window {
    width: 640
    height: 200
    visible: true
    title: qsTr("Tool V1.0.0")
    Viewer {
        anchors.fill: parent
    }
}

main.qml 这个文件是首页元对象的描述文件,一般QML引擎加载的第一个元对象所在的文件就命名为main.qml。不过,命名为main.qml不是硬性规定。

首先,可以看到这里通过import导入了模块QtQuick。同一行,后边还可以加上版本号。

Window是一种模块里预定义的类型,用于窗体描述。当然,类型也可以自定义,下面会说到。这里通过对类型Window的实例化来描述一个窗体对象。

类型后边的{}内部包含了类型实例化后的成员属性、函数、信号、信号处理句柄等,比如width、height、visible、title等都是预定义的属性,这些属性如字面意思比较简单易懂就不一一展开了,大伙要是有兴趣可以反馈给我,我再看看意见给大家细聊。

Viewer其实是自定义的类型,这里通过对类型Viewer的实例化来补充添加新的界面元素。Viewer对象内部的属性anchors.fill: parent描述的是对象在其父对象(Window)中把父对象填充满。QML一般通过anchors属性来锚定对象的位置。

上面这个对象里并没有定义或者引用到函数或者信号等。

2)自定义类型

下面来看看怎么自定义类型

// Viewer.qml

import QtQuick 2.15
import QtQuick.Controls 6.2
import Qt.labs.platform 1.1

Item {
    function log(...msg) {
        let msgs = "";
        msg.forEach((item) => {
                        if (msgs.length != 0) {
                            msgs += " ";
                        }
                        msgs += item;
                    });
        console.log(msgs);
    }

    FileDialog {
        id: fileDialog
        objectName: "fileDialog"
        currentFile: selectedFileTextArea.text

        onFileChanged: {
            log(objectName + ".file =", file.toString().slice(8));
            fileMgrInstance.run(file.toString().slice(8));
        }
    }

    Label {
        id: fileLable
        x: 292
        y: 26
        text: qsTr("文件:")
        verticalAlignment: Text.AlignVCenter
        font.pointSize: 14
    }

    TextField {
        id: selectedFileTextArea
        x: 70
        y: 70
        width: 500
        objectName: "selectedFileTextArea"
        text: fileDialog.file.toString().slice(8)
        font.pointSize: 12
        placeholderText: qsTr("选择文件")
    }

    Button {
        id: selectFileButton
        x: 268
        y: 124
        width: 105
        height: 54
        text: qsTr("选择")
        font.pointSize: 10

        onClicked: {
            log(text, "clicked");
            fileDialog.open();
        }
    }
}

Item是类库QtQuick的预定义组件类型,描述的是一个基础可视组件,quick中所有的可视组件都继承于它。

一般在QML中自定义类型都会使用基础的类型Item,然后在其基础上定制内部属性、函数、信号、信号处理句柄等。

Item的继承链是这样的:

Item -> QtObject -> QObject

看到这里,可以猜测一下,其实所有的Quick预定义组件都是继承于QObject,和QWidget里提供的类库太相似了。

function log(...msg)定义了函数log,function是关键词,log是函数名,后边小括号里的...表示参数不定,这样子在调用log时就可以不限制输入的参数个数了。

要注意的是,QML内部的函数使用的语法是ECMAScript,也就是我们常常听到的JavaScript。

FileDialog是类库Qt.labs.platform的预定义组件类型,描述的是一个文件选择窗口。属性id,继承于QObject,用于标记唯一的对象,也就是说所有对象的id都不能重复,无论对象是否处于同一个QML文件。objectName描述的属性可用于对对象树中的对象进行查找。currentFile描述了当前选中的文件名(包括路径),在确定最后选中的文件之前,此属性也会跟随选择而改变。onFileChanged描述了当属性file值改变时,自动产生的信号的处理句柄(handle),用{}限定处理范围。log是上面定义的函数调用,输入两个参数。fileMgrInstance是C++源文件暴露给QML引擎的特定对象id,通过该id可以调用C++中的相应对象的方法属性(代码中调用了run方法,方法的详情定义看下文)。

C++和QML源文件之间的对象相互调用,会有后续的文章专门介绍,这里不再细聊,敬请关注。

Label是类库QtQuick.Controls的预定义组件类型,描述的是一个文本标签。x、y描述的是坐标。text描述的是显示文本。qsTr()用于标识文本可被翻译,类似Qt C++里的tr()。verticalAlignment描述的是垂直排列方式,这里的属性值标识垂直中间排列。font.pointSize描述字体的大小。

TextField是类库QtQuick.Controls的预定义组件类型,描述的是一个单行文本编辑窗。width是几何宽度。placeholderText描述的是占位符。

Button是类库QtQuick.Controls的预定义组件类型,描述的是一个可点击的按键。height描述的是几何高度。onClicked描述了当信号clicked发生时,该信号的处理句柄(handle),用{}限定处理范围,这里调用了上面的文件选择窗的打开函数。

信号的处理句柄(handle)中,在on后边书写时信号的首字母需要大写。

3)预览界面

什么?代码没写完就可以预览界面了?

是的,QML文件支持用工具预览,非常方便于UI设计过程中的调试。

打开预览的方式是调用qmlscene或者用Qt Design Studio,如下图用的是qmlscene。

看看Viewer.qml页面预览的实际效果。

4.使用C++代码实现逻辑处理

Qt Quick使用QML的目的是为了简化界面的设计开发,而软件除了界面的互动之外还有大量的后台逻辑处理功能也需要实现,针对这块业务,Qt其实还是推荐使用C++,正所谓术业有专攻,毕竟C++对性能的利用还是有两把刷子的。废话不多说,马上看下文。。。

既然如此,那么就来看看负责逻辑处理功能的C++代码部分。不过,这里假设各位看官已经熟悉C++的各项业务技能,所以下面只针对和QML对象的互动来简单介绍一下。

后续也会有更加详细的专题文章介绍这部分,敬请留意哈!

1)QML对象的加载和C++对象传递

QML对象的创建和展现是通过QML引擎来加载的,一般每个程序会由单个引擎对象负责管理。不过,QML引擎对象不是直接管理QML对象,而是通过管理上下文(context)对象来分别管理QML对象。所以在C++里边如果需要往QML对象传递信息也是直接传给对应的上下文对象,然后再在QML对象中通过传入对象时指定的id名调用对应的方法属性。

// main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "./FileMgr/FileMgr.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    const QUrl url(u"qrc:/src/QML/main.qml"_qs);
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        qInfo("%s start\n", QCoreApplication::applicationName().toLatin1().data());

        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);

    FileMgr fileMgr;
    engine.rootContext()->setContextProperty(QStringLiteral("fileMgrInstance"), &fileMgr);
    engine.load(url);

    return app.exec();
}

上面的main.cpp文件代码中,把对象fileMgr传入引擎的根上下文中,并设定id为fileMgrInstance。传入根上下文,意味着引擎加载的所有QML对象都可以通过id=fileMgrInstance访问fileMgr对象内容。这里要注意,fileMgr对象是在C++源码中实例化了的。

把C++对象暴露给QML对象的方法,除了上面这种通过上下文的方式外,还有一种是通过直接往元对象系统(Meta-Object System)注册类型的方式,这种方式也是最根本的方式,因为Qt Quick框架的底层实现原理就依赖于元对象系统。

使用的接口原型:

template <typename T> int qmlRegisterType(const char *uri, int versionMajor, int versionMinor, const char *qmlName)

如果通过这种注册的方式实现,上面的栗子可以掰成这样:

 qmlRegisterType<FileMgr>("com.englyf.qmlcomponents", 1, 0, "FileMgrItem");

根据上面的定义,com.englyf.qmlcomponents是命名空间,1.0是命名空间的版本号,FileMgrItem是QML中的类型名。

然后在QML文件内,导入并实例化这个类:

 import com.englyf.qmlcomponents 1.0

 FileMgrItem {
     // ...
 }

要强调的是,通过注册的方式,类型的实例化会放在QML里边做,而C++源码就不需要再对类FileMgr作实例化了

2)C++类型的定义

既然Qt Quick依赖于元对象系统,那么对C++类型的定义就有必然的要求了。

C++类型需要继承于QObject,并且类开头应该声明宏Q_OBJECT,这样才可以使用元对象系统提供的服务,包括信号槽机制等等。

需要被QML对象调用的方法应该添加修饰Q_INVOKABLE。这个修饰符表明该方法可被元对象系统调用。同时,该方法的参数类型和返回类型,都推荐使用类型QVariant。

如有开放给QML对象的可访问属性,那么也需要对属性声明为Q_PROPERTY。这里暂不举例,可关注后续的专题文章。

// FileMgr.h

#ifndef FILEMGR_H
#define FILEMGR_H

#include <QObject>
#include <QVariant>

class FileMgr : public QObject
{
    Q_OBJECT
public:
    explicit FileMgr(QObject *parent = nullptr);

    Q_INVOKABLE QVariant run(QVariant file);
};

#endif // FILEMGR_H

// FileMgr.cpp

#include "FileMgr.h"

FileMgr::FileMgr(QObject *parent)
    : QObject{parent}
{}

QVariant FileMgr::run(QVariant file)
{
    QString fileStr = file.toString();
    qDebug("C++ get file:%s selected", fileStr.toStdString().data());

    return 0;
}

自动化部署

这部分讲点高级的内容,以往看到网上的教程都是教初学者部署的时候,进入exe生成的目录,然后手动调用windeployqt执行部署。这个程序是Qt自带的,会自动把所有依赖的动态库拷贝过来存放在指定目录下。

这里就介绍一下怎么在Qt Quick软件工程编译结束时自动部署所有依赖项。

首先,debug开发模式下是不需要部署软件的,那么我们就先切换到release模式下。

然后,在Build的步骤下,Build步骤之后新添加一个Custom Process Step的步骤。

我把配置都拷过来:

Command:            windeployqt
Arguments:          --qmldir %{ActiveProject:NativePath}\src\QML\ %{ActiveProject:RunConfig:Executable:NativeFilePath}
Working directory:  %{Qt:QT_INSTALL_BINS}

由于Qt Quick工程涉及到QML文件,所以这里需要带上选项--qmldir,这个选项后边紧跟着参数值是代码工程中存放自定义的QML文档的根目录。

%{ActiveProject:NativePath}代表着当前工程的主目录的本地化路径。

%{ActiveProject:RunConfig:Executable:NativeFilePath}代表着当前工程的exe文件输出目录的本地化路径。

Working directory项意思是Command命令的工作目录,这里填上%{Qt:QT_INSTALL_BINS},代表Qt安装目录下的bin目录。

按照上面的介绍过程配置完整,以后如果需要部署输出,只需要切换到release模式下,然后点击编译,等编译完成就会自动进入部署流程,整个过程就是这么舒心。

生活简单才是美好,部署也不例外!


到最后,一起来看看跑起来的程序:

有关一文入门Qt Quick的更多相关文章

  1. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  2. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  3. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  4. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

  5. 区块链入门教程(6)--WeBASE-Front节点前置服务安装 - 2

    文章目录1.任务背景2.任务目标3.相关知识点4.任务实操4.1安装配置JDK4.2启动FISCOBCOS4.3下载解压WeBASE-Front4.4拷贝sdk证书文件4.5启动节点4.6访问节点4.7检查运行状态5.任务总结1.任务背景FISCOBCOS其实是有控制台管理工具,用来对区块链系统进行各种管理操作。但是对于初学者来说,还是可视化界面更友好,本节就来介绍WeBASE管理平台,这是一款微众银行开源的自研区块链中间件平台,可以降低区块链使用的门槛,大幅提高区块链应用的开发效率。微众银行是腾讯牵头设立的民营银行,在国内民营银行里还是比较出名的。微众银行参与FISCOBCOS生态建设,一定

  6. Tcl脚本入门笔记详解(一) - 2

    TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是

  7. Simulink方法总结和避坑指南(一)——Simulink入门与基本调试方法 - 2

    文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景  最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。  在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记

  8. 一文解决关于VLAN所有的疑惑 - 2

    一文解决关于VLAN所有的疑惑VLAN基本概念为什么需要VLAN?怎么在交换机上划分VLAN,VLAN的工作原理有了子网,已经隔离了广播,还需要VLAN干啥?只进行子网划分,不进行VLAN划分VLAN划分与子网划分附加VLAN信息的方法VLAN划分交换机的端口类型(Access和Trunk)一、访问链接二、汇聚链接汇聚链接VLAN间通信为什么要进行VLAN间通信?路由器实现VLAN间通信路由器和交换机的连接方式通信细节三层交换机实现VLAN间通信加速VLAN间通信三层交换机与路由器三层交换机路由器路由器和交换机配合构建LAN的实例使用VLAN设计局域网的特点VLAN增加网络的灵活性不使用VLA

  9. ESP32学习入门:WiFi连接网络 - 2

    目录一、ESP32简单介绍二、ESP32Wi-Fi模块介绍三、ESP32Wi-Fi编程模型四、ESP32Wi-Fi事件处理流程 五、ESP32Wi-Fi开发环境六、ESP32Wi-Fi具体代码七、ESP32Wi-Fi代码解读6.1主程序app_main7.2自定义代码wifi_init_sta()八、ESP32Wi-Fi连接验证8.1测试方法8.2服务器模拟工具sscom58.3测试代码8.4测试结果前言为了开发一款亚马逊物联网产品,开始入手ESP32模块。为了能够记录自己的学习过程,特记录如下操作过程。一、ESP32简单介绍ESP32是一套Wi-Fi(2.4GHz)和蓝牙(4.2)双模解决方

  10. 一文让你彻底掌握操作符(超详细教程) - 2

    ✅作者简介:大家好,我是小杨📃个人主页:「小杨」的csdn博客🔥系列专栏:小杨带你玩转C语言【初阶】🐳希望大家多多支持🥰一起进步呀!大家好呀!我是小杨。小杨花几天的时间将C语言中的操作符这部分知识做了一个大总结,在方便自己复习的同时也能够帮助到大家。通篇字数在一万字左右,可以算作是非常详细了,一文就可以带领大家彻底掌握操作符这部分内容,文章很长建议先收藏再看,防止下次想看就找不到啦。文章目录✍1,算术操作符✍2,移位操作符    🔍2.1,左移操作符    🔍2.2,右移操作符       ✨2.2.1,算术移位       ✨2.2.2,逻辑移位✍3,位操作符    🔍3.1,按位与&   

随机推荐