Antkillerfarm Hacking V8.0

language » C/C++编程心得(五)

2021-11-05 :: 7332 Words

数组定义时的赋值方法

struct mtd_partition {
	const char *name;		/* identifier string */
	uint64_t size;			/* partition size */
	uint64_t offset;		/* offset within the master MTD space */
};

static struct mtd_partition parts[] = {
      {name: "boot", offset: 0,           size:0x500000,},
      {name: "setting", offset: 0x500000, size:0x300000,},
      {name: "linux", offset: 0x800000,   size:0x500000,}, 
      {name: "config", offset: 0xd00000,   size:0x100000,},	
      {name: "rootfs", offset: 0xe00000,  size:0x3200000,},
      {name: "app", offset: 0x4e00000, size:0x800000,},

};

static struct resource pxa27x_resource_ohci[] = {
	[0] = {
		.start  = 0x4C000000,
		.end    = 0x4C00ff6f,
		.flags  = IORESOURCE_MEM,
	},
	[1] = {
		.start  = IRQ_USBH1,
		.end    = IRQ_USBH1,
		.flags  = IORESOURCE_IRQ,
	},
};

这些方法虽然不知道是C的哪个标准引入的,但是见到有代码这样用。

sanitizers

sanitizers是Google推出的调试工具。已集成到各主流编译器之中,无需安装。

代码:

https://github.com/google/sanitizers

参考:

https://zhuanlan.zhihu.com/p/257939964

编译器自带的调试神器sanitizers

spaceship operator

spaceship operator: <=>

C++20引入了spaceship operator:

  • 如果a<b,返回std::strong_ordering::less。
  • 如果a>b,返回std::strong_ordering::greater。
  • 如果a与b全等/相等,返回std::strong_ordering::equal。

这和strcmp的设计理念类似。

此外,还有关系运算符的重载推导,定义了<=>之后,其他诸如==!=<<=>>=的关系运算自动成立,无需再写相应的重载函数了。同理,如果定义了==,那么!=就自动成立了。

参考:

https://cloud.tencent.com/developer/article/1352324

C++20草案中的宇宙飞船运算符(<=>,spaceship operator)

线程安全注解

线程安全注解是现代C++开发的机制,可以在代码编译阶段检查锁的使用情况,从而发现线程同步的相关问题。该功能目前只有clang编译器支持。

https://blog.csdn.net/qq_32648921/article/details/109114887

线程安全注解

Implementation-defined、Unspecified、Undefined

Implementation-defined:C标准没有明确规定,但是要求编译器必须对此做出明确规定,并写在编译器的文档中。

Unspecified:C标准没有明确规定,编译器可以自己决定,不必写在编译器的文档中。

Undefined:C标准没规定怎么处理,编译器很可能也没规定。

https://www.cnblogs.com/MagicLetters/archive/2011/08/03/2126471.html

C语言中关于Implementation-defined、Unspecified和Undefined

在构造函数内部调用构造函数

struct CLS
{
    int m_i;
    CLS( int i ) : m_i(i){}
    CLS()
    {
        CLS(0);
    }
};

如上,在CLS()调用CLS(0);会创建一个新的临时对象,达不到重载的目的。

应该怎么做呢?new (this)CLS(0);

https://www.cnblogs.com/stemon/p/4834043.html

在构造函数内部调用构造函数

基类析构函数为什么是虚函数?

如果基类析构函数不是虚函数,则当通过父类指针或引用指向子类时,在调用析构函数时只调用父类的析构函数,所以只释放父类在堆区开辟的内存,子类在堆区开辟的内存没有被释放。

临时对象的生命周期

string test()
{
    return "abc";
}

int main()
{
    const char *p = test().c_str();
    cout << p << endl;

    return 0;
}

string("abc")是一个临时对象,在执行完const char *p = string("abc").c_str();这个语句后,临时对象就析构了,而不会等到块结束。

https://blog.csdn.net/stpeace/article/details/46461167

聊聊C++临时对象的析构时间点

switch…case

switch (reg) {
       case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR:
       return 1;
       case TAS5086_INPUT_MUX:
       case TAS5086_PWM_OUTPUT_MUX:
       return 4;
}

可以用上面的办法,少写一些case语句。

https://www.zhihu.com/question/300975864

为什么很多程序员不用 switch,而是大量的 if…else if …?

uint8_t

对于要求严格位宽的场合,应用程序可以使用uint8_t,uint16_t,uint32_t来获得移植时的一致性。它的头文件是inttypes.h。

并发与多线程

https://blog.csdn.net/qq_38231713/category_10001159.html

一个C++11并发与多线程的专栏

K&R风格

在C语言的函数定义上,我们通常用的函数定义方式为ANSI C的函数定义方式。但是在C语言之父创立C语言之时,函数的定义形式并非现在我们所见到的形式。

这种风格被称为K&R风格,多见于一些历史悠久的项目或者老的书籍中。出于兼容性考虑,现代的C编译器仍然支持K&R风格。

记得大学学C语言,用的是谭浩强的书。当时,授课老师看不上谭的书,于是另外推荐了一本,那本书更古老,用的就是所谓的K&R风格。。。但在实际工作中,从来没见过K&R风格的代码。。。

详见:

http://blog.chinaunix.net/uid-7426920-id-2627743.html

ANSI和K&R两种函数定义风格

SFINAE

SFINAE:Substitution failure is not an error

template <
    typename scalar_t,
    typename index_t,
    typename std::enable_if_t<!std::is_same_v<c10::Half, scalar_t> && !std::is_same_v<c10::BFloat16, scalar_t>>* =
        nullptr>
__device__ __forceinline__ void fastSpecializedAtomicAdd(
    scalar_t* tensor,
    index_t index,
    const index_t numel,
    scalar_t value)

第三个模板参数用于在编译时对模板参数进行约束。它的作用是限制模板参数scalar_t的类型,确保它不能是c10::Half或c10::BFloat16。

https://en.cppreference.com/w/cpp/language/sfinae

官方文档

https://zhuanlan.zhihu.com/p/21314708

C++模板进阶指南:SFINAE

CRTP

CRTP:Curiously Recurring Template Pattern是C++模板编程中的一种惯用法。基本特征表现为:基类是一个模板类;派生类在继承该基类时,将派生类自身作为模板参数传递给基类。

template <typename ChildType> struct VectorBase {
  ChildType &underlying() { return static_cast<ChildType &>(*this); }
  inline ChildType &operator+=(const ChildType &rhs) {
    this->underlying() = this->underlying() + rhs;
    return this->underlying();
  }
};
struct Vec3f : public VectorBase<Vec3f> {
  float x{}, y{}, z{};
  Vec3f() = default;
  Vec3f(float x, float y, float z) : x(x), y(y), z(z) {}
};
 
inline Vec3f operator+(const Vec3f &lhs, const Vec3f &rhs) {
  Vec3f result;
  result.x = lhs.x + rhs.x;
  result.y = lhs.y + rhs.y;
  result.z = lhs.z + rhs.z;
  return result;
}

本质上CRTP中的多个派生类并不是同一个基类,只是共用一部分模板代码而已。它省去了动态绑定、查询虚函数表带来的开销。

打印vector的N种方法

auto vec = make_index_vector(std::make_index_sequence<10>());
for(auto i : vec) {std::cout << i << ' ';}
std::cout << std::endl;
std::iota(vec.begin(), vec.end(), 999);
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
std::for_each(std::begin(vec),std::end(vec),[](int n){std::cout<<n<<" ";});
std::cout << std::endl;

malloc与calloc

两者都是动态分配内存。

主要的不同:malloc不初始化分配的内存,已分配的内存中可以是任意的值。calloc初始化已分配的内存为0。

次要的不同:calloc返回的是一个数组,而malloc返回的是一个对象。

常用的malloc实现主要有:

  • dlmalloc。由Doug Lea从1987年开始编写。
  • ptmalloc(pthread malloc)。glibc使用的malloc版本。dlmalloc的一个变种。
  • tcmalloc。Google开源的一个内存管理库。
  • Jemalloc。facebook推出的,最早的时候是freebsd的libc malloc实现。

在多线程环境使用tcmalloc和jemalloc效果非常明显。

当线程数量固定,不会频繁创建退出的时候, 可以使用jemalloc;反之使用tcmalloc可能是更好的选择。

malloc是用户层面而不是系统层面的东西,系统调用里并没有malloc。

https://www.cnblogs.com/cthon/p/10563946.html

内存优化总结:ptmalloc、tcmalloc和jemalloc

RVO

std::vector<int> return_vector(void)
{
    std::vector<int> tmp {1,2,3,4,5};
    return tmp;
}
std::vector<int> rval_ref = return_vector();

如果按照一般的返回值传递的规则,tmp作为局部变量是传不到外面去的,因此返回的时候,会在外部创建一个同类型的变量,并将tmp的内容复制过去。从而带来了一次复制的开销。

RVO(Return Value Optimization)就是用来优化这种情况的。上述代码执行RVO之后,就等价于:

void return_vector(std::vector<int>& tmp)
{
    tmp = std::vector<int>({1,2,3,4,5});
}
std::vector<int> rval_ref;
return_vector(rval_ref);

https://www.zhihu.com/question/27000013

什么时候应当依靠返回值优化(RVO)?

关于引用的一个常见错误

class Base
{
  public:
    void something(Base& b){}
};

int main()
{
  Base b;
  b.something(Base());
  return 0;           
}

上面的代码在编译时,会出现如下错误信息:

abc.cpp:12:20: error: no matching function for call to ‘Base::something(Base)’
abc.cpp:12:20: note: candidate is:
abc.cpp:6:7: note: void Base::something(Base&)
abc.cpp:6:7: note: no known conversion for argument 1 from ‘Base’ to ‘Base&’

这是由于Base()生成的是临时变量,将之赋值给一个non-const的引用是不行的。

解决方法是

void something(const Base& b){}

可以参看下文:

http://stackoverflow.com/questions/20247525/about-c-conversion-no-known-conversion-for-argument-1-from-some-class-to

添加废弃提示

有些类库出于兼容性的考虑,仍然保留了对旧函数的支持。但是继续使用这些函数,显然不是作者的初衷。因此,有必要在编译时,给出废弃的提示。

近日浏览cocos2d-x v3的代码,发现可以这样做:

宏定义:

#if defined(__GNUC__) && ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)))
#define CC_DEPRECATED_ATTRIBUTE __attribute__((deprecated))
#elif _MSC_VER >= 1400 //vs 2005 or higher
#define CC_DEPRECATED_ATTRIBUTE __declspec(deprecated)
#else
#define CC_DEPRECATED_ATTRIBUTE
#endif

宏使用:

CC_DEPRECATED_ATTRIBUTE static TextureCache * getInstance();

Fork me on GitHub