【GDB自定义指令】core analyzer结合gdb的调试及自定义gdb指令详情

目录

🌊前言

🌞1. 程序分析

🌞2. 案例说明

🌍2.1 修改内容

🌍2.2 测试自定义指令

🌞3. 实战内容

🌳3.1 修改内容

🌳3.2 测试自定义指令

🌳3.2 自定义gdb指令错误纠察

🍃测试一:手动开辟空间测试

🍃测试二:程序开辟,修改addstructcmd.c 

🍃测试总结


🌊前言

本文目的:这段时间在使用gdb调试,我在思考能不能通过自定义一些gdb指令进行调试?本文即在此基础上完成了自定义gdb指令【借鉴core analyzer工具实现】。

准备内容:

  • 操作环境
    1. ubuntu 22.04
    2. 安装core analyzer,传送门:【core analyzer安装】core analyzer的简介和安装问题解决详情 
  • 熟悉gdb指令,传送门:【gdb调试】在ubuntu环境使用gdb调试一棵四层二叉树的数据结构详解

说明:为了方便对比和分析,我创建了两个core analyzer的环境,分别是

  1. core_analyzer
  2. core_analyzer_Test

其中core_analyzer 是初始环境【保持不动】,core_analyzer_Test 是测试环境【用作自定义gdb指令的环境】。


🌞1. 程序分析

程序修改的主要位置:
/root/host/core_analyzer_Test/core_analyzer/gdbplus/gdb-12.1/gdb/

可以看见heapcmd.c文件,其中包含一些特定GDB的函数和命令【GDB扩展】,该文件定义的功能是原来gdb12.1所没有的,引入了一些新的命令和功能,用于堆内存分析、对象搜索、内存段显示等。

heapcmd.c文件分析:

  1. 命令函数

    • 文件定义了多个函数,对应于调试器可以执行的命令。这些命令包括与堆内存检查、对象搜索、内存段显示等相关的操作。
    • 每个函数通常接受一个字符串参数args和一个整数参数from_tty,这表示命令的来源是否是终端。
  2. 命令实现

    • heap_commandref_commandpattern_commandsegment_command等函数实现了特定的调试器命令。
    • 例如,heap_command似乎处理与堆内存相关的各种操作,如堆遍历、内存使用统计、泄漏检查等。
    • 同样,ref_command用于搜索给定对象的引用,pattern_command用于显示内存模式,segment_command用于显示内存段。
  3. 命令参数解析

    • 这些命令接收的参数似乎在函数内部进行解析。例如,使用ca_parse_options函数将args字符串解析为标记。
    • 然后使用这些标记来确定要执行的特定操作或提取必要的信息,如内存地址或选项。
  4. 初始化函数

    • 存在一个初始化函数_initialize_heapcmd,它将这些命令注册到调试器中。
    • 这个函数使用add_cmd函数将命令添加到调试器的命令列表中。
  5. 辅助函数

    • 文件中有多个辅助函数,如内存分配包装器unique_xmalloc_ptr,解析函数parse_and_eval_address和打印函数CA_PRINT
  6. 帮助消息

    • 存在一个帮助消息ca_help_msg,提供了可用命令的摘要以及它们的使用说明。
    • 当用户使用ca_help命令请求帮助时,将显示此消息。
  7. 其他

    • 其他函数如display_help_commandswitch_heap_commandinfo_local_command等提供了调试器环境中的其他功能或设置。

🌞2. 案例说明

🌍2.1 修改内容

测试目的:使用自定义指令打印出二叉树的所有节点【固定写死的内容】。

根据上述的分析,这里我打算编写三个文件:

  • addstructcmd.c     -----编写cmd内容
  • addstruct.h            -----编写结构体定义+扩展接口
  • add_Struct.c          -----编写具体的接口实现

详细步骤如下:

在/root/host/core_analyzer_Test/core_analyzer/gdbplus/gdb-12.1/gdb/目录下创建3个文件+内容:


vim addstructcmd.c
/** addstructcmd.c**  Created on: Feb 17, 2024*      Author: sym*/
#include "ref.h"
#include "addstruct.h"/***************************************************************************
* gdb commands
***************************************************************************/
static void
addstructmem_command (const char *args, int from_tty)
{CA_PRINT("addstructmem_command\n");
}// 打印二叉树的所有节点
static void
addstruct_tree_command (const char *args, int from_tty)
{TreeNode* root = buildTree();print_Tree(root);CA_PRINT("\n");
}static char addstruct_help_msg[] = "Commands of addstruct v1.0\n""addstructmem    -- print memory pool info.\n""addstruct_tree  -- print binary tree node values.\n" // 添加新命令的帮助信息"type 'help <command>' to get more detail and usage info\n";static void
display_addstruct_help_command (const char *args, int from_tty)
{CA_PRINT("%s", addstruct_help_msg);
}void _initialize_addstructcmd ();void
_initialize_addstructcmd ()
{add_cmd("addstructmem", class_info, addstructmem_command, _("Search for references to a given object"), &cmdlist);// 打印二叉树add_cmd("addstruct_tree", class_info, addstruct_tree_command, _("Display binary tree node values"), &cmdlist);// help listadd_cmd("addstruct_help", class_info, display_addstruct_help_command, _("Display core analyzer help"), &cmdlist);
}

vim addstruct.h
/** addstruct.h**  Created on: Feb 17, 2024*      Author: sym*/
#include <stdio.h>
#include <stdlib.h>#ifndef TREENODE_H
#define TREENODE_H
/** Data structures for reference*/
typedef struct TreeNode {int data;struct TreeNode *left;struct TreeNode *right;
} TreeNode;/
// Import functions (required from  etc.)
/
// 创建一个新的树节点
extern TreeNode* createNode(int data);
// 建立树节点数据:构建四层树
extern TreeNode* buildTree();
// 递归遍历树并打印节点数据
extern void print_Tree(TreeNode* root);#endif /* TREENODE_H */

vim add_Struct.c
/** add_Struct.c**  Created on: Feb 17, 2024*      Author: sym*/
#include "addstruct.h"// 创建一个新的树节点
TreeNode* 
createNode(int data) {TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));if (newNode == NULL) {fprintf(stderr, "Memory allocation failed.\n");exit(EXIT_FAILURE);}newNode->data = data;newNode->left = NULL;newNode->right = NULL;return newNode;
}// 建立树节点数据:构建四层树
TreeNode* 
buildTree() {TreeNode* root = createNode(1);root->left = createNode(2);root->right = createNode(3);root->left->left = createNode(4);root->left->right = createNode(5);root->right->left = createNode(6);root->right->right = createNode(7);root->left->left->left = createNode(8);root->left->left->right = createNode(9);return root;
}// 递归遍历树并打印节点数据
void 
print_Tree(TreeNode* root) {if (root != NULL) {printf("%d ", root->data);print_Tree(root->left);print_Tree(root->right);}
}

给上述三个文件加入rwx权限:

chmod 777 addstructcmd.c 
chmod 777 addstruct.h 
chmod 777 add_Struct.c

然后打开Makefile.in文件,在1102行加入

    addstructcmd.c \add_Struct.c \


🌍2.2 测试自定义指令

回到/root/host/core_analyzer_Test/core_analyzer目录,终端输入

./build_gdb.sh

编译完成后在当前路径下进行终端输入

./build/gdb-12.1/build/gdb/gdb

测试gdb的自定义指令

addstructmem

addstruct_help

addstruct_tree

显示如上图则表明添加自定义gdb指令初步成功!


🌞3. 实战内容

前面案例实现了几个简单的自定义gdb指令,但缺陷在于都是基于写死的内容打印输出,实际情况使用gdb是为了去调试自己的程序是否存在问题,所以需要加上用户调试的参数以完善自定义gdb指令,使其更加灵活。


🌳3.1 修改内容

 根据上述的案例,这里我打算编写文件:

  • addstructcmd.c     -----编写cmd内容

详细步骤如下:

在/root/host/core_analyzer_Test/core_analyzer/gdbplus/gdb-12.1/gdb/目录下创建addstructcmd.c 文件+内容:


vi addstructcmd.c 
/** addstructcmd.c**  Created on: Feb 17, 2024*      Author: sym*/
#include "ref.h"/***************************************************************************
* new structs and print structs
***************************************************************************/
// 定义数据结构:树
struct TreeNode {int data;struct TreeNode *left;struct TreeNode *right;
};// 打印数据:树【先序遍历】
static void 
print_tree_nodes(struct TreeNode* root) {if (root == NULL) {return;}printf("%d ", root->data);if (root->left != NULL) {print_tree_nodes(root->left);}if (root->right != NULL) {print_tree_nodes(root->right);}
}
/***************************************************************************
* gdb commands
***************************************************************************/
static void
addstructmem_command (const char *args, int from_tty)
{CA_PRINT("addstructmem_command\n");
}// 测试内容
static void
addstruct_test_command (const char *args, int from_tty)
{printf("args=%s\n", args);printf("from_tty=%d\n", from_tty);
}// 打印以指定根节点地址为根的二叉树的所有节点
static void 
addstruct_tree_command(const char *args, int from_tty) 
{if (!args || *args == '\0') {printf("Please provide the root node address.\n");return;}// 将 args 存储的地址内容 转换为 root 的地址struct TreeNode *root = (struct TreeNode *)strtoul(args, NULL, 0);// 如果找到了指定的根节点,则打印以该节点为根的二叉树的所有节点if (root != NULL) {printf("Here is test root info:add and data\n");// 对指针进行有效性检查printf("root node address: %p\n", (void *)root); // 打印传入的根节点地址,用于验证printf("root node data: %d\n\n", root->data); // 打印传入的根节点数据,用于验证printf("Printing tree nodes:\n");print_tree_nodes(root);printf("\n");} else {printf("Specified root node not found.\n");}
}static char addstruct_help_msg[] = "Commands of addstruct v1.0\n""addstructmem    -- print memory pool info.\n""addstruct_test <txt>    -- test get user inputTxt\n""addstruct_tree <root>  -- print binary tree node values starting from specified root node.\n""type 'help <command>' to get more detail and usage info\n";static void
display_addstruct_help_command (const char *args, int from_tty)
{CA_PRINT("%s", addstruct_help_msg);
}void _initialize_addstructcmd ();void
_initialize_addstructcmd ()
{add_cmd("addstructmem", class_info, addstructmem_command, _("Search for references to a given object"), &cmdlist);// 测试获取输入内容add_cmd("addstruct_test", class_info, addstruct_test_command, _("Display user inputTxt"), &cmdlist);// 打印二叉树add_cmd("addstruct_tree", class_info, addstruct_tree_command, _("Display binary tree node values"), &cmdlist);// help listadd_cmd("addstruct_help", class_info, display_addstruct_help_command, _("Display core analyzer help"), &cmdlist);
}

给上述文件加入rwx权限:

chmod 777 addstructcmd.c 

然后打开Makefile.in文件,在1102行加入

    addstructcmd.c \


🌳3.2 测试自定义指令

回到/root/host/core_analyzer_Test/core_analyzer目录,终端输入

./build_gdb.sh

编译完成后在当前路径下进行终端输入【说明:tree3_01.c 文件内容参考:【gdb调试】在ubuntu环境使用gdb调试一棵四层二叉树的数据结构详解】

./build/gdb-12.1/build/gdb/gdb  /root/host/my_program/tree3_01

测试gdb的自定义指令

addstructmem

addstruct_help

addstruct_tree + 地址

显示如下图则表明添加自定义gdb指令初步成功!

这里也反馈了一个信息:地址传递给 args,通过变量 args 成功将 root 的地址设置成了传递的地址【用户传递参数的处理过程是没问题的】,显示段错误是因为这个传递的地址空间未开辟。


🌳3.2 自定义gdb指令错误纠察

纠错思路:使用gdb调试修改的gdb进行查错,由于gdb中没有构建一颗完整的二叉树,所以我打算手动开辟一棵两层的二叉树用作测试。

🍃测试一:手动开辟空间测试

在目录:/root/host/core_analyzer_Test/core_analyzer 使用gdb调试gdb:

./build/gdb-12.1/build/gdb/gdb ./build/gdb-12.1/build/gdb/gdb

打开后依次执行:

start
b addstruct_tree_commandjump /root/host/core_analyzer_Test/core_analyzer/build/gdb-12.1/build/../gdb/addstructcmd.c:55set *((int *)0x5555555592a0) = 1
set ((struct TreeNode *)0x5555555592a0)->left = (struct TreeNode *)0x5555555592c0
set ((struct TreeNode *)0x5555555592a0)->right = (struct TreeNode *)0x5555555592e0set *((int *)0x5555555592c0) = 2
set ((struct TreeNode *)0x5555555592c0)->left = (struct TreeNode *)0x0
set ((struct TreeNode *)0x5555555592c0)->right = (struct TreeNode *)0x0set *((int *)0x5555555592e0) = 3
set ((struct TreeNode *)0x5555555592e0)->left = (struct TreeNode *)0x0
set ((struct TreeNode *)0x5555555592e0)->right = (struct TreeNode *)0x0set variable args = "0x5555555592a0"f

此时地址空间开辟的地址和值如下图:

调试可以正常打印:


🍃测试二:程序开辟,修改addstructcmd.c 

修改addstructcmd.c 内容如下:

/** addstructcmd.c**  Created on: Feb 17, 2024*      Author: sym*/
#include "ref.h"/***************************************************************************
* new structs and print structs
***************************************************************************/
// 定义数据结构:树
struct TreeNode {int data;struct TreeNode *left;struct TreeNode *right;
};// 创建一个新的树节点
TreeNode* createNode(int data) {TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode));if (newNode == NULL) {fprintf(stderr, "Memory allocation failed.\n");exit(EXIT_FAILURE);}newNode->data = data;newNode->left = NULL;newNode->right = NULL;return newNode;
}// 构建四层树
struct TreeNode* buildTree() {TreeNode* root = createNode(1);root->left = createNode(2);root->right = createNode(3);root->left->left = createNode(4);root->left->right = createNode(5);root->right->left = createNode(6);root->right->right = createNode(7);root->left->left->left = createNode(8);root->left->left->right = createNode(9);return root;
}// 打印数据:树【先序遍历】
static void 
print_tree_nodes(struct TreeNode* root) {if (root == NULL) {return;}printf("%d ", root->data);if (root->left != NULL) {print_tree_nodes(root->left);}if (root->right != NULL) {print_tree_nodes(root->right);}
}
/***************************************************************************
* gdb commands
***************************************************************************/
static void
addstructmem_command (const char *args, int from_tty)
{CA_PRINT("addstructmem_command\n");
}// 测试内容
static void
addstruct_test_command (const char *args, int from_tty)
{printf("args=:%s\n", args);printf("args=:%d\n", from_tty);
}// 打印以指定根节点地址为根的二叉树的所有节点
static void 
addstruct_tree_command(const char *args, int from_tty) 
{if (!args || *args == '\0') {printf("Please provide the root node address.\n");return;}// 构建树struct TreeNode* root_frist = buildTree();// 将 args 存储的地址内容 转换为 root 的地址struct TreeNode *root = (struct TreeNode *)strtoul(args, NULL, 0);// 如果找到了指定的根节点,则打印以该节点为根的二叉树的所有节点if (root != NULL) {printf("Here is test root info:add and data\n");// 对指针进行有效性检查printf("root node address: %p\n", (void *)root); // 打印传入的根节点地址,用于验证printf("root node data: %d\n\n", root->data); // 打印传入的根节点数据,用于验证printf("Printing tree nodes:\n");print_tree_nodes(root);printf("\n");} else {printf("Specified root node not found.\n");}
}static char addstruct_help_msg[] = "Commands of addstruct v1.0\n""addstructmem    -- print memory pool info.\n""addstruct_test <txt>    -- test get user inputTxt\n""addstruct_tree <root>  -- print binary tree node values starting from specified root node.\n""type 'help <command>' to get more detail and usage info\n";static void
display_addstruct_help_command (const char *args, int from_tty)
{CA_PRINT("%s", addstruct_help_msg);
}void _initialize_addstructcmd ();void
_initialize_addstructcmd ()
{add_cmd("addstructmem", class_info, addstructmem_command, _("Search for references to a given object"), &cmdlist);// 测试获取输入内容add_cmd("addstruct_test", class_info, addstruct_test_command, _("Display user inputTxt"), &cmdlist);// 打印二叉树add_cmd("addstruct_tree", class_info, addstruct_tree_command, _("Display binary tree node values"), &cmdlist);// help listadd_cmd("addstruct_help", class_info, display_addstruct_help_command, _("Display core analyzer help"), &cmdlist);
}

然后回到章节 3.2 进行编译。

在目录:/root/host/core_analyzer_Test/core_analyzer 使用gdb调试gdb:

./build/gdb-12.1/build/gdb/gdb ./build/gdb-12.1/build/gdb/gdb

打开后依次执行【这里赋给args的值是随意的,防止 if 语句那里直接 return】:

start
b addstruct_tree_commandjump /root/host/core_analyzer/build/gdb-12.1/build/../gdb/addstructcmd.c:84set variable args = "0x5555555592a0"
f

使用p root_frist 打印 root_frist 的地址,然后将其重新赋给args

在gdb写死的数据结构:树【调用方法 struct TreeNode* buildTree() 】。获取地址可以正常输出:


🍃测试总结

上面两轮测试都在说明==>程序没问题,传参的设置与转换没问题。

问题:无法知道用户开辟的地址在哪,解决思路是通过输入变量名然后搜索堆栈获取信息,再打印。类似(比如p root会输出地址信息,这里的root就是变量名),而后面要做的就是获取这个显示的地址。

补充说明:
./xxx  每次执行开辟的地址空间不同,而gdb调试每次的开辟地址都一样。


http://www.ppmy.cn/embedded/9533.html

相关文章

element UI 走马灯 initial-index动态赋值 不生效问题

图片列表点击展示大图&#xff0c;点开不是当前的图片 initial-index属性动态赋值不生效 解决方法&#xff1a; 1.设置initial-index初始值为null initialIndex:null2.设置走马灯轮播数组初始化为[] imgList:[]3.点击图片获取图片的索引&#xff0c;赋值给initialIndex&#x…

【Java EE】Spring核心思想(一)——IOC

文章目录 &#x1f38d;Spring 是什么&#xff1f;&#x1f384;什么是IoC呢&#xff1f;&#x1f338;传统程序开发&#x1f338;传统程序开发的缺陷&#x1f338;如何解决传统程序的缺陷&#xff1f;&#x1f338;控制反转式程序开发&#x1f338;对比总结 &#x1f332;理解…

【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效,灰度、负片、像素化特效

前言【Unity 实用工具篇】 | UIEffect 实现一系列UGUI特效,灰度、负片、像素化特效一、UGUI特效插件:UIEffect1.1 介绍1.2 效果展示1.3 使用说明及下载二、组件属性面板三、代码操作组件四、组件常用方法示例4.1 使用灰度特效做头像(关卡)选择总结

【昇腾产品应用】英码科技EA500I基于昇腾Mind SDK实现实时人体关键点检测

在教育、体育、安防、交通、医疗等领域中&#xff0c;实时人体关键点检测应用发挥着至关重要的作用&#xff0c;比如在体育训练时&#xff0c;实时人体关键点检测可以精确、实时地捕捉运动员的动作&#xff0c;从而进行动作分析和优化&#xff1b;在安防应用场景中&#xff0c;…

JEECG表格选中状态怎么去掉

官网代码&#xff08;在取消选中状态的时候不生效&#xff09; rowSelection() {return {onChange: (selectedRowKeys, selectedRows) > {console.log(selectedRowKeys: ${selectedRowKeys}, selectedRows: , selectedRows);},getCheckboxProps: record > ({props: {disa…

【S32K3 MCAL配置】-7.1-GPT Driver:定时器中断-创建一个周期执行的任务

"><--返回「Autosar_MCAL高阶配置」专栏主页--> 案例背景:常用于周期点亮/关闭一个LED灯;或者精度一般的占空比为50% PWM方波;或者周期调用一个函数,在该函数中我们可以执行一些软件策略(简易的OS)。 目录(共15页精讲,基于评估板: NXP S32K312EVB-Q172,…

Spring注解@ResponseBody的作用与应用场景

注解详情 ResponseBody 是 Spring MVC 中的一个注解&#xff0c;它的作用是将控制器中的方法返回值作为响应体&#xff08;Response Body&#xff09;直接返回给客户端&#xff0c;而不是作为视图模板&#xff08;View Template&#xff09;进行渲染。 在 Spring MVC 中&…

一文了解OCI标准、runC、docker、contianerd、CRI的关系

docker和contanerd都是流行的容器运行时&#xff08;container runtime&#xff09;&#xff1b;想讲清楚他们两之间的关系&#xff0c;让我们先从runC和OCI规范说起。 一、OCI标准和runC 1、OCI&#xff08;open container initiative&#xff09; OCI是容器标准化组织为了…

面试十四、内存泄漏

1.内存溢出和内存泄漏是啥 内存溢出 out of memory&#xff0c;是指程序在申请内存时&#xff0c;没有足够的内存空间供其使用&#xff0c;出现out of memory&#xff1b;比如申请了一个integer,但给它存了long才能存下的数&#xff0c;那就是内存溢出。 内存泄露 memo…

ROS 2边学边练(28)-- 将launch文件集合到ROS 2包中

前言 上一篇我们单独创建了文件夹&#xff08;可以任何路径&#xff09;&#xff0c;并在其中创建编写了一份launch文件&#xff0c;调用了小海龟例程&#xff0c;但通常情况下这并非是launch文件的典型应用场景&#xff0c;更多的是在ROS 2功能包中创建使用launch文件。 动动…

v-deep 打破作用域隔离的原理

vue 中使用 scoped 样式隔离 使用 ::v-deep 和 >>> &#xff0c;穿透作用域样式&#xff0c;以便在父组件中修改子组件的样式&#xff0c;即打破样式隔离。 vue 使用了一种叫做 scoped css 的技术来隔离组件的样式&#xff0c;确保他们不会泄漏到其他组件中&#xf…

中图分类法的正则表达式参考

文章目录 1. 中图分类法2. 正则表达式3. 使用方法4. 参考 1. 中图分类法 中图分类法&#xff0c;全称为《中国图书馆图书分类法》&#xff0c;简称《中图法》&#xff0c;是中国国内普遍采用的一种图书分类体系&#xff0c;用于组织和管理图书馆藏书&#xff0c;方便读者查找和…

第25天:安全开发-PHP应用文件管理包含写入删除下载上传遍历安全

第二十五天 一、PHP文件管理-下载&删除功能实现 1.文件上传&#xff1a; 无过滤机制黑名单过滤机制白名单过滤机制文件类型过滤机制 2.文件删除&#xff1a; unlink() 文件删除函数调用命令删除&#xff1a;system shell_exec exec等 3. 文件下载&#xff1a; 修改HT…

在Debian上可以部署哪些有用的服务?

2024年4月20日&#xff0c;周六下午 在 Debian 上可以部署许多有用的服务&#xff0c;以满足各种需求。 以下是一些常见的有用服务&#xff1a; Web 服务器&#xff1a;如 Apache、Nginx、Lighttpd 等&#xff0c;用于托管网站和 Web 应用。数据库服务器&#xff1a;如 MySQL…

三分钟带你读懂面向对象的三大特征:封装,继承,多态

很多小伙伴在学面向对象的时候觉得非常抽象&#xff0c;尤其是对于面向对象的三大特征&#xff1a;封装&#xff0c;继承&#xff0c;多态不理解&#xff0c;那这期文章呢&#xff0c;九九就给大家安排&#xff0c;三分种带你迅速掌握封装&#xff0c;继承&#xff0c;多态。 …

七、Mock 模拟后端接口

1、安装&#xff1a;pnpm install -D vite-plugin-mock mockjs 2、vite.config.ts 配置文件启用插件。 // mock 插件提供的方法 import { viteMockServe } from vite-plugin-mockexport default defineConfig({plugins: [vue(),viteMockServe({mockPath: mock,enable: true,/…

Linux 2.进程(守护进程)

守护进程 何谓守护进程常见守护进程进程查看命令pskill命令编写简单守护进程守护进程的父进程 何谓守护进程 daemon&#xff0c;表示守护进程&#xff0c;简称为d&#xff08;进程名后面带d的基本就是守护进程&#xff09; 长期运行&#xff08;一般是开机运行直到关机时关闭&…

Docker一键快速私有化部署(Ollama+Openwebui) +AI大模型(gemma,llama2,qwen)20240417更新

几行命令教你私有化部署自己的AI大模型&#xff0c;每个人都可以有自己的GTP 第一步&#xff1a;安装Docker(如果已经有了可以直接跳第二步) ####下载安装Docker wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O/etc/yum.repos.d/docker-ce.repo##…

el-table-column叠加el-popover使用

需求&#xff1a;el-table-column有一列展示多个tag信息&#xff0c;实现点击tag展示tag信息以及tag对应的详细信息 table的数据格式 data:[{...,isPopoverVisible:false,},{...,isPopoverVisible:false,},... ]写法&#xff1a; <el-table-column label"配置信息&q…

字符数组和字符串题

1.字符数组(1-2) 下面的程序段将输出 ▁▁▁C▁▁ 。 char s[10] "abcd"; printf("%d\n", sizeof(s)); A.4 B.5 C.10 D.11 2.字符数组(1-3) 下面的程序段将输出 ▁▁C▁▁▁ 。 char s[] "abcd"; printf("%d\n", strlen(s))…