8

编程小知识 之 杂记两则

 3 years ago
source link: https://blog.csdn.net/tkokof1/article/details/106451802
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

编程小知识 之 杂记两则

tkokof1 2020-05-31 10:10:49 342

本文简述了之前遇到的两则编程杂记

隐式字符串字面量合并

之前看到一段 C/C++ 代码:

void func(const char* str) {
    // implementation ...
}

func("string" "params");

代码定义了一个参数为字符串指针的函数 func,调用时却使用了 “string” “params” 这种字符串连写的方式(中间没有逗号分隔符),初看时我一直怀疑调用代码存在"笔误",甚至认为这种写法会引起编译错误,但实际上, func(“string” “params”) 这种调用方式是正确无误的.

因为在编译期,像 “string” “params” 这种连写的字符串字面量会被合并为单个字符串字面量,所以上面 func(“string” “params”) 的调用代码经过编译后其实等价于 func(“stringparams”),这种被称为 隐式字符串字面量合并 的特性有两个注意点:

  • 合并发生在编译期,所以合并对运行时性能没有影响
  • 合并仅针对字符串字面量

所以如果你尝试对字符串变量进行隐式合并,那么等待你的就是编译错误了:

void func(const char* str) {
    // implementation ...
}

const char* params = "params";
// error here ...
func("string" params);

隐式字符串字面量合并的一大作用就是方便我们分隔大段的字符串代码文本,之前我们一般都会使用 行接续符(\) 来分隔长字符串:

const char* long_long_str = "long\
long\
string";

这种方式比较繁琐,格式上也不美观,使用隐式字符串字面量合并的话则会改善很多:

const char* long_long_str = "long"
		                    "long"
                            "string";

try … catch 效率之殇

之前简单用 C# 写了一些用于读取 excel 配置的代码,其中有段逻辑用于处理以下的功能需求:

excel 单元格中一般配置为整型值,但是也有情况会配置为字符串(譬如配置为"是",而"是"最终会被映射为 1(这种映射关系事先会有定义),总的来说,支持将整数值配置为字符串是为了方便配置人员进行更直观的配置)

相关(简化)代码如下:

var cellStr = getCellStr();
var cellNum = 0;

try
{
    // first try parse int
    cellNum = int.Parse(cellStr);
}
catch (...)
{
    // then try map str to int
    if (!TryMapCellStr(cellStr, out cellNum)) 
    {
        // config error    
    }
}

结果代码运行之后奇慢无比,简单看了一下 profile 发现消耗最大的就是这段 try … catch 代码,自己摸索了一番,大概确定了几点原因:

  • 首先, int.Parse 的效率不高,可以考虑使用 int.TryParse 或者根据项目情况定制自己的 Parse 代码.
  • 再者, 应该也是最根本的一点,就是运行使用的 excel 表格中有大量使用字符串定义的整数值,遂而不断触发 try … catch 进行调用栈的记录和回退(unwind)操作,一般程序可能对少量的 try … catch 性能消耗并不敏感,但是大量 try … catch 的性能消耗非常巨大.

知道了原因,上述代码的调整方法也就明了了,我们首先尝试字符串映射(用以规避在 catch 块中进行正常的程序流处理)即可(使用 int.TryParse 直接规避 try … catch 应该是更好的一种做法):

var cellStr = getCellStr();
var cellNum = 0;

try
{
    // first try map str to int
    if (!TryMapCellStr(cellStr, out cellNum)) 
    {
        // then try parse int
        cellNum = int.Parse(cellStr);
    }
}
catch (...)
{
    // config error
}

try … catch 尽量不要用于正常的程序流控制


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK