之后,我们将比较三种主要编译器(GCC、Clang 和 MSVC)在这方面的表现,并讨论一些潜在的改进或解决方案。...不提倡使用 std::vector,而提倡使用 T*,或者至少通过指针进行迭代(即通过 std::vector::data),而不是通过迭代器。...MSVC 没有与 -Og 相对应的东西,而大多数游戏开发者使用 MSVC 作为他们的主要编译器! 即使 -Og 无处不在,但它仍然不及 -O0——对于高效的调试会话来说,它可能仍然内联了太多代码。...我们可以说函数模板不是为强制转换和位操作创建轻量级抽象的正确模型,类模板和轻量级类型,如 std::vector::iterator,也是如此。...最后,不要忘了,直接解决这个问题,而不是绕过它,我们还可以从中获得其他好处,比如更快的编译。
谨慎明智地使用模板,不仅仅是因为可以使用。提示:使用编译自动测试可以查看测试中的所有编译器是否支持C++功能。...即使为共享库定义了初始化程序的执行时间,在插件中移动该代码或静态编译库时也会遇到麻烦: /* 全局作用域 */ static const QString x; /* 错误: 需要运行默认构造函数来初始化...*/ static QString *ptr = 0; /* 指向对象的指针是ok的, 不需要运行代码来初始化ptr。...避免的操作 不要继承模板/工具类 由于析构函数不是virtual,这会导致潜在的内存泄漏问题。 这些符号没有被导出(大部分是内联的),会导致报符号冲突的编译错误提示。...因为是GCC 4.7和更早版本存在一个错误,需要捕获此错误,但如果您这样做,则Clang 5.0和更高版本将产生警告: void Foo::something() { ...
一、前言 二、问题描述 三、把类型改为 void 指针类型 四、总结 一、前言 昨天在编译代码的时候,之前一直OK的一个地方,却突然出现了好几个 Warning!...我们用其他的编译器试一下: (1) clang $ clang main.c -m32 -o main -I./ main.c:18:20: warning: incompatible pointer...三、把类型改为 void 指针类型 把 struct _Data2_ 中的 next 成员,改为 指向 void 型的指针,然后在 main 函数中操作它。...这又回到了指针的本质: 指针就是一个地址,至于如何来解释这个地址中的内容,这是由定义这个指针时所指定的数据类型来决定的 结合代码来看:虽然d2.next是一个 void 型指针,但是它的确存储了一个 地址...不过,从中我们也看到了一个现象:gcc编译器在面对结构体时,主要关心的是结构体在内存空间中所占用的空间大小,对其内部指向结构体类型的指针,并没有严格的检查是否存在,g++ 在这一点就做的严谨一些了。
这样的话,如果在头文件中实现了某个函数,而该函数又被多个源文件使用,那么在编译时正常,而在链接时就会报错,某些函数多次重复定义。...// 错误,实参不是含有 10个整数的数组 print(j); // 错误,实参不是含有 10个整数的数组 使用 main函数处理命令行选项时,通常会写成下列两种形式...在上面两个表达式中,argv是一个数组,它的元素是指向 C风格字符串的指针,而 argv又可以看成是指向首元素的指针,因此 argv就是一个二级指针,所以也就有了第二个表达式的写法。...10.initializer_list提供了对一系列相同类型元素的轻量级存储和访问的能力,值初始化后列表中的元素永远是常量值。在拷贝或赋值时,执行的也是“类指针拷贝”,原始列表和副本共享元素。...基于这个原因,内联函数和 constexpr函数通常定义在头文件中。也因为它们可以多次定义,所以即使定义在头文件中,链接时也不会出现多次定义的错误,而普通函数这样做就会出错。
++代码转换成最终形式的C++代码,有点类似于C/C++的预处理器一样,把一些宏代码替换成真实的代码一样,但它的功能更进一步也更强大,该工具支持基于范围的循环、结构化绑定、生成默认构造函数、初始化列表、...编译时打印编译器肯定是知道变量的类型的,但是它没法直接告诉你,有一个可以让编译器告诉你的办法,就是编译发生错误时编译器在报告的错误信息中肯定会提到导致此错误的类型,因此我们可以声明一个如下的模板:template...所以我们想要查看哪个变量的类型,只要将这个变量的类型作为模板的形参去实例化它,就会导致一个错误,在编译器给出的错误信息里就会显示出这个变量的具体类型,如下所示:const int x1 = 1;auto...这时可以采用另外一种手段来输出变量的类型,跟上小节中的例子一样借助模板的技术,实现一个模板函数,在模板函数中利用编译器提供的宏,把这个函数的原型打印出来,函数原型中就包含了函数的参数个数及其类型,这个宏由于不是...C++标准中定义的,是由各编译器扩展的,因此名称不一样,在GCC/Clang中是__PRETTY_FUNCTION__,在微软的MSVC中是__FUNCSIG__,如下代码:#include <iostream
静态分析工具能够在代码未运行的情况下分析源代码,发现代码中的bug。在C/C++程序中,静态分析工具可以发现程序错误,如空指针取消引用、内存泄漏、被零除、整数溢出、越界访问、初始化前使用等。...我第一次也错误的认为这段代码会打印“ON”。 如果我们用Clang编译,又有什么结果呢?...Clang是一个优秀的静态分析器,能够分析代码中潜在的问题。对于上面的问题,GCC 在编译时加上-Wall 和-Wpedantic编译选项也可以分析出bug。...和GCC的主要任务是编译代码,静态分析也并不是在每次编译时都需要,而且编译器在做静态分析时需要花费大量的时间。...比如空指针,除零,整数溢出,无效的移位操作,无效的转换,STL的无效用法,内存管理,空指针引用,越界检查,未初始化的变量,未使用或者重复的代码等。
考虑到凡事不可把话说的太绝,我顿了顿补充道:“是不是你忘记打开-fms-extensions了?这是常见错误。” “加了,因为这的确是你的代码出现编译故障的常见原因,所以我第一时间就处理了……呐!...实践中经常会发现,clang比gcc的语法要严格,gcc很多时候在语法风格上更加“放飞自我”,因此clang中可以通过编译的代码,怎么会在GCC中无法编译通过呢? “Bug!一定是编译器Bug!”...,clang是正常的认可了0x12345678作为逗号表达式的返回值; clang并没有认为这个表达式不是常量; clang也没有认为这个静态常量 s_wTest 的初始化有什么不妥; 如果觉得这个warning...翻译一下就是: 常量表达式不应包含赋值、递增、递减、函数调用或逗号运算符…… 问题似乎是水落石出了:这的确是一个由C99明确规定的“feature”而非编译器的"Bug"。...clang和IAR显然因为某种原因(我猜是为了方便)在编译C代码(而非C++代码)时也同时移除了这一限制——这在某种程度上误导我们得出了“好学生GCC有Bug”的错误结论。
有些程序员使用'restrict'关键字来通知编译器指针是分离的,但是在我们的示例中,循环向量器无法知道指针A和B是唯一的。...这个循环使用C++迭代器,这些指针是指针,而不是整数索引。循环矢量器检测指针感应变量,并对该循环进行矢量化。这个特性很重要,因为许多C++程序使用迭代器。...还可以在对结构成员的指针访问上添加运行时检查。 支持许多变体,但是有些依赖于未定义行为被忽略的变体(就像其他编译器一样),仍然没有被矢量化。...有关这些函数的列表,请参见下表。 ? 请注意,如果库调用访问外部状态(如“errno”),优化器可能无法将与这些内部函数对应的数学库函数矢量化。...当向量化和展开因子较大时,行程计数较小的循环可能会将大部分时间花费在标量(而不是矢量)代码中。
inline变量出现后,我们可以直接将全局变量定义在头文件中,而不用担心出现redefine的错误信息。...有兴趣的朋友可以看看下面两篇文章: 《c++ inline variable 内联变量 c++17》 《GCC,Clang 在C模式,较低优化等级下,链接器对内联函数报未定义错误,为什么?》...,结构化绑定的结果并不是变量,c++标准称之为名字/别名,这也导致它们不允许被lambda捕获,但是gcc并没有遵循c++标准,所以以下代码在gcc可以编译,clang则编译不过。...常用于可能失败的函数的返回值中,比如工厂函数。在C++17之前,往往使用T*作为返回值,如果为nullptr则代表函数失败,否则T*指向了真正的返回值。...需要注意的是,c++17只提供了一个库级别的variant实现,没有对应的模式匹配(Pattern Matching)机制,而最接近的std::visit又缺少编译器的优化支持,所以在c++17中std
印象最深的是关于函数返回局部指针变量的处理: eg: char * fun() { char *p = "hello"; return p; } 这个函数输入到VS里面是完全没有警告和错误的...但是实际上CPP是不建议把字符串常量赋值给非常量字符指针的(C语言中不会报错)。同样的代码在VScode选择clang编译就会出现警告。...还有:return p;这个是有内存风险的,局部指针创建的对象在栈上,返回p的时候函数调用完毕,指针被赋值给其他全局变量的话,栈上空间就会被释放掉,所以这个也是有警告的。...重启之后,Win+r输入cmd打开命令行,分别输入gcc和clang后回车。 ? 出现如上所示的提示认为安装成功。 法2....但貌似clang默认开启而gcc不接受此参数 "--target=x86_64-w64-mingw", // clang的默认target为msvc,不加这一条就会找不到头文件
1、什么是别名(alias) 在 C 和 C++ 中,当多个左值 lvalue 指向同一个内存区域时,就会出现别名(alias)。...类型双关经常应用在编译器、序列化、网络传输等领域。 类型双关一般做法是通过别名(alias)来实现,通过获取对象的地址,将其转换为我们想要重新解释的类型的指针,然后访问该值。...int a;float *ptr = (float *)&a;printf("%f\n", *ptr); 2、什么是严格别名 严格别名就是编译器当看到多个别名(alias)时,会在一定规则下默认它们指向不同的内存区域...), &a); 违背 strict aliasing,编译器认为 argv1,argv2 指向不同的内存区域 ,为未定义的行为(UB,Undefined Behavior)。...在 C11 标准的 3.4.3 小结对未定义行为进行了明确定义: 未定义行为:当使用不可移植或者错误的程序/错误的数据时,将导致不可预期的结果。典型例子就是整数溢出时的行为。
当程序员看到使用const修饰的代码时就知道不应该修改对应对象的值,而编译器则会强制实施这个约束,任何违反这个规定的代码会在编译期间报错。...对于内置类型,还是建议使用传值方式,因为引用在底层一般是使用指针来实现,对于内置类型反而更浪费资源,而且编译器也可以做优化。对于自定义类型,一般情况下建议定义成const的引用,而不是普通的引用。...const的,即在成员函数的参数列表之后加上const关键字,表示this是一个指向常量的指针,我们也这个成员函数为常量成员函数。...常量对象只能调用常量成员函数,也就是说不允许通过常量成员函数修改对象内部的数据或者状态,但有可能出现这种情况,常量成员函数虽然符合要求,因此欺骗过编译器而通过编译。...,编译器利用实参来推断出模板实参,根据模板实参来生成一个函数实例。
使用nullptr C++11引入了nullptr表示空指针,应该用来代替0或NULL来指示空指针。 注释 注释块应该使用//,而不是/* */,使用//可以更容易的在调试时注释掉代码块。...用大括号初始化默认值 用大括号初始化不允许在编译时截断数据长度。.... // 除非有明确的理由,否则优先使用{}初始化,而不是=。 忘记初始化成员会导致未定义行为错误,而这些错误通常很难发现。 如果成员变量在初始化后不会更改,则将其标记为const。...,否则不提供编译器可以提供的任何函数(拷贝构造函数、拷贝赋值操作符、移动构造函数、移动赋值操作符、析构函数)。...目标是让编译器提供在添加更多成员变量时自动维护的最佳版本。 这篇文章介绍了这一原则的背景,并解释了几乎可以覆盖所有情况的实现技术: C++'s Rule of Zero[19]。
函数指针指向的是特殊的数据类型,函数的类型是由其返回的数据类型和其参数列表共同决定的,而函数的名称则不是其类型的一部分。...然而当实现该模板的.cpp文件中没有用到模板的实例时,编译器懒得去实例化,所以,整个工程的.obj中就找不到一行模板实例的二进制代码,于是连接器也黔驴技穷了。...因为在编译时模板并不能生成真正的二进制代码,而是在编译调用模板类或函数的CPP文件时才会去找对应的模板声明和实现,在这种情况下编译器是不知道实现模板类或函数的CPP文件的存在,所以它只能找到模板类或函数的声明而找不到实现...编译器会一一操作初始化列表,以适当顺序在构造函数之内安插初始化操作,并且在任何显示用户代码前。...sizeof(p),p 为指针得到的是一个指针变量的字节数,而不是p 所指的内存容量。 3) 编译器为了简化对数组的支持,实际上是利用指针实现了对数组的支持。
等分配的动态内存范围 读/写不适当的栈中内存块 内存泄漏,指向一块内存的指针丢失 不正确的malloc/free或new/delete匹配 memcpy()相关函数中的dst和src指针重叠问题 ---...- Memcheck检查步骤及注意事项 在编译程序的时候打开调试模式(gcc编译器的-g选项),以便显示行号,编译时去掉-O1 -O2等优化选项,检查的是C++程序的时候,考虑加上选项:-fno-inline...---- 结果分析 Valgrind(memcheck)包含这7类错误 illegal read/illegal write errors —— 非法读取/非法写入错误 use of uninitialised...,还有机会使用或者释放,指针指向的动态内存还没有被释放就退出了 Definitely lost —— 确定的内存泄露,已经不能够访问这块内存 Indirectly lost —— 指向该内存的指针位于内存泄露处...Possibly lost —— 可能的内存泄露,仍然存在某个指针能够访问某块内存,但该指针指向的已经不是该内存首位置 Suppressed —— 某些库产生的错误不予以提示,这些错误会被统计到suppressed
普通引用不能初始化为常量,const引用可以。 typedef用来定义类型的同义词。 头文件用于声明,不是定义,因而可以出现多次。定义的语句不应该放在头文件里,出现两次会导致多重定义链接错误。...静态变量只在初次调用时初始化,static size_t ctr=0只执行一次。 内联函数避免函数调用的开销:编译时展开为函数体中的表达式,免去函数调用的寄存器保存恢复、复制实参跳转等。...初始化const或引用类型或没有默认构造函数的类类型数据成员的唯一机会是构造函数的初始化列表【冒号开始,逗号分隔】。初始化顺序由定义顺序决定,而不是初始化列表顺序。...即使定义了其他构造函数,也会合成复制构造函数【能够复制类中的数组】。类成员有指针一般需要显示定义复制构造函数。 声明而不定义成员函数是合法的,但是使用将导致链接失败。...派生类指针的静态类型和动态类型不一致时【基类指针指向派生类是时】,为保证删除指针调用合适的析构函数【多态】,基类析构必须为virtual。
即可 以下是不同的编译器对 C++11 语法的支持情况(绿色表示最低支持版本,红色表示不支持) 主流的编译器有:GCC、Clang、MSVC,其中 GCC 就是在 Linux 中使用的编译器,基本上...GCC 4.6 及后续版本就能对 C++11 进行很好的支持,而 MSVC 是微软 VS 系列的编译器,从 VS 2015 及后续版本对 C++11 语法支持较好 推荐使用 VS 2019 或 VS...,这对于编码时初始化是十分友好的 2.1.对于内置类型 首先需要明白,为了适应 泛型编程,C++ 中的内置类型(比如 int、double 等)就已经全部配备了 构造函数,方便在进行模板传参时,传递默认构造值...int m, int y) :_day(d), _month(m), _year(y) {} 接下来同样的代码,尝试编译,结果出现了错误 现在的情况是 d1 列表初始化失败,d2 列表初始化成功 这是因为...d1 是由 构造 + 赋值 优化后进行的构造,而 explicit 关键字可以杜绝编译器这种 隐式 优化行为,编译器无法优化,也就无法构造 d1 了;而 d2 相当于直接调用了 拷贝构造函数,不受优化的影响
我们已经学习了对象的初始化、内存对齐等内容。这篇文章将深入学习探究对象的本质、对isa进行分析。 学习对象本质之前,先引入一个工具clang。...一.clang 1.什么是clang Clang是⼀个C语⾔、C++、Objective-C语⾔的轻量级编译器。源代码发布于BSD协议下。...))来修饰函数),其⽬标(之⼀)就是超越GCC。...对象本质总结 通过工具clang,编译生成的cpp文件,我们可以发现,对象实质是一个结构体。在OC层,NSObject是大多数类的根类,而objc_object可以理解为就是c\c++层面的根类。...,0:纯isa指针,1:不⽌是类对象地址,isa中包含了类信息、对象的引⽤计数等。
运行在编译阶段,只能看到编译时的常数和类型,看不到运行时的变量、指针、内存数据等, (__visibility__("default"): 某个符号是否导出 试想这样的情景,程序调用某函数A,A函数存在于两个动态链接库...赋值初始化或者花括号初始化(初始化列表、Initializer list),变量右边必须要有一个表达式(简单、复杂都可以) 很容易理解,只是声明,没有赋值,无法推导类型。...指针是内存地址,引用是变量别名,指针可以是空,而引用不能为空(引用必须初始化,否则编译失败) 引用是通过指针常量实现的 指针完全映射了计算机硬件,操作效率高,是 C++ 效率高的根源。...不会带病工作 使用范围更广,比如没有返回值的函数,出现异常 使用 noexcept 修饰不会抛出异常的函数,方便编译器做优化: noexcept 的真正意思是:“我对外承诺不抛出异常,我也不想处理异常...lambda 保存了定义时捕获的外部变量,就可以跳离定义点,把这段代码“打包”传递到其他地方去执行 在 C++ 里,每个 lambda 表达式都会有一个独特的类型,而这个类型只有编译器才知道,
构造函数使用成员初始化列表来赋值,而不是在构造函数里去赋值(会导致赋值两次,浪费了),列表的排列次序保持和class中声明次序一致。...,且只有两个指针都销毁时才delete,而auto_ptr只会保证一个指针有效,在复制时,原指针会指向null。...不要只因为模板函数出现在头文件,就将它们声明为inline,模板函数和inline并不是必须结对出现的。...做法是声明一个泛化构造函数,也就是定义一个模板构造函数,接收模板参数,声明一个指向的真实对象指针,声明一个获取该对象指针的get函数,用该get函数放在初始化列表中来构造模板类。...这样就能使用一种类型特化出的自制智能指针来构造另一种类型特化出的自制智能指针了。同时,在初始化列表中编译器会为你检查是否允许该类型转换(比如只允许子类往父类的转换,不能相反)。
领取专属 10元无门槛券
手把手带您无忧上云