做一个企业网站设计长春网站建设那家好

pingmian/2025/10/15 22:06:32/文章来源:
做一个企业网站设计,长春网站建设那家好,aspsql server典型网站建设案例 源码,壹起航网络推广的目标C入门 文章目录 C入门框架命名空间 namespace #xff08;不常用#xff09;命名空间的使用方式#xff08;三种#xff09;using namespace std;\iostreamcoutendlcincout的使用命名冲突缺省参数#xff08;省钱的省#xff09;缺省参数分类全缺省参数半缺省参数…C入门 文章目录 C入门框架命名空间 namespace 不常用命名空间的使用方式三种using namespace std;\iostreamcoutendlcincout的使用命名冲突缺省参数省钱的省缺省参数分类全缺省参数半缺省参数缺省参数函数的使用 函数重载函数重载的要求函数重载的使用函数重载的面试题通过这里就理解了C语言没办法支持函数重载因为同名函数没办法区分。而C是通过函数修饰规则来区分只要参数不同修饰出来的名字就不一样就支持了重载。 另外我们也知道了为什么函数重载要求参数不同而跟返回值没关系。。。 extern “C”是干啥的面试题缺省参数会影响函数重载吗面试题引用别名引用的概念格式: 类型 引用变量名(对象名) 引用实体引用的特性常引用 引用的使用总结引用的不安全总结 引用的好处传值返回和传引用返回的差别传值做参数和传引用做参数的差异引用作返回值的适用场景引用和指针的区别 内联函数inline内联函数的特性 宏的优缺点面试题C有哪些技术替代宏auto关键字C11auto的使用规则auto最大的作用 基于范围的for循环(C11)范围for的使用条件 指针空值nullptr(C11) 框架 #include iostream using namespace std;int main() {return 0; }命名空间 namespace 不常用 定义命名空间需要使用到namespace关键字后面跟命名空间的名字然后接一对{}即可{}中即为命名 空间的成员。 命名空间的{}的后面不需要加分号跟结构体不一样 //1. 普通的命名空间 namespace N1 // N1为命名空间的名称 {// 命名空间中的内容既可以定义变量也可以定义函数int a;int Add(int left, int right){return left right;} } //2. 命名空间可以嵌套 namespace N2 {int a;int b;int Add(int left, int right){return left right;}namespace N3{int c;int d;int Sub(int left, int right){return left - right;}} } //3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。 namespace N1 {int Mul(int left, int right){return left * right;} }这个命名空间可以放变量进去也可以放函数进去。就像结构体一样但是又不太一样因为不用在main主函数里面再次创建结构体了。这个命名空间拿起来就能用。 同一个工程里面相同名称的命名空间会自动合并。在不同的命名空间里面可以建立相同的变量和相同名称的函数函数的功能完全不同的那种 命名空间的使用方式三种 因为命名空间里面的成员不可以直接使用 namespace N {int a 10;int b 20;int Add(int left, int right){return left right;}int Sub(int left, int right){return left - right;} }像这种直接使用命名空间里面的成员就会报错因为编译器无法识别 int main() {printf(%d\n, a); // 该语句编译出错无法识别areturn 0; }正确方式 加命名空间名称及作用域限定符 这个作用域就是变量的来源比如上面的例子的命名空间N 而作用域限定符就是 :: ,左边是作用域右边是作用域里面的成员变量。 作用域名::成员名 int main() {printf(%d\n, N::a);return 0; }使用using将命名空间中成员引入 using N::b; 如果经常使用这个变量b 通过上面这种方式这个b就是一个全局变量了。就不用再次声明b是来源于哪一个命名空间了。 using N::b; int main() {printf(%d\n, N::a);printf(%d\n, b);return 0; }使用using namespace 命名空间名称引入 能把命名空间的一个成员拿出来作为全局变量所以也能把这个命名空间全部拿出来当成全局变量。 怎么说呢感觉你在命名空间里面定义变量再解开这个命名空间还不如直接创建全局变量呢。 using namespce N; int main() {printf(%d\n, a);printf(%d\n, b);Add(10, 20);return 0; }using namespace std; 关于using namespace std;学了命名空间这个概念之后就会比较好解释了。 首先std是c的库 那么这句话就是把c库里面的所有东西放到这个工程里面了 也就是命名空间的第三种使用方式。 这个方式也是相当于把std这个库全展开了好处就是不用再声明变量是来自std库了坏处就是再想命名的时候就会跟库里面的命名冲突了 因为它是std这个命名空间里面的变量或者函数啊什么的。你要是还想再命名一个同名的有冲突的函数也可以自己重新定义一个命名空间。因为命名空间里面的变量不会互相冲突 iostream 对于这个头文件 io stream 它是c的输入流和输出流。就像c语言的stdio.h 不过在c语言中printf,scanf这些函数是stdio.h这个库里面的函数。 在c中iostream这个库也有自己的函数来实现输入和输出的功能 对于一些特别特别老的编译器VC6.0这个头文件也有这种写法iostream.h,因为实在是太老了。而且新版编译器都不支持这种写法尽量忽略 cout 这个函数是c中iostream库的输出函数就跟printf一样。但是这个函数来自std这个命名空间。所以想要使用这个函数必须要带iostream头文件。关于std命名空间有那三种方式如下 提前声明整个std命名空间 #include iostream using namespace std; // 有这句话int main() {cout hello World;return 0; }给cout加上命名空间的作用域限制符:: #include iostream //using namespace std; int main() {std::cout hello World;// 作用域限制符::return 0; }endl 这个函数跟cout的来源一样iostream库的std命名空间的函数。 作用换行 #include iostream using namespace std; // 包整个std命名空间int main() {cout hello Worldendl;return 0; }#include iostream //using namespace std;int main() {std::cout hello Worldstd::endl; // 或者自己声明来源哪个命名空间return 0; }之前的换行方式也能继续用如下 #include iostream using namespace std;int main() {cout hello World\n;return 0; }cin 相当于scanf,但是也不相同。也省去了自己写输入数据的类型这个步骤以后就不用指定输入和输出的类型了。 #include iostream using namespace std;int main() {int i 1;double d 1.11;cin i d; // 输入5 5.55cout i d endl;// 输出5 5.55return 0; }cout的使用 关于cout的使用和printf有很大的不同。 cout输出的时候不用区分变量的类型这个函数可以自动识别变量是什么类型。只要定义过了。自己就会按照定义去输出。 #include iostream using namespace std;int main() {int i 1;double d 1.11;cout i d endl; // 先输出i的值再输出一个空格再输出d的值然后换行// 输出结果是1 1.11return 0; }对比,下面这种写代码的方式。不难发现每个std库里面的函数都声明命名空间就很麻烦。所以在日常练习中不要在乎命名冲突。自己改名。 #include iostream // using namespace std;int main() {int i 1;double d 1.11;std::cout i d std::endl;return 0; }命名冲突 由于c的特性你在命名的时候如果不是特别必要比如不是工程需求啥的能自己换名称尽量自己换。 如果真的有必要跟std库起冲突。也可以按照命名空间剩下的两种展开方式 只给常用的函数展开比如cout、cin啥的用using std::cout;这种方式展开局部对象。不常用的函数就用命名空间作用域限制符来写std::cin; 到此为止算是c的简单入门了至少能看懂框架的每行代码是什么意思了 缺省参数省钱的省 缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时如果没有指定实参则采用该默认值否则使用指定的实参。 #include iostream using namespace std;void Func(int a 0) // 设计函数参数的时候可以给一个默认值这个默认值在调用函数的时候生效。如果调用函数的时候不给参数这个默认值就会生效 {cout a endl; }int main() {Func(); // 没有传参时使用参数的默认值Func(10); // 传参时使用指定的实参return 0; }就是设计函数的时候函数的参数可以给个默认值就像int a0 如果是放在以前就只能是int a。现在可以直接给这个a赋值。当然如果正常使用这个函数也就是调用函数的时候该有的参数都有一开始给a赋的值就无效了。如果少参数这个a才会生效。就像现在的汽车备胎一样其他的轮胎不坏就一直不用备胎一旦有坏的轮胎备胎才会有用处。 这个Func语句调用的时候就没给参数所以a的默认值生效了 而Func(10);这个语句调用的时候给参数了所以给的参数被函数正常使用了。a的默认值无效。 缺省参数分类 全缺省参数 全缺省的意思就是所有的参数都有默认值。 void Func(int a 10, int b 20, int c 30) {cout a a endl;cout b b endl;cout c c endl; }半缺省参数 半缺省就是部分参数有默认值。不是真的一半参数有默认值 void Func(int a, int b 10, int c 20) {cout a a endl;cout b b endl;cout c c endl; }半缺省参数必须从右往左依次连续来给出不能间隔着给 就是设计函数参数为缺省参数的时候不能随便设计不能说是函数的第一个参数和第三个参数是缺省参数第二个参数是正常的参数 缺省参数设计的时候只能是从右往左设计缺省参数 错误示范 void Func(int a 0, int b , int c 20) // 错误方式其中第一个和第三个是缺省参数不是从右到左依次连续的会报错的 { } void Func(int a 0, int b 10, int c) // 也是错的必须是从右往左连续 { }缺省参数函数的使用 对于全缺省参数犹如语句Func(int a0, int b10, int c 20) 传参的时候可以不给参数调用也可以只给一个参数或者只给两个参数或者全部参数都给。 **在给部分参数的时候参数只能从左往右依次赋值。**不能是(,1)或者(,1,) 逗号只是传参的时候给数据的分隔而不是告诉函数你只想给哪个参数传参 #include iostream using namespace std;void Func(int a 10, int b 20, int c 30) {cout a a endl;cout b b endl;cout c c endl; }int main() {Func(); // 一个参数都不给 // 输出就是102030Func(1); // 只给一个参数 // 输出就是 12030Func(1, 2); // 给两个参数 // 输出就是1230Func(1, 2, 3); // 给三个参数 // 输出就是123return 0; }对于半缺省参数函数 在传参的时候至少也是必须给非缺省参数的参数。就像下面这个半缺省参数函数。第一个参数不是缺省参数。那么在调用这个函数的时候只要要有一个参数而且这个一个参数从左往右开始赋值也正好给到第一个参数a。 如果不给第一个参数传参就会报错。 剩下的缺省参数可以选择性传参当然也只能是从左往右传参。 #include iostream using namespace std;void Func(int a, int b 20, int c 30) {cout a a endl;cout b b endl;cout c c endl; }int main() {Func(); // error Func(1); // 只给一个参数 // 输出就是 12030Func(1, 2); // 给两个参数 // 输出就是1230Func(1, 2, 3); // 给三个参数 // 输出就是123return 0; }函数重载 函数重载:是函数的一种特殊情况C允许在同一作用域中声明几个功能类似的同名函数这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同常用来处理实现功能类似数据类型不同的问题 一个函数有多种定义和多种调用方式。 如果是在c语言里面只允许一个函数名称使用一次。 函数重载就是可以一定的条件下使用同名的函数。 // 这种结构在c语言中会报错 int Add(int left, int right) {return leftright; } double Add(double left, double right) {return leftright; } long Add(long left, long right) {return leftright; } int main() {Add(10, 20); // 10是整型编译器默认是个整型就会按照int Add(int left, int right)这个语句去办事Add(10.0, 20.0); // 10.0是浮点型就会去调double Add(double left, double right)Add(10L, 20L); // 10L的10是long的类型。编译器会找参数是long的函数去调用return 0; }函数重载的要求 参数类型不同 int Add(int left, int right) {return leftright; } double Add(double left, double right) {return leftright; } long Add(long left, long right) {return leftright; }参数个数不同 (0个也算个数不同) int Add() {return 0; } int Add(int left) {return left; } int Add(int left, int right) {return left right; } int Add(int left, int right, int mid) {return left right mid; }类型顺序不同 int Add(int i, char ch) {return ich; } int Add(char ch, int i) {return ich; }这三个有一个符合要求就可以构成重载 对于函数前面的那个返回值不能作为函数重载的依据 int Add(int left, int right) {return left right; } void Add(int left, int right) // error 不满足函数重载 {return; }int Add(int right, int left) // error 形参的名称对函数重载没有影响函数重载看的是参数的类型不是参数名称 {return left right; }也就是只有返回值不同不管用 函数重载的使用 #include iostream using namespace std;int Add(int i, char ch) {cout i ch endl;return i ch; } int Add(char ch, int i) {cout ch i endl;return i ch; } void Add() {} int main() {Add(); // 没有参数程序先去找有没有这个函数发现有这个函数void Add()虽然是空但是不影响程序Add(a,5); // 第一个参数是一个字符第二个参数是整型。于是程序找到了第二个函数int Add(char ch, int i)Add(10,b); // 整型字符即第一个函数int Add(int i, char ch)return 0; } /* 输出 a 5 10 b*/函数重载的面试题 什么是函数重载 答在c环境里面函数名相同但是函数参数不同。就叫函数重载。函数参数的不同可以有三种情况类型不同 或者 个数不同 或者 顺序不同 满足这三种其中一个条件就可以构成函数重载。对函数的返回值没有要求 C是如何支持函数重载的C语言为什么不支持难 答这个问题和程序的编译链接有关 下面这部分是对程序预处理的复习 对于一个工程来说至少有三个子文件list.h list.c test.c 预处理 头文件展开宏替换条件编译去掉注释 生成list.i test.i 编译 检查语法生成汇编代码 生成 list.s test.s 汇编 把汇编代码转成二进制机器码 list.o test.o 链接 把两个目标文件链接在一起 关于链接在链接的前一阶段中的汇编阶段遗留了许多问题。比如在tset文件中用到一个函数但是这个函数在另一个.c文件里面实现所以在汇编阶段会在tset文件标记这个函数的地址在其他文件中。汇编阶段会有符号表这个概念符号表里面就是是这个函数的名称和该函数的地址。这个问题在链接阶段实现也就是把两个文件链接了。 名字修饰(name Mangling) 在编译阶段中由于编译器对函数名称处理的方式不同才有了c语言和c的不同。 在c语言的编译器编译阶段函数名称会直接保留到汇编语言中不发生改变 而在c的编译器的编译下函数名称会被重新修饰。 //对同一个函数来说 //比如 int Add(int a,int b) { } void func(int a,double b,int* c) { }// 这两个函数在c语言编译器编译下函数名还是Add和func// 而在c编译器编译的情况下函数名发生改变 // 比如Add函数名被改成了_Z3Addii) // _Z3Addii)是什么意思呢 _Z 是统一的函数名前缀 3 是函数名字符的个数 Add 是原函数名 ii 是函数参数的类型简称第一个i是函数第一个参数的类型第二个i是函数第二个参数的类型 // 同理func函数名被修饰成_Z4funcidPi _Z 前缀 4 函数名字符数 func函数本来的名字 i 是第一个参数的类型 d 是第二个参数的类型 Pi 是第三个参数的类型通过这里就理解了C语言没办法支持函数重载因为同名函数没办法区分。而C是通过函数修饰规则来区分只要参数不同修饰出来的名字就不一样就支持了重载。 另外我们也知道了为什么函数重载要求参数不同而跟返回值没关系。。。 所以函数的参数不同函数在编译阶段生成的名称就不同 因为函数名称不同所以不同函数有不同的地址 再去调用函数的时候就知道调哪个函数了 还是因为c会对函数名进行修饰这些修饰也就是区分不同函数的关键 以上演示是在Linux环境下演示而windows环境下的命名规则更复杂但是道理是相同的。就不再重复演示了。 extern “C”是干啥的面试题 extern C int Add(int left, int right);int Add(int left,int right) {return leftright; } int main() {Add(1,2);return 0; }官方解释 有时候在C工程中可能需要将某些函数按照C的风格来编译在函数前加extern “C”意思是告诉编译器将该函数按照C语言规则来编译。比如tcmalloc是google用C实现的一个项目他提供tcmallc()和tcfree两个接口来使用但如果是C项目就没办法使用那么他就使用extern “C”来解决。 我的理解 在写工程文件的时候本来要求你用c写代码。突然想让你把其中一个函数让c语言也能识别。然而你这个工程本来就是c环境。通过前面的知识你也知道这两种语言的编译器编译函数名称的不同想要直接让c语言去用这个函数是不可能的。但是由于c编译器兼容c语言。所以想到了一个方法也就是在这个函数前面加一个语句entern “C”告诉编译器这个函数要按照c语言去编译。 既然按照c语言去编译了那么c语言编译器可以直接食用了。但c编译器就不一定能食用了。所以设计这个语句的时候也考虑了这个问题。这个语句还完成了一个工作就是告诉c编译器这个函数是c语言版的函数要按照c语言去食用。所以这个函数被暂时用c语言编译了。 一个函数对应一个extern “C”想转换几个函数就写几个。一般只会有少量函数给外部程序使用。 总结 extern “C语句完成了两个任务 告诉c编译器把这个函数按照c语言去编译 因为是按照c语言去编译所以命名规则也变了也就不能函数重载啥的了告诉c编译器这个函数要按照c语言去食用 缺省参数会影响函数重载吗面试题 下面两个函数能形成函数重载吗有问题吗或者什么情况下会出问题 void Func(int a 10) {cout void TestFunc(int) endl; } void Func(int a) {cout void TestFunc(int) endl; } // 根据c的命名规则都是看参数类型这两个函数的函数名相同参数的类型也相同都是i 所以无法区分引用别名 引用就是给一个变量取新的名字而且原来的名字还能接着用。 物理意义上也就是对同一个空间取多个名字 引用的概念 引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。 比如李逵在家称为铁牛江湖上人称黑旋风。 格式: 类型 引用变量名(对象名) 引用实体 int main() {int a1;int raa; // 引用int rba;int rcrb;printf(%d %d %d %d\n,a,ra,rb,rc); // 1 1 1 1printf(%p %p %p %p\n,a,ra,rb,rc); // 都是同一个地址/空间rc2; // 改变rc指向空间的值printf(%d %d %d %d\n,a,ra,rb,rc); // 2 2 2 2printf(%p %p %p %p\n,a,ra,rb,rc); // 都是同一个地址/空间return 0; }注意引用类型必须和引用实体是同种类型的 引用的特性 引用在定义时必须初始化 int main() {int a1;int b; // error 必须初始化return 0; }一个变量可以有多个引用 int main() {int a1;int raa; // 多个引用int rba; // 多个引用int rcrb;// 多个引用 return 0; }引用一旦引用一个实体再不能引用其他实体 int main() {int a1;int ba; // 创建a的引用b // b的类型是int不是intint c2;b c; // 分析此处是b是a的引用还是c的引用还是把c的值赋给a或b// 经过取地址调试可以发现b始终是a的引用。最后a变成了2.return 0; }引用取别名的时候变量的权限可以缩小但是不能放大 int main() {// 正常int a1;int ba;// 不允许变量权限放大const int a1;int ba; // error // a在定义的时候有const修饰是只读的权限但是b这个别名在定义的时候没有const修饰那么b是可读可写的。是权限放大。不被允许//允许变量权限缩小int a1;int ba;const int ca; // 在这里a是可读可写但c是只读。属于权限缩小。被允许。// 也就是在使用的时候c这个变量可以被使用但是不能被修改。 对于a、b变量可以修改和读取。return 0; }引用时可以取不同的类型但是只允许从变量到别名的权限缩小 int main() {int a1; // 一个正常的变量double ba; // 这里是用 a 的值给 b 赋值 // 这里的 a的值 强制类型转换给了 b // 在类型不同的时候编译器会进行自动的类型转换但是又不能改变原来的值a所以会产生一个临时的double类型的变量然后再把这个临时的变量的值赋给 bdouble ca; // error // 在取别名的时候如果类型不同会产生一个临时变量但是这个临时变量不能逆向改变比如这里的a的值只能是从int转换为别名的double可是c这个别名是double的类型是可读可写的。又因为不能在类型不同的时候通过临时变量逆向改变最开始的变量a。所以编译不通过。const double da; // yes // 这里对d进行了const限制虽然是double类型但是这个类型不会通过临时变量逆向改变a所以允许转换。// 但是这个别名d的值也就被锁定了const float ea; // yes // e 和 d 同理return 0; }上面的a与b没有直接关系b只是得到了a的值。b的变化跟a没关系。赋值不涉及权限等问题。 但是cde都是a的别名他们的改变会影响a。可是类型又不同还不能逆向改变变量a的值。所以必须用const修饰 在赋值的时候产生的临时文件是一个常量也不能说是常量应该是具有常性。也就是说有常量的性质。 关于权限的缩小和放大的规则适用于引用和指针 从现在开始变量被称为对象。 常引用 就是在引用的时候加上const进行修饰。加上const 进行修饰之后引用的类型可以不同。给别名加上const进行限制相当于创造了一个常量。所以可以换类型。 这个const属于缩小权限也就是程序对变量修改的权限。当没有const的时候别名也可以之间修改原变量。加上const限制之后别名就定死了不能再改变了。 引用的使用 引用 作参数 // 取别名做参数 void Swap_cpp(int r1, int r2) // 这里的引用只有在传参的时候才被定义 // r1是a的别名r2是b的别名 {int tmp r1;r1 r2;r2 tmp; } // 用指针做参数 void Swap_c(int* r1, int* r2) // r1是a的指针r2是b的指针 {int tmp *r1;*r1 *r2;*r2 tmp; } int main() {int a5,b10;Swap_c(a,b);Swap_cpp(a,b);return 0; }引用做返回值 在学这块知识的时候要先了解一个概念。就是函数在传值的时候一般会产生一个临时变量。这个临时变量是目标变量的临时拷贝然后才是把临时变量的值交给结果变量。比如Add(int a,int b),再调用这个函数的时候不是把值直接给ab变量的而且ab的临时空间得到了值再传给ab。 是不是有一点点浪费空间有一点点臃肿。明明我已经有了一个变量自己的空间现在还要再申请一个空间再存值取值 取别名这个操作完美解决了这个问题在取别名这个操作下想要传值的时候是直接把这个空间类似地址的东西递过去了。 就不需要创建临时变量了。下面的例子也是为了解释这个问题。 void swap(int r1,int r2) // 这是一个传值的函数那些值在传过来的时候会先创建临时两个临时副本然后才把临时空间的值给r1r2两个变量 {int tmpr1;r1r2;r2tmp; } void swap(int r1,int r2) // 这是一个传引用做参数的函数那些作为参数的值传过来的时候不用创建临时空间r1和r2会直接根据传过来的值的空间进行引用 {int tmpr1;r1r2;r2tmp; }int Count1() // 这是一个返回值是传值的函数在这个函数调用结束后会返回一个值这个值也就是n的值n是有一个自己的空间的。 { //在传返回值的时候这个空间被浪费了用的是n的临时拷贝空间传出去的。当然传出去的值是放在临时空间的。这个空间具有常量性static int n0;n;return n; } int Count2() // 这是一个返回值是传引用的函数返回值是n的值n本身有一个空间因为是传引用出去。所以直接利用n自己的空间传值出去了 {static int n0; // static不会影响变量的类型只是会影响变量的生命周期n;return n; } int main() {int r1Conut1(); // error // 因为Conut1是传值返回值得到的空间是一个临时空间具有常量性而r1是int类型不是常量int r2Conut2(); // COnut2的返回值是n的空间本身类型是int而且r2的类型也是int。所以可以直接接收const int r1Conut1(); // 只有r1被const限制之后具有常量性才能接收Conut1的返回值return 0; }总结 凡是临时变量都具有常量性传值返回会多一个临时空间这个临时空间是对值的拷贝而引用返回直接传回来的空间就是那个值自己的空间 引用的不安全 int Add(int a,int b) {int cab;return c; } int main() {int retAdd(1,2);Add(3,4);cout ret ret endl; // ret是一个随机值因为这个ret的空间已经被覆盖了也可能是原来的值要看编译器。所以不安全return 0; }在上面这个示例中可以看到如果Add的返回值c的生命周期只在Add函数内部但ret是c的别名这个时候再调用retret虽然是保存c的空间但是已经是非法访问了。所以引用也是不安全的。 很明显就是c的生命周期太短了。虽然Add算出了结果这个结果保存在了c的空间里面ret也是这个空间的别名。但是出了Add函数空间就失效了。所以为了解决这个问题可以用staticc的生命周期就变成了整个程序有效了。 // 改良版 int Add(int a,int b) {static int cab; // 这个c只有在函数第一次被调用的时候才定义也就是说只有Add(1,2)中创建了c且c的值是3。c已经是一个全局变量了return c; } int main() {int retAdd(1,2);Add(3,4); // 第二次调用函数的时候c已经被创建好了所以对于第二次函数调用而言没有执行任何操作只是把之前c的值传出来了cout ret ret endl; // ret 3return 0; }经过static修饰后c是建立在数据段的常变量。数据段的空间不会被销毁。ret一直是Add(1,2)里面c空间的别名 static修饰过的语句只会执行一次所以第一次执行过后c的值就固定了。 int Add(int a,int b) {static int cab; cab; // 除非再给c重新赋值并且没有static修饰return c; } int retAdd(1,2); // ret3Add(3,4); // ret7总结 用引用返回值的时候要看看这个返回值的生命周期如果只是在他自己的函数内部有效就不安全了因为这块空间可能被使用或者被覆盖。 如果返回值是一个全局变量可以考虑用引用返回。 引用的好处 少创建一个临时变量——提高效率作输出型参数——提高效率其他作用以后讲 传值返回和传引用返回的差别 #include iostream #include time.h using namespace std; struct A { int a[10000]; }; A a; // 值返回 A Func1() { return a; } // 引用返回 A Func2() { return a; } void TestReturnByRefOrValue() // 测试函数返回值是 引用 或 值 的差异 {// 以值作为函数的返回值类型size_t begin1 clock();for (size_t i 0; i 100000; i)Func1();size_t end1 clock();// 以引用作为函数的返回值类型size_t begin2 clock();for (size_t i 0; i 100000; i)Func2();size_t end2 clock();// 计算两个函数运算完成之后的时间cout Func1 time: end1 - begin1 endl; // 165 mscout Func2 time: end2 - begin2 endl; // 1 ms }int main() {TestReturnByRefOrValue();return 0; }上面的测试中func1是传值返回他的返回值是结构体a的临时拷贝 func2的函数返回值是a的引用也就是a自己本来的空间没有额外创建新的空间 传值做参数和传引用做参数的差异 #include iostream #include time.h using namespace std; struct A { int a[10000]; }; void TestFunc1(A a) {} void TestFunc2(A a) {} void TestRefAndValue() {A a;// 以值作为函数参数size_t begin1 clock();for (size_t i 0; i 100000; i)TestFunc1(a);size_t end1 clock();// 以引用作为函数参数size_t begin2 clock();for (size_t i 0; i 100000; i)TestFunc2(a);size_t end2 clock();// 分别计算两个函数运行结束后的时间cout TestFunc1(A)-time: end1 - begin1 endl; // 83 cout TestFunc2(A)-time: end2 - begin2 endl; // 0 } int main() {TestRefAndValue();return 0; }经过上面这两个测试用例发现无论是传值做参数还是传值返回都会浪费一定的效率传引用做参数或返回值都能解决这个问题也证明了引用可以提高效率 引用作返回值的适用场景 返回值是一个全局变量静态变量 引用和指针的区别 在语法上来说 在创建时引用直接就是在原有的空间上取别名。 指针则是新建一个空间存放原空间的地址。有新的空间产生 但是对于底层来说不一样。 eax,[a]的意思,是把a的地址给eax。所以后面就是eax再把地址给b和p.指针和引用在汇编语法上是一样的。 但是在底层逻辑上一样。 引用在定义时必须初始化指针没有要求引用在初始化时引用一个实体后就不能再引用其他实体而指针可以在任何时候指向任何一个同类型 实体没有NULL引用但有NULL指针在sizeof中含义不同引用结果为引用类型的大小但指针始终是地址空间所占字节个数(32位平台下占 4个字节)引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小有多级指针但是没有多级引用访问实体方式不同指针需要显式解引用引用编译器自己处理引用比指针使用起来相对更安全 内联函数inline 以inline修饰的函数叫做内联函数编译时C编译器会在调用内联函数的地方展开没有函数压栈的开销内联函数提升程序运行的效率。 内联函数解决的问题 因为某些函数在程序执行的过程中被频繁调用。哪怕再小的函数也需要建立函数栈帧也会有消耗的。 就是从主函数到调用的函数的过程中会进行压栈的操作。也就是程序会不断的从主函数的栈帧跳到调用的函数的栈帧。 c语言中可以使用宏函数c使用内联函数 他们都是在预编译阶段使用宏替换在函数中展开的。c语言的做法是有缺点的宏不能调试、可读性很差 对于c来说有了内联函数的概念了只需要在相应的函数前面再加上inline语句。这个函数在程序的预编译阶段就会被展开到函数中去。观察代码的汇编语言中是否有call语句。call语句是使用外函数的意思。当然内联函数就没有call了 #include iostream #include time.h using namespace std; inline void Swap(int r1, int r2) // 改成内联函数 {int tmp r1;r1 r2;r2 tmp; } //void Swap(int r1, int r2) // 原函数 //{ // int tmp r1; // r1 r2; // r2 tmp; //} int main() {int a 1, b 2;Swap(a, b);cout a bendl;return 0; }很明显想要把常用函数改成内联函数只需要在函数前面再加个inline就可以了。 这个常用函数还是保持正常的函数结构。————函数的可读性强 在写函数的时候节省大量的时候不用多次写重复的语句了。 内联函数的特性 inline是一种以空间换时间的做法省去调用函数额开销。所以代码很长/递归的函数不适宜使用作为内联函数。适用小函数inline对于编译器而言只是一个建议编译器会自动优化如果定义为inline的函数体代码很多/有递归等等编译器优化时会忽略掉内联。inline不建议声明和定义分离分离会导致链接错误。因为inline被展开就没有函数地址了链接就会找不到。 如果不用inline展开函数所占用的空间就是主函数和内联函数相加。 用inline展开之后。函数所占用的空间就是主函数自己的内联函数*次数之和。所以inline的函数要么不能频繁使用要么自己本身的语句要少 类似递归或者超过20行代码的函数就不会内联了编译器自动优化。 因为内联函数是会展开到主函数中那么在展开的时候就不构成函数了也就没有内联函数的地址。如果此时函数的声明说这个函数是内联。程序是找不到函数的。 // F.h #include iostream using namespace std; inline void f(int i); // 函数的声明是内联但是函数的原型不在这个.h文件中// F.cpp #include F.h void f(int i) {cout i endl; }// main.cpp #include F.h int main() {f(10); // errorreturn 0; }宏的优缺点面试题 优点 增强代码的复用性。提高性能。 缺点 不方便调试宏。因为预编译阶段进行了替换导致代码可读性差可维护性差容易误用。太难设计没有类型安全的检查 。 C有哪些技术替代宏 常量定义 换用const #define N 10 // c语言const int N 10; // c优化版函数定义 换用内联函数 宏函数————》》》inline函数替换 以上所有的知识都是C98支持的 auto关键字C11 官方定义 在早期C/C中auto的含义是使用auto修饰的变量是具有自动存储器的局部变量但遗憾的是一直没有人去使用它大家可思考下为什么 C11中标准委员会赋予了auto全新的含义即auto不再是一个存储类型指示符而是作为一个新的类型指示符来指示编译器auto声明的变量必须由编译器在编译时期推导而得。 就是说auto这个类型关键字可以自己推出要定义的变量应该是什么类型。 #include iostream using namespace std;int main() {int a 0;auto b a;int c a;auto d a;auto* e a; auto f a;cout typeid(b).name() endl; // typeid(b).name()这个语句可以打印括号里面参数的类型cout typeid(c).name() endl;cout typeid(d).name() endl;cout typeid(e).name() endl;cout typeid(f).name() endl;return 0; } /* 输出 int int int int * __ptr64 // e是指针类型__ptr64意味着是本机是64位系统 int * __ptr64 // e和f的类型相同.所以用auto的时候就不用再自己加”\*“了. */auto的使用规则 使用auto定义变量时必须对其进行初始化 auto e; // 无法通过编译auto与指针和引用结合起来使用 用auto声明指针类型时用auto和auto*没有任何区别但用auto声明引用类型时则必须加 int a 0; int b a; auto c a; // c是对a的引用c的类型是autoauto推导出c的类型是int。auto只是一个类型。类型 变量变量。才是取别名在同一行定义多个变量 当在同一行声明多个变量时这些变量必须是相同的类型否则编译器将会报错因为编译器实际只对第一个类型进行推导然后用推导出来的类型定义其他变量 void TestAuto() {auto a 1, b 2; // 允许同一行是一个类型auto c 3, d 4.0; // 该行代码会编译失败因为c和d的初始化表达式类型不同 // 同一行类型不同 }auto不能作为函数的参数 // 此处代码编译失败auto不能作为形参类型因为编译器无法对a的实际类型进行推导 void TestAuto(auto a) {}因为在编译的时候这个函数虽然没有被使用哪怕这个程序也不使用。但是在编译阶段依旧要对这个函数建立栈帧。如果是auto作参数参数类型未知那么auto所需要的空间字节也未知 auto不能直接用来声明数组 void TestAuto() {int a[] {1,2,3};auto b[] {456}; }auto最大的作用 在以后使用容器的时候可以优化变量的类型。 #include iostream #include map // 未来学习的容器map using namespace std;int main() {std::mapstd::string, std::string dict;std::mapstd::string, std::string::iterator it1 dict.begin(); // 这就是创建了一个变量it1类型名称很长auto it2 dict.begin(); // 使用auto可以简化那一大段类型名称return 0; }基于范围的for循环(C11) #include iostream #include map using namespace std;int main() {int array[] { 1, 2, 3, 4, 5 };// 把array数组乘2倍再打印出来// C语言的做法for (int i 0; i sizeof(array) / sizeof(array[0]); i){array[i] * 2;}for (int* p array; p array sizeof(array) / sizeof(array[0]); p){cout *p endl;}// C11 范围for的用法for (auto e : array) // auto自动类型名auto是取别名的意思。auto e就是{e * 2;}for (auto e : array){cout e ;}return 0; }上面这个示例中展示了两种解决问题的方法 一种是c语言的做法比较简单不再解释 第二种就是c11 中“范围for”的使用 for (auto e : array) // 注意是auto e是引用 auto自动类型名auto是取别名的意思。auto e就是一个准备取别名的变量e。array的意思就是从array这个数组中从数组的0到结束依次取数值出来。对就是自动取值自动结束 连上前面的auto e。整体for (auto e : array)意思就是从array这个数组中取值出来给这个值取别名 e 。然后再对e进行操作 e * 2; // e变成原来的二倍取别名的原空间的值也就变成了二倍。 for (auto e : array) // 注意是auto e是赋值 意思就是从这个数组名为array的数组里面从偏移量为0的地方取值出来赋值给e。等e完成一次循环之后再向后移动一个步长取数组的下一个值出来交给e。 反正这个“范围for”用起来很方便。 这是c池里面的一个语法操作因为用起来实在是方便就像吃糖一样也叫语法糖。 目前知道可以这么用就够了。 范围for的使用条件 for循环迭代的范围必须是确定的 对于数组而言就是数组中第一个元素和最后一个元素的范围对于类而言应该提供begin和end的方法begin和end就是for循环迭代的范围。 注意以下代码就有问题因为for的范围不确定 void TestFor(int array[]) // 这里的array是一个指针不是真正的数组名 {for(auto e : array) // 会报错cout e endl; }迭代的对象要实现和的操作。(关于迭代器这个问题以后会讲现在大家了解一下就可以了) 指针空值nullptr(C11) 先上个例子 #include iostream using namespace std; void fun(int a) {cout 整型 endl; } void fun(int* a) {cout 整型指针 endl; } int main() {fun(0);fun(NULL);fun(nullptr);return 0; } /* 输出 整型 整型 整型指针 */很明显第二个出错了。明明NULL就是指针置空的。但是错了。 原因就是C语言中NULL是被宏定义出来的。也就是#define NULL 0 NULL就是00的类型是int 一个 int 类型的 0给一个 int* 类型的指针置空 所以在C11中新增一个常量nullptr。 这个新增的常量nullptr的类型是void* 满足了日常写代码对指针的正确初始化的操作。如果还是用NULL可能会发生意外情况。 nullptr也是宏替换。 NULL是int类型的0 nullptr是void*类型的0

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/87827.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小程序电商平台排名嘉兴做网站优化价格

TCL:花开刹那还是浴火重生 [url]http://www.sina.com.cn[/url] 2008年04月07日 15:24 民营经济报本报记者 叶键 TCL3月份公布了2007年财报,公司净利润达3.9亿元,并于3月27日“摘星脱帽”,恢复为“TCL集团”的自由身,…

网站建设总结ppt深圳做网站的公司的区域

1、效果 如下图所示,我们在输入大学时,程序会到后端查询名字中包含大学的数据,并展示到前端页面。 用户选择一个大学,该大学值会被赋值到input表单,同时关闭下拉表单; 当页面展示的数据都不符合条件时&…

安徽城乡住房建设厅网站网站建设預算

这里写目录标题 1. 主要功能:2. 讲解视频:3. 仿真4. 实物烧录和现象5. 程序代码6. 设计资料内容清单&&下载链接资料下载链接: 【普中开发板】基于51单片机温度报警器设计( proteus仿真程序实物演示讲解视频) Proteus 仿真…

个人网站可以收费吗网站建设源代码版权问题

大家好,我是若川。持续组织了近一年的源码共读活动,感兴趣的可以 加我微信lxchuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外&…

政务建设网站得必要性网站开发在线学习

通信框架功能设计 功能描述 通信框架承载了业务内部各模块之间的消息交互和服务调用,它的主要功能如下: 基于 Netty 的 NIO 通信框架,提供高性能的异步通信能力; 提供消息的编解码框架,可以实现 POJO 的序列化和反序…

泰和网站制作厦门网页建站申请费用

emlog默许不能生成静态文件,不过彷佛有生成静态页面的相干插件,该插件博客吧先不研讨,本日博客簿要引见的是emlog 4.0版本在IIS6环境下的伪静态划定规矩,人人都晓得,经由过程伪静态能够让博客文章网址变得对搜索引擎越…

ui设计的细分研究方向包含哪几项福州搜索优化网站

线程同步机制为线程协同工作而设计,windows系统中有多种机制可以用于线程同步,最常用的有下面几种: 互斥对象(Mutex)时间对象(Event)信号量(Semaphore)临界区(critical section)可等待计时器(waitable Timer)同步过程中两个重要的概念是同步对…

石家庄建设企业网站建设我们的网站

GPS北斗卫星同步时钟系统(北斗授时设备)在通信系统中应用 GPS北斗卫星同步时钟系统(北斗授时设备)在通信系统中应用 卫星时间同步系统是根据《华东电网统一时钟系统技术规范》、《上海电网GPS时间同步系统技术原则和运行管理规定》和《电力系统时间同步技术规范》设计的时间同步…

做平台的网站有哪些功能张店学校网站建设定制

最近在使用这几个在做项目,因为第一次用这个,所以不免有些问题。总结下踩的坑 1.测试类位置 首先测试类约定会放在tests里面,不然有可能发生引入包的问题,会报错某些包找不到。 2. 测试类依赖注入 这里我就用的真实的数据库操作…

学校网站群建设做采购 通常在什么网站看

🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师…

哪个网站专门做商铺啊德州市住房和城乡建设部网站

贪心算法-活动安排-最详细注释解析 题目: 学校在最近几天有n个活动,这些活动都需要使用学校的大礼堂,在同一时间,礼堂只能被一个活动使用。由于有些活动时间上有冲突,学校办公室人员只好让一些活动放弃使用礼堂而使用…

单页营销式网站模板app开发公司有哪些部门

1.MySQL为什么要用数字做自增主键? 首先为什么我们使用的是int类型,而不是varchar类型 int永远是固定的4个字节,而char类型是1~255字节之间 优点 占用空间小,节省CPU开销在使用中,通常会在主键上建立索引&#xff…

爱网站大全广州seo网络优化公司

问题描述: 一打开office就显示正在更新,请稍后,过一会就弹窗报错。 解决方案: 1.winR输入services.msc 2.将Microsoft Office即点即用服务设为自动 如果第二步中已经是自动状态,先禁用一下,再重新设置为…

天津网站定制设计平台官网

在用java导出Excel的时候,表头不能写死,而是根据情况变化的。 实体类如下: public class EquSysExportNoChainVo {Excel(name "")private String thisValue; //当前值 } 给实体类的Excel的name赋值的方法如下&…

湖州建设企业网站模仿别人网站侵权

发,即头发,又名血余。 发之营养来源于血,故称“发为血之余”。 但发的生机根源于肾。 因为肾藏精,精能化血,精血旺盛,则毛发壮而润泽,故又说肾“其华在发” 黑桑葚,黑枸杞&#xff0…

电商新手从哪里做起关键词seo资源

每天你都会听见五花八门的投资建议,告诉你应该买入还是卖出。如果这让你感到无所适从,不妨静下心来,听听历史上最成功的投资者的建议。 我们搜集了21位顶尖大牛的投资箴言,以飨读者。 1、George Soros:好的投资总是无…

百度右边相关网站怎么做的网站建设模块一项目三

使用场景 在Node版本快速更新迭代的今天,新老项目使用的node版本号可能已经不相同了,node版本更新越来越快,项目越做越多,node切换版本号的需求越来越迫切,传统卸载一个版本在安装另一个版本的方式太过于麻烦&#xf…

深圳企业网站建设推荐公司最近民生新闻100条

1 前言 本文通过一个立方体贴图的例子,讲解三维纹理贴图的应用,案例中使用 6 张不同的图片给立方体贴图,图片如下。 读者如果对 libGDX 不太熟悉,请回顾以下内容。 使用Mesh绘制三角形使用Mesh绘制矩形使用Mesh绘制圆形使用Mesh绘…

网站图片做cdn做区块链网站的公司

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 📧 清隆这边最…

邵阳做网站鹤岗网站建设

在现代快节奏的生活中,我们时常感到生活与工作的节奏难以协调。繁琐的工作日程与待办任务繁杂交织,往往让我们束手无策,无法高效地进行协调与分配。 桌面便签工具成为了我们处理这些挑战的得力助手。它不仅能够随时提醒我们完成任务&#xf…