让你的代码变的更加健壮(Making your C++ code robust)

2023-10-28

 Making your C++ code robust

  • Introduction

       在实际的项目中,当项目的代码量不断增加的时候,你会发现越来越难管理和跟踪其各个组件,如其不善,很容易就引入BUG。因此、我们应该掌握一些能让我们程序更加健壮的方法。

       这篇文章提出了一些建议,能有引导我们写出更加强壮的代码,以避免产生灾难性的错误。即使、因为其复杂性和项目团队结构,你的程序目前不遵循任何编码规则,按照下面列出的简单的规则可以帮助您避免大多数的崩溃情况。

  • Background

        先来介绍下作者开发一些软件(CrashRpt),你可以http://code.google.com/p/crashrpt/网站上下载源代码。CrashRpt 顾名思义软件崩溃记录软件(库),它能够自动提交你电脑上安装的软件错误记录。它通过以太网直接将这些错误记录发送给你,这样方便你跟踪软件问题,并及时修改,使得用户感觉到每次发布的软件都有很大的提高,这样他们自然很高兴。

图 1、CrashRpt 库检测到错误弹出的对话框

       在分析接收的错误记录的时候,我们发现采用下文介绍的方法能够避免大部分程序崩溃的错误。例如、局部变量未初始化导致数组访问越界,指针使用前未进行检测(NULL)导致访问访问非法区域等。

      我已经总结了几条代码设计的方法和规则,在下文一一列出,希望能够帮助你避免犯一些错误,使得你的程序更加健壮。

  • Initializing Local Variables 

     使用未初始化的局部变量是引起程序崩溃的一个比较普遍的原因,例如、来看下面这段程序片段:

  // Define local variables
  BOOL bExitResult; // This will be TRUE if the function exits successfully
  FILE* f; // Handle to file
  TCHAR szBuffer[_MAX_PATH];   // String buffer
    
  // Do something with variables above... 

     上面的这段代码存在着一个潜在的错误,因为没有一个局部变量初始化了。当你的代码运行的时候,这些变量将被默认负一些错误的数值。例如bExitResult 数值将被负为-135913245 ,szBuffer 必须以“\0”结尾,结果不会。因此、局部变量初始化时非常重要的,如下正确代码:

  // Define local variables
  
  // Initialize function exit code with FALSE to indicate failure assumption
  BOOL bExitResult = FALSE; // This will be TRUE if the function exits successfully
  // Initialize file handle with NULL
  FILE* f = NULL; // Handle to file
  // Initialize string buffer with empty string
  TCHAR szBuffer[_MAX_PATH] = _T("");   // String buffer
  // Do something with variables above... 

    注意:有人说变量初始化会引起程序效率降低,是的,确实如此,如果你确实非常在乎程序的执行效率,去除局部变量初始化,你得想好其后果。

  • Initializing WinAPI Structures

       许多Windows API都接受或则返回一些结构体参数,结构体如果没有正确的初始化,也很有可能引起程序崩溃。大家可能会想起用ZeroMemory宏或者memset()函数去用0填充这个结构体(对结构体对应的元素设置默认值)。但是大部分Windows API 结构体都必须有一个cbSIze参数,这个参数必须设置为这个结构体的大小。

       看看下面代码,如何初始化Windows API结构体参数:

  NOTIFYICONDATA nf; // WinAPI structure
  memset(&nf,0,sizeof(NOTIFYICONDATA)); // Zero memory
  nf.cbSize = sizeof(NOTIFYICONDATA); // Set structure size!
  // Initialize other structure members
  nf.hWnd = hWndParent;
  nf.uID = 0;   
  nf.uFlags = NIF_ICON | NIF_TIP;
  nf.hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
  _tcscpy_s(nf.szTip, 128, _T("Popup Tip Text"));
        
  // Add a tray icon
  Shell_NotifyIcon(NIM_ADD, &nf);

      注意:千万不要用ZeroMemory和memset去初始化那些包括结构体对象的结构体,这样很容易破坏其内部结构体,从而导致程序崩溃.

  // Declare a C++ structure
  struct ItemInfo
  {
    std::string sItemName; // The structure has std::string object inside
    int nItemValue;
  }; 
 
  // Init the structure
  ItemInfo item;
  // Do not use memset()! It can corrupt the structure
  // memset(&item, 0, sizeof(ItemInfo));
  // Instead use the following
  item.sItemName = "item1";
  item.nItemValue = 0; 
     这里最好是用结构体的构造函数对其成员进行初始化.

  // Declare a C++ structure
  struct ItemInfo
  {
    // Use structure constructor to set members with default values
    ItemInfo()
    {
      sItemName = _T("unknown");
      nItemValue = -1;
    }
        
    std::string sItemName; // The structure has std::string object inside
    int nItemValue;
  };
  // Init the structure
  ItemInfo item;
  // Do not use memset()! It can corrupt the structure
  // memset(&item, 0, sizeof(ItemInfo));
  // Instead use the following
  item.sItemName = "item1";
  item.nItemValue = 0;    
  • Validating Function Input 

      在函数设计的时候,对传入的参数进行检测是一直都推荐的。例如、如果你设计的函数是公共API的一部分,它可能被外部客户端调用,这样很难保证客户端传进入的参数就是正确的。

      例如,让我们来看看这个hypotethical DrawVehicle() 函数,它可以根据不同的质量来绘制一辆跑车,这个质量数值(nDrawingQaulity )是0~100。prcDraw 定义这辆跑车的轮廓区域。

      看看下面代码,注意观察我们是如何在使用函数参数之前进行参数检测:

BOOL DrawVehicle(HWND hWnd, LPRECT prcDraw, int nDrawingQuality)
  {
    // Check that window is valid
    if(!IsWindow(hWnd))
      return FALSE;
 
    // Check that drawing rect is valid
    if(prcDraw==NULL)
      return FALSE;
 
    // Check drawing quality is valid
    if(nDrawingQuality<0 || nDrawingQuality>100)
      return FALSE;
   
    // Now it's safe to draw the vehicle
 
    // ...
 
    return TRUE;
  }
  • Validating Pointers

       在指针使用之前,不检测是非常普遍的,这个可以说是我们引起软件崩溃最有可能的原因。如果你用一个指针,这个指针刚好是NULL,那么你的程序在运行时,将报出异常。

  CVehicle* pVehicle = GetCurrentVehicle();
  
  // Validate pointer
  if(pVehicle==NULL)
  {
    // Invalid pointer, do not use it!
    return FALSE;
  }
  • Initializing Function Output

     如果你的函数创建了一个对象,并要将它作为函数的返回参数。那么记得在使用之前把他复制为NULL。如不然,这个函数的调用者将使用这个无效的指针,进而一起程序错误。如下错误代码:

int CreateVehicle(CVehicle** ppVehicle)
  {
    if(CanCreateVehicle())
    {
      *ppVehicle = new CVehicle();
      return 1;
    }    
 
    // If CanCreateVehicle() returns FALSE,
    // the pointer to *ppVehcile would never be set!
    return 0;
  }

      正确的代码如下;

  int CreateVehicle(CVehicle** ppVehicle)
  {
    // First initialize the output parameter with NULL
    *ppVehicle = NULL;
 
    if(CanCreateVehicle())
    {
      *ppVehicle = new CVehicle();
      return 1;
    }    
 
    return 0;
  }
  • Cleaning Up Pointers to Deleted Objects

     在内存释放之后,无比将指针复制为NULL。这样可以确保程序的没有那个地方会再使用无效指针。其实就是,访问一个已经被删除的对象地址,将引起程序异常。如下代码展示如何清除一个指针指向的对象:

 // Create object
 CVehicle* pVehicle = new CVehicle();
 delete pVehicle; // Free pointer
 pVehicle = NULL; // Set pointer with NULL
  • Cleaning Up Released Handles 

      在释放一个句柄之前,务必将这个句柄复制伪NULL (0或则其他默认值)。这样能够保证程序其他地方不会重复使用无效句柄。看看如下代码,如何清除一个Windows API的文件句柄:

  HANDLE hFile = INVALID_HANDLE_VALUE; 
  
  // Open file
  hFile = CreateFile(_T("example.dat"), FILE_READ|FILE_WRITE, FILE_OPEN_EXISTING);
  if(hFile==INVALID_HANDLE_VALUE)
  {
    return FALSE; // Error opening file
  }
 
  // Do something with file
 
  // Finally, close the handle
  if(hFile!=INVALID_HANDLE_VALUE)
  {
    CloseHandle(hFile);   // Close handle to file
    hFile = INVALID_HANDLE_VALUE;   // Clean up handle
  } 

     下面代码展示如何清除File *句柄:

  // First init file handle pointer with NULL
  FILE* f = NULL;
 
  // Open handle to file
  errno_t err = _tfopen_s(_T("example.dat"), _T("rb"));
  if(err!=0 || f==NULL)
    return FALSE; // Error opening file
 
  // Do something with file
 
  // When finished, close the handle
  if(f!=NULL) // Check that handle is valid
  {
    fclose(f);
    f = NULL; // Clean up pointer to handle
  } 
  • Using delete [] Operator for Arrays 

     如果你分配一个单独的对象,可以直接使用new ,同样你释放单个对象的时候,可以直接使用delete . 然而,申请一个对象数组对象的时候可以使用new,但是释放的时候就不能使用delete ,而必须使用delete[]:

 // Create an array of objects
 CVehicle* paVehicles = new CVehicle[10];
 delete [] paVehicles; // Free pointer to array
 paVehicles = NULL; // Set pointer with NULL
or
 // Create a buffer of bytes
 LPBYTE pBuffer = new BYTE[255];
 delete [] pBuffer; // Free pointer to array
 pBuffer = NULL; // Set pointer with NULL
  • Allocating Memory Carefully 

     有时候,程序需要动态分配一段缓冲区,这个缓冲区是在程序运行的时候决定的。例如、你需要读取一个文件的内容,那么你就需要申请该文件大小的缓冲区来保存该文件的内容。在申请这段内存之前,请注意,malloc() or new是不能申请0字节的内存,如不然,将导致malloc() or new函数调用失败。传递错误的参数给malloc() 函数将导致C运行时错误。如下代码展示如何动态申请内存:

  // Determine what buffer to allocate.
  UINT uBufferSize = GetBufferSize(); 
 
  LPBYTE* pBuffer = NULL; // Init pointer to buffer
 
  // Allocate a buffer only if buffer size > 0
  if(uBufferSize>0)
   pBuffer = new BYTE[uBufferSize];

      为了进一步了解如何正确的分配内存,你可以读下Secure Coding Best Practices for Memory Allocation in C and C++这篇文章。

  • Using Asserts Carefully

       Asserts用语调试模式检测先决条件和后置条件。但当我们编译器处于release模式的时候,Asserts在预编阶段被移除。因此,用Asserts是不能够检测我们的程序状态,错误代码如下:

 #include <assert.h>
  
  // This function reads a sports car's model from a file
  CVehicle* ReadVehicleModelFromFile(LPCTSTR szFileName)
  {
    CVehicle* pVehicle = NULL; // Pointer to vehicle object
 
    // Check preconditions
    assert(szFileName!=NULL); // This will be removed by preprocessor in Release mode!
    assert(_tcslen(szFileName)!=0); // This will be removed in Release mode!
 
    // Open the file
    FILE* f = _tfopen(szFileName, _T("rt"));
 
    // Create new CVehicle object
    pVehicle = new CVehicle();
 
    // Read vehicle model from file
 
    // Check postcondition 
    assert(pVehicle->GetWheelCount()==4); // This will be removed in Release mode!
 
    // Return pointer to the vehicle object
    return pVehicle;
  }

      看看上述的代码,Asserts能够在debug模式下检测我们的程序,在release 模式下却不能。所以我们还是不得不用if()来这步检测操作。正确的代码如下;

 CVehicle* ReadVehicleModelFromFile(LPCTSTR szFileName, )
  {
    CVehicle* pVehicle = NULL; // Pointer to vehicle object
 
    // Check preconditions
    assert(szFileName!=NULL); // This will be removed by preprocessor in Release mode!
    assert(_tcslen(szFileName)!=0); // This will be removed in Release mode!
 
    if(szFileName==NULL || _tcslen(szFileName)==0)
      return NULL; // Invalid input parameter
 
    // Open the file
    FILE* f = _tfopen(szFileName, _T("rt"));
 
    // Create new CVehicle object
    pVehicle = new CVehicle();
 
    // Read vehicle model from file
 
    // Check postcondition 
    assert(pVehicle->GetWheelCount()==4); // This will be removed in Release mode!
 
    if(pVehicle->GetWheelCount()!=4)
    { 
      // Oops... an invalid wheel count was encountered!  
      delete pVehicle; 
      pVehicle = NULL;
    }
 
    // Return pointer to the vehicle object
    return pVehicle;
  }
  • Checking Return Code of a Function 

        断定一个函数执行一定成功是一种常见的错误。当你调用一个函数的时候,建议检查下返回代码和返回参数的值。如下代码持续调用Windows API ,程序是否继续执行下去依赖于该函数的返回结果和返回参数值。

HRESULT hres = E_FAIL;
    IWbemServices *pSvc = NULL;
    IWbemLocator *pLoc = NULL;
    
    hres =  CoInitializeSecurity(
        NULL, 
        -1,                          // COM authentication
        NULL,                        // Authentication services
        NULL,                        // Reserved
        RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication 
        RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation  
        NULL,                        // Authentication info
        EOAC_NONE,                   // Additional capabilities 
        NULL                         // Reserved
        );
                      
    if (FAILED(hres))
    {
        // Failed to initialize security
        if(hres!=RPC_E_TOO_LATE) 
           return FALSE;
    }
    
    hres = CoCreateInstance(
        CLSID_WbemLocator,             
        0, 
        CLSCTX_INPROC_SERVER, 
        IID_IWbemLocator, (LPVOID *) &pLoc);
    if (FAILED(hres) || !pLoc)
    {
        // Failed to create IWbemLocator object. 
        return FALSE;               
    }
   
    hres = pLoc->ConnectServer(
         _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace
         NULL,                    // User name. NULL = current user
         NULL,                    // User password. NULL = current
         0,                       // Locale. NULL indicates current
         NULL,                    // Security flags.
         0,                       // Authority (e.g. Kerberos)
         0,                       // Context object 
         &pSvc                    // pointer to IWbemServices proxy
         );
    
    if (FAILED(hres) || !pSvc)
    {
        // Couldn't conect server
        if(pLoc) pLoc->Release();     
        return FALSE;  
    }
    hres = CoSetProxyBlanket(
       pSvc,                        // Indicates the proxy to set
       RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx
       RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx
       NULL,                        // Server principal name 
       RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx 
       RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
       NULL,                        // client identity
       EOAC_NONE                    // proxy capabilities 
    );
    if (FAILED(hres))
    {
        // Could not set proxy blanket.
        if(pSvc) pSvc->Release();
        if(pLoc) pLoc->Release();     
        return FALSE;               
    } 
  • Using Smart Pointers

       如果你经常使用用享对象指针,如COM 接口等,那么建议使用智能指针来处理。智能指针会自动帮助你维护对象引用记数,并且保证你不会访问到被删除的对象。这样,不需要关心和控制接口的生命周期。关于智能指针的进一步知识可以看看Smart Pointers - What, Why, Which? 和 Implementing a Simple Smart Pointer in C++这两篇文章。

       如面是一个展示使用ATL's CComPtr template 智能指针的代码,该部分代码来至于MSDN。

#include <windows.h>
#include <shobjidl.h> 
#include <atlbase.h> // Contains the declaration of CComPtr.
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | 
        COINIT_DISABLE_OLE1DDE);
    if (SUCCEEDED(hr))
    {
        CComPtr<IFileOpenDialog> pFileOpen;
        // Create the FileOpenDialog object.
        hr = pFileOpen.CoCreateInstance(__uuidof(FileOpenDialog));
        if (SUCCEEDED(hr))
        {
            // Show the Open dialog box.
            hr = pFileOpen->Show(NULL);
            // Get the file name from the dialog box.
            if (SUCCEEDED(hr))
            {
                CComPtr<IShellItem> pItem;
                hr = pFileOpen->GetResult(&pItem);
                if (SUCCEEDED(hr))
                {
                    PWSTR pszFilePath;
                    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
                    // Display the file name to the user.
                    if (SUCCEEDED(hr))
                    {
                        MessageBox(NULL, pszFilePath, L"File Path", MB_OK);
                        CoTaskMemFree(pszFilePath);
                    }
                }
                // pItem goes out of scope.
            }
            // pFileOpen goes out of scope.
        }
        CoUninitialize();
    }
    return 0;
}  
  • Using == Operator Carefully

       先来看看如下代码;

  CVehicle* pVehicle = GetCurrentVehicle();
   
  // Validate pointer
  if(pVehicle==NULL) // Using == operator to compare pointer with NULL
     return FALSE; 
 
  // Do something with the pointer
  pVehicle->Run();

      上面的代码是正确的,用语指针检测。但是如果不小心用“=”替换了“==”,如下代码;

 CVehicle* pVehicle = GetCurrentVehicle();
 
  // Validate pointer
  if(pVehicle=NULL) // Oops! A mistyping here!
     return FALSE; 
 
  // Do something with the pointer
  pVehicle->Run(); // Crash!!! 

        看看上面的代码,这个的一个失误将导致程序崩溃。

       这样的错误是可以避免的,只需要将等号左右两边交换一下就可以了。如果在修改代码的时候,你不小心产生这种失误,这个错误在程序编译的时候将被检测出来。

 

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

让你的代码变的更加健壮(Making your C++ code robust) 的相关文章

  • 如何检查 .NET 4.0 中的泛型参数是否是动态的

    我有课ObjectMapper
  • C++:获取注册表值仅给出第一个字符[重复]

    这个问题在这里已经有答案了 我试图从注册表中获取字符串值 但我只得到第一个字母 HKEY hKey char gamePath MAX PATH if RegOpenKeyEx HKEY CURRENT USER L Software Bl
  • 隐形打开的弹出窗口

    第二天就解决这个问题 要重现 请创建新的 WPF 应用程序 xaml
  • 禁用除滚动之外的 DataGridView

    我如何配置 datagridview 以便用户只能在行中移动并使用滚动 而没有其他 如果我禁用网格不允许我使用滚动 将您的 datagridview 设置为只读 这将禁用任何编辑 dataGridView1 ReadOnly true 在你
  • Windows 程序如何临时更改其时区?

    我写了一个函数来返回time t与给定日期的午夜相对应的值 当给定日期没有午夜时 它返回最早可用的时间 例如 当埃及进入夏令时时 这种情况就可能发生 今年 时间更改于 4 月 29 日晚上午夜生效 因此时钟直接从 23 59 转到 01 0
  • 从 ef core 的子集合中删除一些项目

    我有一个父表和子表 其中父表与子表具有一对多关系 我想删除一些子项 并且希望父项的子集合反映该更改 如果我使用删除选定的子项RemoveRange 那么子集合不会更新 如果我使用Remove从子集合中删除子集合然后 显然 它不如使用效率高R
  • 使用对象列表构建树

    我有一个带有属性 id 和parent id 的对象列表 我想建造一棵树来连接那些孩子和父母 1 个父对象可以有多个子对象 并且有一个对象将成为所有对象的祖先 实现该功能最快的算法是什么 我使用 C 作为编程语言 但其他语言也可以 像这样的
  • 如何使用 C# 代码使用超链接的 onClick 事件?

    我正在尝试为页面中的超链接添加条件 而不是仅仅使用特定的链接 例如 a href help Tutorial html Tutorial a 我想为不同的用户显示不同的页面 例如 如果用户以管理员身份登录 他们将看到与普通用户不同的链接 我
  • 在 C# 中生成随机值

    如何使用以下命令生成随机 Int64 和 UInt64 值RandomC 中的类 这应该可以解决问题 这是一个扩展方法 因此您可以像调用普通方法一样调用它Next or NextDouble上的方法Random目的 public stati
  • 使用 cudamalloc()。为什么是双指针?

    我目前正在浏览有关的教程示例http code google com p stanford cs193g sp2010 http code google com p stanford cs193g sp2010 学习CUDA 演示的代码 g
  • 为什么我的 ITexthandler 不工作?我正在尝试将 XML 解析为 ITextSharp 文档

    我正在使用 Visual Developer 2010 MVC 3 c 我正在尝试将 XML 解析为 iTextSharp 文档 如下所示 ITextHandler textHandler new ITextHandler doc text
  • 如果数组为空,LINQ 返回 null

    public class Stuff public int x other stuff 我有一个IEnumerable
  • C# - 命名空间内的类型声明

    在命名空间内而不是在类中声明类型的可能用途是什么 For ex namespace Test public delegate void Ispossible 这是有效的并且不会产生任何编译错误 但我无法想象为什么我们会以这种方式声明它而不是
  • 检索 Autofac 容器以解析服务

    在 C WindowForms 应用程序中 我启动一个 OWIN WebApp 它创建另一个类 Erp 的单例实例 public partial class Engine Form const string url http 8080 49
  • 如何在Linux上构建GLFW3项目?

    我已经使用 cmake 和 make 编译了 glfw3 和包含的示例 没有出现任何问题 开始编写我的第一个项目 作为 opengl 和 glfw 的新手 并且对 C 和 CMake 没有经验 我正在努力理解示例构建文件 甚至要链接哪些库和
  • C 中的静态和动态绑定(严格来说是 C,而不是 C++)是什么?

    我最初对发布这个问题感到担忧 以免它重复 但即使在谷歌搜索了许多关键字之后 我在 StackOverflow 上找不到任何解释 C 的静态和动态绑定的链接 尽管有 C 的问题和答案 但是都涉及classes以及显然不适合 C 的东西 Sta
  • 淹没在空无的海洋中

    我继承的一个应用程序跟踪对材料样品执行的实验室测试结果 数据存储在单个表 tblSampleData 中 其主键为 SampleID 并有 235 列代表潜在的测试结果 问题是每个样本仅执行少量测试 因此每行包含超过 200 个空值 实际上
  • 计算两个日期之间的工作日数?

    在C 中 如何计算business 或工作日 两个日期之间的天数 我以前曾经遇到过这样的任务 并且我已经找到了解决方案 当可以避免的时候 我会避免列举其间的所有日子 这里就是这种情况 正如我在上面的一个答案中看到的那样 我什至没有提到创建一
  • 网页执行回发时如何停止在注册表单上?

    我正在做我的最后一年的项目 其中 我在一页上有登录和注册表单 WebForm 当用户点击锚点时Sign Up下拉菜单ddlType 隐藏 和文本框 txtCustName txtEmail and txtConfirmPassword 显示
  • XmlDocument Save 使文件保持打开状态

    我有一个简单的 C 函数 可以创建一个基本的 XML 文件并保存 private void CreateXMlFile string Filename string Name string Company XmlDocument doc n

随机推荐

  • SpringBoot 学习摘要

    SpringBoot 今日目标 掌握基于SpringBoot框架的程序开发步骤 熟练使用SpringBoot配置信息修改服务器配置 基于SpringBoot的完成SSM整合项目开发 1 SpringBoot简介 SpringBoot 是由
  • Keil开发环境安装教程

    一 环境安装包 百度网盘链接 https pan baidu com s 1MVlP7yWM mDu Rf xFPFQ 二 安装步骤 1 双击安装mdk515 exe 默认即可 可更改路径 2 安装完成后 用管理员的身份运行keil 3 在
  • JavaScript设置innerHTML时出现“未知的运行时错误”--我也遇上了

    在Ajax里经常会通过innerHTML来改变界面 这个比使用DOM要简单一些 比如 element innerHTML something 不过 在IE中 有时候会出现 未知的运行时错误 unknown runtime error 而在f
  • C语言在读取txt类型文件中的汉字字符串出现乱码的解决办法

    题目 C语言在读取txt类型文件中的汉字字符串出现乱码的解决办法 以下是本篇文章正文内容 欢迎朋友们进行指正 一起探讨 共同进步 来自考研路上的lwj 一 前言 当我们在练习文件这一章节时 因为需要从文件中读取数据 有很多数据是中文形式的
  • 【 Spring Boot + MyBatis Plus + Druid】

    1 1 配置版本 具体使用到的各配置版本信息如下 JAVA17 SpringBoot 3 0 5 MyBatis Plus 3 5 3 1 Druid 1 1 14 MySql 5 0 8 因为以前装的老版本的mysql 用的mysql c
  • K210、Openmv与串行总线舵机通信(基于micropython)舵机驱动板和舵机控制板代码

    最近博主在使用幻尔公司 串行总线舵机时 想使用k210控制 openmv和k210都是micropython编写的所以这个代码是通用的 由于官方没有相关例程 树莓派的版本是python版本代码 用不了 特此分享一下控制代码 主要调用函数 a
  • Spring的应用上下文

    Spring提供了多个应用上下文 以下三个必须掌握 因为最容易遇到 1 ClassPathXmlApplicationContext 从classpath处获取xml文件来加载一个上下文 2 ClassPathXmlApplicationC
  • Java学生管理系统(简单版)

    步骤 A 定义学生类 B 学生管理系统的主界面的代码编写 C 学生管理系统的查看所有学生的代码编写 D 学生管理系统的添加学生的代码编写 E 学生管理系统的删除学生的代码编写 F 学生管理系统的修改学生的代码编写 A 定义学生类 Stude
  • HTML表格标签

    标签 与 标签的关系 是互相包裹的关系 或者说 里外嵌套的关系 快捷写法 p gt img 表格 由 table 标签来定义 每个表格均有若干行 由 tr 标签定义 每行被分割为若干单元格 由 td 标签定义 字母 td 指表格数据 tab
  • PHP实现AES-128-CBC加密+base64_encode

    AES加密 public function encrypt input 传false相当于base64 encode 编码了一次 encode base64 encode openssl encrypt input AES 128 CBC
  • SSH反向代理使用

    SSH反向代理 先说说什么是代理 源服务器由于各种原因无法访问目标服务器提供的服务 但是存在一个agent服务器 源服务器可以访问它 它可以访问目标服务器 那么源服务器的消息发给他 它在把请求转发给目标服务器 就间接的实现了源服务器访问目标
  • 进程(process)、线程(thread)、协程 (Coroutine) 的区别

    说到协程 Coroutine 我们必须提到两个名称相似的东西 在操作系统 os 级别 有进程 process 和线程 thread 两个 仅从我们常见的讲 实际的 东西 不说概念是因为这两个家伙的确不仅仅是概念 而是实际存在的 os的代码管
  • Short与Integer互转

    int 是4字节 short 是2字节的 如果将int Integer 转成short Short 那么必须强制转换 否则会报编译异常 但是 当int Integer 是一个final时 可以直接转换 不必强转 如 short t 1 正确
  • 【力扣1462】课程表(拓扑排序+bitset优化到O(n))

    题目描述 你总共需要上 numCourses 门课 课程编号依次为 0 到 numCourses 1 你会得到一个数组 prerequisite 其中 prerequisites i ai bi 表示如果你想选 bi 课程 你 必须 先选
  • android虚拟机启动不了,android虚拟机adb不能启动情况汇总

    在开启android虚拟机的时候 可能会遇到adb不能启动的问题 大概有以下几下情况 1 报错 BUILD FAILED D workspace ganji build xml 144 The following error occurre
  • 底量超顶量超级大黑马指标源码_底量超顶量超级大黑马指标源码

    主力买力度 LARGEINTRDVOL 100 VOL COLORRED 主力卖力度 LARGEOUTTRDVOL 100 VOL COLORGREEN 超B L2 VOL 0 0 VOL CAPITAL 大B L2 VOL 1 0 VOL
  • linux服务器高并发的极限和瓶颈

    最大并发数探究 Fancylee 2022 03 30 并发数 QPS 并发数 系统中同时存在的请求 同时处理中 QPS query per second 每秒的访问数 如何理解 将整个系统比喻成一个超市 QPS在超市门口测得的每秒钟有多少
  • U盘重装系统后可能遇到的问题

    一 重装系统 具体流程安装参考百度盘的使用优启通进行安装 安装完系统后可能会出现如下现象 一般台式机比笔记本简单 因为台式机不存在外围设备 例如触控板等 1 自己的优启通的万能驱动可能不具有相应的硬件驱动 在安装完系统后会提示 未找到相应的
  • 使用Hyperledger Composer将业务网络部署到单个组织的Hyperledger Fabric区块链上

    转载请标明出处 http blog csdn net qq 27818541 article details 78727076 本文出自 BigManing的博客 前言 先前准备 1先满足下列环境要求 2安装Hyperledger Comp
  • 让你的代码变的更加健壮(Making your C++ code robust)

    Making your C code robust Introduction 在实际的项目中 当项目的代码量不断增加的时候 你会发现越来越难管理和跟踪其各个组件 如其不善 很容易就引入BUG 因此 我们应该掌握一些能让我们程序更加健壮的方法