GObject学习教程---第二章:模拟类的数据封装形式

2023-05-16

索引:https://blog.csdn.net/knowledgebao/article/details/84633798

本文是学习学习他人的博客的心得(具体详见“楼主见解”),如果源网站可访问的话,建议直接访问源网站:

楼主见解:

主要介绍如何使用GObject定义自己的类,具体详见代码注释,此类的名称叫PMDList,函数前缀是pm_dist,类型是PM_TYPE_DLIST

主要分3步:

第一:确定子类类型PM_TYPE_DLIST,函数前缀pm_dist,类名称PMDList。

第二:根据上一步确定的类型、前缀、类名,定义二个结构体(实例结构体PMDList+类结构体PMDListClass),一个函数(类型函数pm_dlist_get_type)

第三:初始化二个结构体(pm_dlist_init+pm_dlist_class_init)以及一个函数(G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT))。

1:.h实现:

#ifndef PM_DLIST_H
#define PM_DLIST_H
 
/*必须包含的基类头文件*/
#include <glib-object.h>
 
/*定义子类类型,其中pm_dist字段是用户可以修改的内容,表示这个类的用途,后续所有的函数都必须以pm_dist开头,pm_dlist_get_type ()函数在下边定义*/
#define PM_TYPE_DLIST (pm_dlist_get_type ())
 
/*普通结构体,作为实例结构体的成员*/
typedef struct _PMDListNode PMDListNode;
struct  _PMDListNode {
        PMDListNode *prev;
        PMDListNode *next;
        void *data;
};
 
/*实例结构体,类似于C++中实例化时,每个实例独有的内容,比如成员函数,成员变量等*/
typedef struct _PMDList PMDList;
struct  _PMDList {
        GObject parent_instance;
        PMDListNode *head;
        PMDListNode *tail;
};
 
/*类结构体,类似于C++中,属于类的部分,比如:static成员函数和static成员变量等*/
typedef struct _PMDListClass PMDListClass;
struct _PMDListClass {
        GObjectClass parent_class;
};
 
/*privae,定义获取类型函数,供PM_TYPE_DLIST调用,不建议直接调用*/
GType pm_dlist_get_type (void);
 
#endif

2:.c实现:

#include "pm-dlist.h"
 
/*此宏有三个功能:
1,函数GType pm_dlist_get_type (void)的实现,GObject帮我们具体实现
2,注册函数前缀pm_dlist到GObject
3,注册实例结构体名称PMDList到GObject
通过此宏的调用,GObject知道了我们的三个信息:类型,类名称,函数前缀
*/
G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT);
 
/*实例结构体的初始化,类似于C++中的构造函数*/
static
void pm_dlist_init (PMDList *self)
{
        g_printf ("\t实例结构体初始化!\n");
 
        self->head = NULL;
        self->tail = NULL;
}

/*类结构体的初始化,类似于C++中的static变量的初始化,此函数只会在第一次类实例化的时候被调用,后续类实例化不在调用*/ 
static
void pm_dlist_class_init (PMDListClass *klass)
{
        g_printf ("类结构体初始化!\n");
}

源博客网址:http://garfileo.is-programmer.com/categories/6934/posts

 

使用 GObject 库模拟类的数据封装形式

事实上,有关 GObject 库的学习与使用,GObject 库参考手册提供了一份简短且过于晦涩的指南。如果你能够理解它,那么完全可以无视这篇以及后续的几篇文章。倘若没有明白那份指南,那么建议最好能克制一下,先不要急于去做文档 [1] 中所列举那些探索,谨记 Knuth 所说的,过早优化是诸恶之源。

这篇文档主要讲述如何使用 GObject 库来模拟面向对象程序设计的最基本的要素,即基于类的数据封装,所采用的具体示例是一个双向链表的设计。

从一个双向链表的数据结构开始

对于双向链表这种数据结构,即便是 C 语言的初学者也不难创建一个名为 double-list.h 的头文件并写出以下代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

/* file name: double-list.h */

 

#ifndef DOUBLE_LIST_H

#define DOUBLE_LIST_H

 

struct double_list_node {

        struct doule_list_node *prev;

        struct double_list_node *next;

        void *data;

};

 

struct double_list {

        struct double_list_node *head;

        struct double_list_node *tail;

};

 

#endif

较为熟悉 C 语言的人自然不屑于写出上面那种新手级别的代码,他们通常使用 typedef 关键字去定义数据类型,并且将 double_list 简写为 dlist,以避免重复的去写 "struct double_list_xxxxx" 这样的代码,此外还使用了 Pascal 命名惯例,即单词首字母大写[2],具体如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

/* file name: dlist.h(版本 2)*/

 

#ifndef DLIST_H

#define DLIST_H

 

typedef struct _DListNode DListNode;

struct  _DListNode {

        DListNode *prev;

        DListNode *next;

        void *data;

};

 

typedef struct _DList DList;

struct  _DList {

        DListNode *head;

        DListNode *tail;

};

 

#endif

现在,代码看上去稍微专业了一点。

但是,由于 C 语言没有为数据类型提供自定义命名空间的功能,程序中所有的数据类型(包括函数)均处于同一个命名空间,这样数据类型便存在因为同名而撞车的可能性。为了避免这一问题,更专业一点的程序员会为数据类型名称添加一些前缀,并且通常会选择项目名称的缩写。我们可以为这种命名方式取一个名字,叫做 PT 格式,P 是项目名称缩写,T 是数据类型的名称。例如,对于一个多面体建模(Polyhedron Modeling)的项目,如果要为这个项目定义一个双向链表的数据类型,通常是先将 dlist.h 文件名修改为 pm-dlist.h,将其内容改为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

/* file name: pm-dlist.h*/

 

#ifndef PM_DLIST_H

#define PM_DLIST_H

 

typedef struct _PMDListNode PMDListNode;

struct  _PMDListNode {

        PMDListNode *prev;

        PMDListNode *next;

        void *data;

};

 

typedef struct _PMDList PMDList;

struct  _PMDList {

        PMDListNode *head;

        PMDListNode *tail;

};

 

#endif

在以上一波三折的过程中,我们所做的工作就是仅仅是定义了两个结构体而已,一个是双向链表结点的结构体,另一个是双向链表的结构体,并且这两个结构体中分别包含了一组指针成员。这样的工作,用面向对象编程方法中的术语,叫做”数据封装“。借用《C++ Primer》中的定义,所谓数据封装,就是一种将低层次的元素组合起来,形成新的、高层次实体的技术。对于上述代码而言,指针类型的变量属于低层次的元素,而它们所构成的结构体,则是高层次的实体。在面向对象程序设计中,是数据封装形式中的一等公民。

接下来,我们更进一步,使用 GObject 库模拟的数据封装形式,如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

/* file name: pm-dlist.h*/

 

#ifndef PM_DLIST_H

#define PM_DLIST_H

 

#include <glib-object.h>

 

typedef struct _PMDListNode PMDListNode;

struct  _PMDListNode {

        PMDListNode *prev;

        PMDListNode *next;

        void *data;

};

 

typedef struct _PMDList PMDList;

struct  _PMDList {

        GObject parent_instance;

        PMDListNode *head;

        PMDListNode *tail;

};

 

typedef struct _PMDListClass PMDListClass;

struct _PMDListClass {

        GObjectClass parent_class;

};

 

#endif

上述代码与 dlist.h 版本 2 中的代码相比,除去空行,多出 6 行代码,它们的作用是实现一个双向链表类。也许你会感觉这样很滑稽,特别当你特别熟悉 C++、Java、C# 之类的语言之时。

但是,上述代码的确构成了一个。在 GObject 世界里,是两个结构体的组合,一个是实例结构体,另一个是类结构体。例如,PMDList 是实例结构体,PMDListClass 是类结构体,它们合起来便可以称为 PMDList 类(此处的“PMDList 类”只是一个称谓,并非是指 PMDList 实例结构体。下文将要谈及的“GObject 类”的理解与此类似)。

也许你会注意到,PMDList 类实例结构体的第一个成员是 GObject 结构体,PMDList 类类结构体的第一个成员是 GObjectClass 结构体。其实,GObject 结构体与 GObjectClass 结构体分别是 GObject 类实例结构体类结构体,当它们分别作为 PMDList 类实例结构体类结构体第一个成员时,这意味着 PMDList 类继承自 GObject 类

也许你并不明白 GObject 为什么要将类拆解为实例结构体与类结构体,也不明白为什么将某个类的实例结构体与类结构体,分别置于另一个类的实例结构体与类结构体的成员之首便可实现类的继承,这些其实并不重要,至少是刚接触 GObject 时它们并不重要,应当像对待一个数学公式那样将它们记住。以后每当需要使用 C 语言来模拟类的封装形式时,只需要构建一个基类是 GObject 类的类即可。就像我们初学 C++ 那样,对于下面的代码,

1

2

3

4

5

6

7

8

9

10

11

12

class PMDListNode {

public:

        PMDListNode *prev;

        PMDListNode *next;

        void *data;

}

 

class PMDList : public GObject {

public:

        PMDListNode *head;

        PMDListNode *tail;

};

也许除了 C++ 编译器的开发者之外,没人会对为什么使用“class”关键字便可以将一个数据结构变成类,为什么使用一个冒号便可以实现 DList 类继承自 GObject 类,为什么使用 public 便可以将 DList 类的 head 与 tail 属性对外开放之类的问题感兴趣。

继承 GObject 类的好处

为什么 DList 类一定要将 GObject 类作为父类?主要是因为 GObject 类具有以下功能:

  • 基于引用计数的内存管理
  • 对象的构造函数与析构函数
  • 可设置对象属性的 set/get 函数
  • 易于使用的信号机制

现在即使并不明白上述功能的意义也无关紧要,这给予了我们对其各个击破的机会。

虽然,我们尚且不是很清楚继承 GObject 类的好处,但是不继承 GObject 类的坏处是显而易见的。在 cloverprince 所写的 11 篇文章[3]中,从第 2 篇到第 5 篇均展示了不使用 GObject 的坏处,虽然它们对理解 GObject 类的实现原理有很大帮助,但是对于 GObject 库的使用并无太多助益。

概念游戏

我承认,我到现在也不是很明白面向对象程序设计中的”类“、”对象“和”实例“这三者的关系。不过看到还有人同我一样没有搞明白[4],心里便略微有些安慰,甚至觉得 C 语言不内建支持面向对象程序设计是一件值得庆幸的事情。暂时就按照文档[4]那样理解吧,对于上面所设计的 PMDList 类,可以用下面的代码模拟类的实例化与对象的实例化。

1

2

PMDList *list; /* 类的实例化 */

list = g_object_new (PM_TYPE_DLIST, NULL); /* 对象的实例化 */

也就是说,对于 PMDList 类,它的实例是一个对象,例如上述代码中的 list,而对象的实例化则是让这个对象成为计算机内存中的实体。

也许,对象的实例化比较令人费解,幸好,C 语言的表示会更加直观,例如:

1

2

3

4

5

6

PMDList *dlist; /* 类的实例化,产生对象 */

 

dlist = g_object_new (PM_TYPE_DLIST, NULL); /* 创建对象的一个实例 */

g_object_unref (dlist); /* 销毁对象的一个实例 */

 

dlist = g_object_new (PM_TYPE_DLIST, NULL); /* 再创建对象的一个实例 */

这里需要暂停,请回溯到上一节所说的让一个类继承 GObject 类的好处的前两条,即 GObject 类提供的“基于引用计数的内存管理”与“对象的构造函数与析构函数”,此时体现于上例中的 g_object_new 与 g_object_unref 函数。

g_object_new 用于构建对象的实例,虽然其内部实现非常繁琐和复杂,但这是 GObject 库的开发者所要考虑的事情。我们只需要知道 g_object_new 可以为对象的实例分配内存与初始化,并且将实例的引用计数设为 1。

g_object_unref 用于将对象的实例的引用计数减 1,并检测对象的实例的引用计数是否为 0,若为 0,那么便释放对象的实例的存储空间。

现在,再回到上述代码的真正要表述的概念,那就是:类的实例是对象,每个对象皆有可能存在多个实例

下面,继续概念游戏,看下面的代码:

1

PMDList *list = g_object_new (PM_TYPE_DLIST, NULL);

这是类的实例化还是对象的实例化?自然是类的实例化的实例化,感觉有点恶心,事实上这只不过是为某个数据类型动态分配了内存空间,然后用指针指向它!我们最好是不再对“对象”和“实例”进行区分,对象是实例,实例也是对象,只需要知道它们所指代的事物在同一时刻是相同的,除非有了量子计算机。

PM_TYPE_DLIST 引发的血案

上一节所使用的 g_object_new 函数的参数是 PM_TYPE_DLIST 和 NULL。对于 NULL,虽然我们不知道它的真实意义,但是至少知道它表示一个空指针,而那个 PM_TYPE_DLIST 是什么?

PM_TYPE_DLIST 是一个宏,需要在 pm-dlist.h 头文件中进行定义,于是最新版的 pm-dlist.h 内容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

#ifndef PM_DLIST_H

#define PM_DLIST_H

 

#include <glib-object.h>

 

#define PM_TYPE_DLIST (pm_dlist_get_type ())

 

typedef struct _PMDListNode PMDListNode;

struct  _PMDListNode {

        PMDListNode *prev;

        PMDListNode *next;

        void *data;

};

 

typedef struct _PMDList PMDList;

struct  _PMDList {

        GObject parent_instance;

        PMDListNode *head;

        PMDListNode *tail;

};

 

typedef struct _PMDListClass PMDListClass;

struct _PMDListClass {

        GObjectClass parent_class;

};

 

GType pm_dlist_get_type (void);

 

#endif

嗯,与之前的 pm-dlist.h 相比,现在又多了两行代码:

1

2

#define PM_TYPE_DLIST (pm_dlist_get_type ())

GType pm_dlist_get_type (void);

显然,PM_TYPE_DLIST 这个宏是用来替代函数 pm_dlist_get_type 的,该函数的返回值是 GType 类型。

我们将 PM_TYPE_DLIST 宏作为 g_object_new 函数第一个参数,这就意味着向 g_object_new 函数传递了一个看上去像是在获取数据类型的函数。不需要猜测,也不需要去阅读 g_object_new 函数的源代码,g_object_new 之所以能够为我们进行对象的实例化,那么它必然要知道对象对应的类的数据结构,pm_dlist_get_type 函数的作用就是告诉它有关 PMDList 类的具体结构。

现在既然知道了 PM_TYPE_DLIST 及其代表的函数 pm_dlist_get_type,那么 pm_dlist_get_type 具体该如何实现?很简单,只需要再建立一个 pm-dlist.c 文件,内容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

#include "pm-dlist.h"

 

G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT);

 

static

void pm_dlist_init (PMDList *self)

{

        g_printf ("\t实例结构体初始化!\n");

 

        self->head = NULL;

        self->tail = NULL;

}

 

static

void pm_dlist_class_init (PMDListClass *klass)

{

        g_printf ("类结构体初始化!\n");

}

这样,在源代码文件的层次上,pm-dlist.h 文件存放这 PMDList 类的声明,pm-dlist.c 文件存放的是 PMDList 类的具体实现。

在上述的 pm-dlist.c 中,我们并没有看到 pm_dlist_get_type 函数的具体实现,这是因为 GObject 库所提供的 G_DEFINE_TYPE 宏可以为我们生成 pm_dlist_get_type 函数的实现代码。

G_DEFINE_TYPE 宏,顾名思义,它可以帮助我们最终实现类型的定义。对于上例,

1

G_DEFINE_TYPE (PMDList, pm_dlist, G_TYPE_OBJECT);

G_DEFINE_TYPE 可以让 GObject 库的数据类型系统能够识别我们所定义的 PMDList 类类型,它接受三个参数,第一个参数是类名,即 PMDList;第二个参数则是类的成员函数(面向对象术语称之为”方法“或”行为“)名称的前缀,例如 pm_dlist_get_type 函数即为 PMDList 类的一个成员函数,"pm_dlist" 是它的前缀;第三个参数则指明 PMDList 类类型的父类型为 G_TYPE_OBJECT……嗯,这又是一个该死的宏!

也许你会注意到,PMDList 类类型的父类型 G_TYPE_OBJECT 与前面所定义的宏 PM_TYPE_DLIST 非常相像。的确是这样,G_TYPE_OBJECT 指代 g_object_get_type 函数。

为了便于描述,我们可以将 PMDList 类和 GObject 类这种形式的类型统称为 PT 类类型,将 pm_dlist_get_type 和 g_object_get_type 这种形式的函数统称为 p_t_get_type 函数,并将 PM_TYPE_DLIST 和 G_TYPE_OBJECT 这样的宏统称为 P_TYPE_T 宏。当然,这种格式的函数名与宏名,只是一种约定。

若想让 GObject 库能够识别你所定义的数据类型,那么必须要提供一个 p_t_get_type 这样的函数。虽然你不见得非要使用 p_t_get_type 这样的函数命名形式,但是必须提供一个具备同样功能的函数。p_t_get_type 函数的作用是向 GObject 库所提供的类型管理系统提供要注册的 PT 类类型的相关信息,其中包含 PT 类类型的实例结构体初始化函数 p_t_init 与类结构体初始化函数 p_t_class_init,例如上例中的 pm_list_init 与 pm_list_class_init。

因为 p_t_get_type 函数是 g_object_new 函数的参数,当我们首次调用 g_object_new 函数进行对象实例化时,p_t_get_type 函数便会被 g_object_new 函数调用,从而引发 GObject 库的类型管理系统去接受 PT 类类型(例如 PMDList 类型)的申请并为其分配一个类型标识码作为 p_t_get_type 函数的返回值。当 g_object_new 函数从 p_t_get_type 函数那里获取 PT 类类型标识码之后,便可以进行对象实例的内存分配及属性的初始化。

将一切放在一起

这篇文章居然出乎意料的长,原本没打算涉及任何细节,结果有些忍不住。不过,倘若你顺利的阅读到这里,便已经掌握了如何使用 GObject 库在 C 语言程序中模拟面向对象程序设计中基于类的数据封装,并且我们完成了一个双向链表类 PMDList 的数据封装。

为了验证 PMDList 类是否可用,可以再建立一份 main.c 文件进行测试,内容如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

#include "pm-dlist.h"

 

int

main (void)

{

        /* GObject 库的类型管理系统的初始化 */

        g_type_init ();

 

        int i;

        PMDList *list;

 

        /* 进行三次对象实例化 */

        for (i = 0; i < 3; i++){

                list = g_object_new (PM_TYPE_DLIST, NULL);

                g_object_unref (list);

        }

 

        /* 检查实例是否为 GObject 对象 */

        list = g_object_new (PM_TYPE_DLIST, NULL);

        if (G_IS_OBJECT (list))

                g_printf ("\t这个实例是一个 GObject 对象!\n");

         

        return 0;

}

编译上述测试程序的命令为:

1

$ gcc $(pkg-config --cflags --libs gobject-2.0) pmd-dlist.c main.c -o test

测试程序的运行结果如下:

1

2

3

4

5

6

7

$ ./test

类结构体初始化!

    实例结构体初始化!

    实例结构体初始化!

    实例结构体初始化!

    实例结构体初始化!

    这个实例是一个 GObject 对象!

从输出结果可以看出,PMDList 类的类结构体初始化函数只被调用了一次,而实例结构体的初始化函数的调用次数等于对象实例化的次数。这意味着,所有实例共享的数据,可保存在类结构体中,而所有对象私有的数据,则保存在实例结构体中

上述的 main 函数中,在使用 GObject 库的任何功能之前,必须先调用 g_type_init 函数初始化 GObject 库的类型管理系统,否则程序可能会出错。

main 函数中还使用了 G_IS_OBJECT 宏,来检测 list 对象是否为 G_TYPE_OBJECT 类型的对象:

1

G_IS_OBJECT (list)

因为 PMDList 类继承自 GObject 类,那么一个 PMDList 对象自然是一个 G_TYPE_OBJECT 类型的对象。

总结

也许我讲的很明白,也许我一点都没有讲明白。但是使用 GObject 库模拟基于类的数据封装,或者用专业术语来说,即 GObject 类类型的子类化,念起来比较拗口,便干脆简称 GObject 子类化,其过程只需要以下四步:

  1. 在 .h 文件中包含 glib-object.h;
  2. 在 .h 文件中构建实例结构体与类结构体,并分别将 GObject 类的实例结构体与类结构体置于成员之首;
  3. 在 .h 文件中定义 P_TYPE_T 宏,并声明 p_t_get_type 函数;
  4. 在 .c 文件中调用 G_DEFINE_TYPE 宏产生类型注册代码。

也许让我们感到晕眩的,是那些命名约定。但是,从工程的角度来说,GObject 库所使用的命名风格是合理的,值得 C 程序员借鉴。

~ End ~


参考文档

[1]  有关 GObject 的几份文档

[2]  Pascal 命名法

[3]  cloverprince 的 GObject 学习笔记

[4]  对象与实例的区别

转载时,希望不要链接文中图片,另外请保留本文原始出处:http://garfileo.is-programmer.com

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GObject学习教程---第二章:模拟类的数据封装形式 的相关文章

  • 解决java poi之SXSSFWorkbook导出大数据量时自适应列宽效果异常问题

    场景 xff1a 使用SXSSFWorkbook导出大数据量 百万行 excel报表 xff0c 表格样式统一使用自适应列宽时 xff0c 发现导出表头行格式异常 xff0c 异常情况如下图所示 xff1a 分析 xff1a 从导出效果看
  • Win11基于WSL2安装CUDA、cuDNN和TensorRT(2023-03-01)

    文章目录 2023 03 06 更新2023 03 05 更新前言TensorRT介绍环境配置CUDA Driver检查安装 安装CUDA安装nvcc安装cuDNN安装验证 安装TensorRT安装验证 2023 03 06 更新 如果有小
  • python OCR Tesseract 训练

    Ps xff1a Tesseract识别英文和字母效果好 中文的话 xff0c 虽然有训练数据也可以识别 xff0c 但是效果不是很好 Tesseract的安装和使用 xff1a 1 首先用 pip 下载包 pip install tess
  • ffmpeg实战教程(十)ffmpeg/camera实现最近很火的视频壁纸,相机壁纸

    本篇实现一个有意思的玩意儿 xff0c 视频壁纸 xff0c 相机壁纸 这玩意好像现在还都是国外版本 xff0c 哈哈 先上图 xff1a 视频壁纸 相机壁纸 1 动态壁纸制作的知识 xff1a 每一个动态壁纸都继承自WallpaperSe
  • C/C++ 连接两个字符串

    一 xff1a C风格字符串连接 include lt iostream gt using namespace std int main const char str 61 34 hello 34 const char str2 61 34
  • 返回urls跳转后的页面地址

    string urls 61 34 http xxx xxx com SoftDown aspx SoftID 61 275781 amp fdid 61 119418725f1d4e20 34 Response Redirect urls
  • 阶乘数码(基础算法-高精度)

    P1591 阶乘数码 洛谷 计算机科学教育新生态 luogu com cn include lt bits stdc 43 43 h gt include lt iostream gt include lt unordered map gt
  • MPICH的安装与使用

    1 安装MPICH之前 xff0c 首先要在centos6 5上安装c编译器 xff0c 使用指令安装如下 xff1a yum install make 安装make程序 yum install gcc 安装GCC编译器 xff08 支持C
  • 使用cmake生成可执行文件以及开发中常用到的一些指令说明

    span class token macro property span class token directive keyword include span span class token string lt iostream gt s
  • Ubuntu18.04 CUDA10 cudaGetDeviceCount returned 35

    报错现象 我的环境是Ubuntu18 04 安装了CUDA10 运行deviceQuery报错如下 xff1a deviceQuery Starting CUDA Device Query Runtime API version CUDAR
  • Wampserver最全安装教程(一定全绿图标)

    Wampserver最全安装教程 xff08 一定全绿图标 xff09 通过以下步骤进行一定可以全绿 xff0c 一定要按照步骤来哦 xff1a 打开http wampserver aviatechno net 滚到最下面 xff0c 下载
  • WSL安装及配置

    WSL xff0c 全称Windows Subsystem for Linux xff0c 是微软为Windows 10 11集成的功能 xff0c 可以在Windows中不用虚拟机使用Linux 使用WSL首先要开启Windows功能 x
  • “invalid character ‘\x1f‘ looking for beginning of value“ 报错解决方法

    目录 问题背景 分析方法 问题原因 问题背景 这个问题来源于我在开启kubectl proxy代理的时候 xff0c 通过kubectl apply f xxx yaml向代理提交一个configMap对象 xff0c 返回了 34 Err
  • 08-1 UIControl 及其子类 UISegmentedControl 、UISlider

    1 UIControl UIControl是所有控制控件 xff08 比如UIButton UISlider UISegmentedControl等 xff09 的基类 只要跟控制有关的控件都是继承于该类 UISlider是可以响应滑动事件
  • android Settings.Secure的使用

    在Android中 xff0c 许多的系统属性都在settings应用当中进行设置的 xff0c 比如wifi 蓝牙状态 xff0c 当前本机语言 xff0c 屏幕亮度等等一些相关的系统属性值 这些数据主要是存储在数据库中 xff0c 对应
  • 关于VM一启动虚拟机电脑就重启或蓝屏的几个解决方法

    最近在刚开始学习Linux在使用VMware创建新的虚拟机时只要一点启动虚拟机电脑就直接重启了 xff0c 最开始以为是vm版本或者是Linux镜像的原因来来回回换了好几个vm和Linux xff0c 电脑重启了二三十次都没成功启动虚拟机
  • Golang + Qt5 桌面开发终极解决方案

    Golang 43 Qt5 桌面开发终极解决方案 首先要安装Qt和Golang 一 安装前准备 1 下载Go1 4版本的压缩包版本 xff0c 解压至C盘User目录下 2 安装MinGW 并配置相关环境变量 参考链接 xff1a MinG
  • Oracle snapper ASH监控工具

    Oracle snapper ASH监控工具 snapper工具是由国外技术人员 xff0c 将基于Oracle ash技术原理用来监控数据库会话的负载情况 比较适合小范围时间监控 xff0c 可以生成多个快照 xff0c 例如1小时内 x
  • Matlab之数据筛选

    Matlab功能强大 xff0c 这里介绍一些数据筛选方法 xff0c 至少让其达到Excel的数据筛选程度 一 从多维数组中取某些行或列组合为新数组 示例如下 xff1a 取某些列组成新数组 newdata span class toke
  • kurento-room的搭建教程,绝对可行

    目前网上参考的kurento room的搭建教程 xff0c 比如https blog csdn net u010602143 article details 106670864 已经跑不起了 我估计原来也跑不起 原因很简单 xff0c k

随机推荐

  • Python 爬取携程所有机票

    打开携程网 xff0c 查询机票 xff0c 如广州到成都 这时网址为 xff1a http flights ctrip com booking CAN CTU day 1 html DDate1 61 2018 06 15 其中 xff0
  • Rust Web框架warp使用

    目录 简介快速开始Request和Response从path和body中获取参数从query中获取参数 设置状态码 静态文件 目录websocket重定向tls 简介 warp是一个超级便捷 可组合 速度极快的异步Web框架 目前最新版本为
  • CCNP路由实验之四 动态路由协议之EIGRP

    CCNP 路由实验之四 动态路由协议之 EIGRP 动态路由协议可以自动的发现远程网络 xff0c 只要网络拓扑结构发生了变化 xff0c 路由器就会相互交换路由信息 xff0c 不仅能够自动获知新增加的网络 xff0c 还可以在当前网络连
  • C++中typedef用法说明

    typedef声明提供了一种将标识符声明为类型别名的方法 xff0c 用于替换复杂的类型名 解释 在声明中使用typedef说明符时 xff0c 会指定这个声明是typedef声明 xff0c 而不是变量或函数声明 通常 xff0c typ
  • Ubuntu 服务配置(sysv-rc-conf)

    版权声明 xff1a 本文为博主原创文章 xff0c 未经博主允许不得转载 sudo apt get install sysv rc conf sudo sysv rc conf 运行级别说明 xff1a S表示开机后就会运行的服务0表示关
  • 安装vnc的各种悲剧解决

    系统 环境 VM 43 RHEL5 1 root 64 localhost vnc uname r 2 6 18 53 el5xen 本地XP系统安装 VNCVIEW去控制VM中的RHEL5 1 下面在LINUX上安装VNCSERVER 1
  • iOS基础 UITabBarController

    使用 创建子控制器继承自UITabBarController xff0c 在viewDidLoad阶段 xff0c 把各个分页上的控制器给创建好 xff0c 用UITabBarController的方法addChildControoler相
  • 插入内核模块失败提示"Invalid module format"

    产品需要编译自己的定制内核 43 内核模块 xff0c 下载内核源码定制修改后rpmbuild方式 点击打开链接 编译升级内核 xff0c 如下方式编译内核模块 make C kernel source SUBDIRS 61 96 pwd
  • microsoft visual c++ build tools

    因为visual studio的安装包太大 xff0c 所以在不需要开发的情况下 xff0c 可以选择使用microsoft visual c 43 43 build tools安装c 43 43 编译器 xff0c 这个工具会小很多 安装
  • C++ 应用程序 内存结构 --- BSS段,数据段,代码段,堆内存和栈

    转自 xff1a http hi baidu com C6 BF D6 D0 B5 C4 C5 AE CE D7 blog item 5043d08e741075f3503d922c html ld 时把所有的目标文件的代码段组合成一个代码
  • 4.1 简单题 - B 恭喜你

    当别人告诉你自己考了 x 分的时候 xff0c 你要回答说 xff1a 恭喜你考了 x 分 xff01 比如小明告诉你他考了90分 xff0c 你就用汉语拼音打出来 gong xi ni kao le 90 fen 输入格式 xff1a 输
  • <script>在页面代码上没有显示

    记录一下 导入js文件 xff0c 自己路径都没有问题 xff0c 为什么在浏览器查看页面代码没有自己写的那行js导入文件的代码呢 xff0c 原来 xff0c 是之前看着不舒服 xff0c 点了exclude xff0c exclude是
  • 利用Rust构建一个REST API服务

    利用Rust构建一个REST API服务 关注公众号 xff1a 香菜粉丝 了解更多精彩内容 Rust 是一个拥有很多忠实粉丝的编程语言 xff0c 还是很难找到一些用它构建的项目 xff0c 而且掌握起来甚至有点难度 想要开始学习一门编程
  • 安装cmake3.22

    升级cmake版本 脚本 span class token assign left variable file name span span class token operator 61 span cmake 3 22 0 yum era
  • stdout stderr 重定向到文件

    1 stdout stderr 重定向 1 stdout stderr 重定向 1 1 dup dup2 重定向到已打开文件 或 新文件1 2 freopen 重定向到新文件1 3 命令行重定向1 4 参考资料 1 1 dup dup2 重
  • 逆向基础-Windows驱动开发(一)

    Windows内核开发 第一个驱动程序 环境配置 xff1a 安装WDK xff1a WDK版本与SDK保持一致 然后记得把Spectre Mitigation给Disabled掉 xff0c 就不用去下载漏洞补丁了 然后在内核层 xff0
  • json-c 理解记录

    1 json c 理解记录 1 json c 理解记录 1 1 编译及说明1 2 特色1 3 使用 1 3 1 创建 xff0c 读写文件1 3 2 拷贝1 3 3 增改 1 3 3 1 字典增加元素1 3 3 2 数组增加修改元素 1 3
  • valgrind 简介(内存检查工具)

    1 valgrind 简介 1 valgrind 简介 1 1 概图1 2 特点1 3 使用示例1 4 参数说明 1 4 1 常用参数1 4 2 展示1 4 3 子进程 动态加载库及记录时机1 4 4 查错内存优化1 4 5 其他不常用1
  • GObject学习教程---第一章:GObject是有用并且简单的

    索引 xff1a https blog csdn net knowledgebao article details 84633798 本文是学习学习他人的博客的心得 xff08 具体详见 楼主见解 xff09 xff0c 如果源网站可访问的
  • GObject学习教程---第二章:模拟类的数据封装形式

    索引 xff1a https blog csdn net knowledgebao article details 84633798 本文是学习学习他人的博客的心得 xff08 具体详见 楼主见解 xff09 xff0c 如果源网站可访问的