当前位置: 首页 > news >正文

阿里云建设网站买哪个服务wordpress m3u8

阿里云建设网站买哪个服务,wordpress m3u8,科学新概念外链平台,如何给网站加cdn目录 一. C关键字(C98) 二、C的第一个程序 三、命名空间 3.1.namespace的价值 3.2.namespace的定义 3.2.命名空间使用 总结#xff1a;在项目当中第一、第二种方法搭配使用#xff0c;第三种冲突风险非常大#xff0c;仅适合练习使用。 四、C输入输出 五、缺省…目录 一. C关键字(C98) 二、C的第一个程序 三、命名空间 3.1.namespace的价值 3.2.namespace的定义 3.2.命名空间使用 总结在项目当中第一、第二种方法搭配使用第三种冲突风险非常大仅适合练习使用。 四、C输入输出 五、缺省参数 六、函数重载 七、引用 7.1.引用的概念和定义 7.2.引用的特性 7.3.引用的使用 7.4.const引用 7.5.指针和引用的关系 八、 inline 九、nullptr 一. C关键字(C98) C总计63个关键字C语言32个关键字。 下面只是看一下C有多少关键字不对关键字进行具体的讲解。后面对应文章会详细讲解。 asmdoifreturntrycontinueautodoubleinlineshorttypedefforbooldynamic_castintsignedtypeidpublicbreakelselongsizeoftypenamethrowcaseenummutablestaticunionwchar_tcatchexplicitnamespacestatic_castunsigneddefaultcharexportnewstructusingfriendclassexternoperatorswitchvirtualregisterconstfalseprivatetemplatevoidtrueconst_castfloatprotectedthisvolatilewhiledeletegotoreinterpret_cast 二、C的第一个程序 C兼容C语言绝大多数的语法部分地方存在改动所以C语言实现的那套hello world依旧可以在C下运行如下图 // test.cpp #includestdio.h int main() {printf(hello world\n);return 0; } 在实际应用中以VS为例当文件后缀改为.cpp后vs编译器看到是.cpp就会调用C编译器编译当文件后缀改为.c后vs编译器就会调用C编译器编译但是linux下要用g编译不再是gcc。 当然C有一套自己的输入输出严格说C版本的hello world应该是这样写的。 // test.cpp // 这里的std cout等都看不懂没关系下面会依次讲解 #includeiostream using namespace std;int main() {cout hello world\n endl;return 0; } 三、命名空间 3.1.namespace的价值 在C/C中同一域内不能存在定义相同名字的不同事物变量、函数和后面要学到的类都是大量存在的这些变量、函数和类的名称将都存在于全局作用域或者局部作用域中可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的。 如下图我们定义了一个变量rand如果我们不包含的头文件stdlib.h可以直接使用但是由于在这个头文件内rand是被定义成一个函数那么我们定义的rand就会与头文件中的rand发生冲突产生重定义错误。 #include stdio.h #include stdlib.h int rand 10; int main() {// 编译报错error C2365: “rand”: 重定义以前的定义是“函数”printf(%d\n, rand);return 0; } 看上去来说,这种情况我们似乎只要使用不同的名字就可以避免但是在实际的项目工程中不同人员之间开发者与使用者之间默契的使用不同的名称是完全不可能的 简单的名称也无法满足巨大的需求因此对于C语言来说这样的命名冲突是普遍存在的问题C引入namespace就是为了更好的解决这样的问题 3.2.namespace的定义 • 定义命名空间需要使用到namespace关键字后面跟命名空间的名字然后接一对{}即可{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。 namespace zlr {// 命名空间中可以定义变量/函数/类型int rand 10;int Add(int left, int right){return left right;}struct Node{struct Node* next;int val;}; } • namespace本质是定义出一个域(命名空间域)这个域跟全局域各自独立不同的域可以定义同名变量所以下面的rand不在冲突了。 我们上文说过C/C中不同的域中是不可以定义相同的变量的原本C/C中内只有全局域与局部域大量的变量、函数、类挤在两个域内会产生大量的冲突。而使用namespace命名空间名就相当于在变量、函数、类周围建造了一堵墙形成了命名空间域域其他域相互隔绝命名空间名不同虽然同属于命名空间域但是不同墙之间也会隔绝冲突大大减少。注实际项目实践中不同的项目组之间会分配固定的命名空间名因此命名空间域定义冲突的可能性非常低如果冲突。 • C中域有函数局部域全局域命名空间域类域域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑所有有了域隔离名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑还会影响变量的生命周期命名空间域和类域不影响变量生命周期。 以上图为例C语言中当局部与全局定义相同的变量时查找逻辑是就近原则先采用局部定义的变量。对于命名空间域来说在变量周围建墙后查找时只会在其他域在全局域中找。想要使用命名空间域内定义有一定的要求。 • namespace只能定义在全局当然他还可以嵌套定义。 //2. 命名空间可以嵌套 namespace zlr {namespace z1{int rand 1;int Add(int left, int right){return left right;}}namespace z2{int rand 2;int Add(int left, int right){return (left right) * 10;}} } int main() { //……………………return 0; } • 项目工程中多文件中定义的同名namespace会认为是一个namespace不会冲突。 // 多文件中可以定义同名namespace他们会默认合并到一起就像同一个namespace一样 // Stack.h #pragma once #includestdio.h #includestdlib.h #includestdbool.h #includeassert.h namespace zlr {typedef int STDataType;typedef struct Stack{int top;int capacity;}ST;void STInit(ST* ps, int n);void STDestroy(ST* ps);void STPush(ST* ps, STDataType x);void STPop(ST* ps);STDataType STTop(ST* ps);int STSize(ST* ps);bool STEmpty(ST* ps); } // Stack.cpp #includeStack.h namespace zlr {void STInit(ST* ps, int n){assert(ps);ps-a (STDataType*)malloc(n * sizeof(STDataType));ps-top 0;ps-capacity n;}// 栈顶void STPush(ST* ps, STDataType x){assert(ps);// 满了 扩容if (ps-top ps-capacity){printf(扩容\n);int newcapacity ps-capacity 0 ? 4 : ps-capacity* 2;STDataType* tmp (STDataType*)realloc(ps-a,newcapacity * sizeof(STDataType));if (tmp NULL){perror(realloc fail);return;}ps-a tmp;ps-capacity newcapacity;}ps-a[ps-top] x;ps-top;}//... } // Queue.h #pragma once #includestdlib.h #includestdbool.h #includeassert.h namespace zlr {typedef int QDataType;typedef struct QueueNode{int val;struct QueueNode* next;}QNode;typedef struct Queue{QNode* phead;QNode* ptail;int size;}Queue;void QueueInit(Queue* pq);void QueueDestroy(Queue* pq);// 入队列void QueuePush(Queue* pq, QDataType x);// 出队列void QueuePop(Queue* pq);QDataType QueueFront(Queue* pq);QDataType QueueBack(Queue* pq);bool QueueEmpty(Queue* pq);int QueueSize(Queue* pq); } // Queue.cpp #includeQueue.h namespace zlr {void QueueInit(Queue* pq){assert(pq);pq-phead NULL;pq-ptail NULL;pq-size 0;}// ... } // test.cpp #includeQueue.h #includeStack.h // 全局定义了一份单独的Stack typedef struct Stack {int a[10];int top; }ST; void STInit(ST* ps) {} void STPush(ST* ps, int x) {} int main() {// 调用全局的ST st1;STInit(st1);STPush(st1, 1);STPush(st1, 2);printf(%d\n, sizeof(st1));// 调用zlr namespace的zlr::ST st2;printf(%d\n, sizeof(st2));zlr::STInit(st2);zlr::STPush(st2, 1);zlr::STPush(st2, 2);return 0; } • C标准库都放在一个叫std(standard)的命名空间中。 为了防止冲突设计者当初设计之际也将语言本身自带的库放在std命名空间内需要注意的是不同的库是放在不同的文件内正是因为上文所讲的相同的namespace会被视为一份所以当我们包含不同头文件时不会冲突包含头文件后也需要对应的方法才能使用头文件命名空间内的定义。 3.2.命名空间使用 编译查找一个变量的声明/定义时默认只会在局部或者全局查找不会到命名空间里面去查找。所以下面程序会编译报错。所以我们要使用命名空间中定义的变量/函数有三种方式 • 通过域作用限定符::指定命名空间访问项目中推荐这种方式。 域作用限定符写在对应函数或变量等名字左边如上图对于遭遇就近原则的变量我们在其左边加上访问限定符左边加不加空格都不影响那么变量采用的就是全局定义的值。 namespace zlr {int rand 10; }int a 0;int main() {printf(%p\n, rand);printf(%d\n, zlr::rand);int a 1;printf(%d\n, a);// ::域作用限定符printf(%d\n,::a);return 0; } 我们要使用命名空间内定义的值我们需要命名空间名::变量的形式这样在编译环节就相当于拆除对应的墙可以进对应命名空间找到对应的量。 // 域 namespace zlr {int rand 10;int Add(int left, int right){return left right;}struct Node{struct Node* next;int val;}; }int main() {printf(%d\n, zlr::Add(1, 1));struct zlr::Node p1;return 0; } 对于命名空间内定义的函数和结构体我们以如上图的形式使用需要注意的是对于结构体::右边接结构体名因为结构体名才是我们实际定义的量struct是关键字。 #include stdio.h #include stdlib.h// 域 namespace zlr {namespace z1{int rand 1;int Add(int left, int right){return left right;}}namespace z2{int rand 2;int Add(int left, int right){return (left right) * 10;}} }int main() {printf(%d\n, zlr::z1::rand);printf(%d\n, zlr::z2::rand);return 0; } 对于嵌套定义的命名空间我们就需要一层一层的“拆墙”才能使用最内部定义的对应量。 •通过 using关键字将命名空间中某个成员展开项目中经常访问的不存在冲突的成员推荐这种方式。 对于命名空间内的定义的量每次使用都需要以上文第一种的方式来使用无疑非常繁琐我们最初使用命名空间的目的是防止命名冲突的问题如果一个项目我们能确保某一个量名字不会冲突或者冲突概率很小并且这个量多次使用那么我们就可以通过using命名空间名对象名形式将对应量完全展开其他量不受影响这样量继续相当于全局定义的可以直接使用。 #include stdio.h namespace zlr {int a 0;int b 1; }using zlr::a;int main() {printf(%d\n, a);printf(%d\n, zlr::b);return 0; } • 展开命名空间中全部成员项目不推荐冲突风险很大日常小练习程序为了方便推荐使用。 除此之外对于学生来说参加一些比赛或者写一下小算法的情况可以确保一定不会遇到命名冲突的问题并且需要经常使用std内的定义量所以我们可以通过usingnamespace命名空间名的形式将对应命名空间完全展开这样该命名空间内的所有量可以直接使用。 //展开头文件 #include stdio.h namespace zlr {int a 0;int b 1; }using namespace zlr;int main() {printf(%d\n, a);printf(%d\n, b);return 0; } 总结在项目当中第一、第二种方法搭配使用第三种冲突风险非常大仅适合练习使用。 四、C输入输出 • iostream 是 Input Output Stream 的缩写是标准的输入、输出流库定义了标准的输入、输出对象。 类似于我们所熟知的C语言内的stdio.h为了使用C内的输入输出以后我们也需要开头包含该头文件 #include iostream • std::cin 是 istream 类的对象类本文不做详细介绍可以先理解为结构体它主要面向窄字符narrow characters (of type char)的标准输入流类似scanf。 • std::cout 是 ostream 类的对象它主要面向窄字符的标准输出流(类似pritntf)。 (注c代表的就是字符的意思除了窄字符还有宽字符宽字符本文暂且不详细介绍。 内存中为了方便计算数据被划分出int等各种类型但是在文件、网络等内部都是以字符流的形式存储。) • std::endl 是一个函数流插入输出时相当于插入一个换行字符加刷新缓冲区。底层也较为复杂使用了运算符重载本文不做具体讲解可以理解为换行符 • 是流插入运算符是流提取运算符。C语言还用这两个运算符做位运算左移/右移这里是通过运算符重载实现的一符多用本文详细不介绍运算符重载。 • cout/cin/endl等都属于C标准库C标准库都放在一个叫std(standard)的命名空间中所以要通过命名空间的使用方式去用他们。 • 一般日常练习中我们可以using namespace std实际项目开发中不建议using namespace std。 #includeiostream using namespace std;int main() {int a 0;cin a;cout hello world\n endl;//完全展开std直接使用std::cout hello world\n std::endl;//不展开std的使用方式return 0; } 上段代码cin这一段cin是通过标准输入设备键盘通过提取输入的字符自动转换为对应的 int类型数据放到a中cout这一行cout代表着标准输出这是代表控制台——运行代码常见的黑框框然后hello world\n 通过被提取输出到控制台endlend line通过再输出一个换行符并刷新缓冲区。 • 使用C输入输出更方便不需要像printf/scanf输入输出时那样需要手动指定格式C的输入输出可以自动识别变量类型(本质是通过函数重载实现的本文不做具体介绍)其实最重要的是C的流能更好的支持自定义类型对象的输入输出。 #includeiostream using namespace std;int main() {int a 0;double b 0.1;char c x;cout a b c \n \n endl;//输出多个空格std::cout a b c std::endl;//输出单个空格//cout输出不需要指定输出格式自动识别类型//scanf(%d%lf, a, b);//printf(%d %lf\n, a, b);double d 2.22222222;printf(%.2lf\n, d);cout d endl;cout d endl;// cin可以自动识别输入变量的类型//cin a;//cin b c;cin a b c;cout a endl;cout b c endl;return 0; } 需要注意的是使用时因为是二元操作符像上图这样写就存在a、空格、cout三个操作数。只能一个一个输入。 需要注意的是C中想实现输出数据精度控制的功能非常复杂这里推荐使用C语言printf来实现精度的控制 • 这里我们没有包含stdio.h也可以使用printf和scanf因为在包含iostream间接包含了。vs系列编译器是这样的其他编译器可能会报错。 #include iostream using namespace std; int main() {int a 0;double b 0.1;char c x;cout a b c endl;std::cout a b c std::endl;scanf(%d%lf, a, b);printf(%d %lf\n, a, b);// 可以自动识别变量的类型cin a;cin b c;cout a endl;cout b c endl;return 0; } #includeiostream using namespace std; int main() {// 在io需求比较高的地方如部分大量输入的竞赛题中加上以下3行代码// 可以提高CIO效率ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);return 0; } 因为C很多地方都要兼顾C,那么就会需要兼顾C的缓冲区的刷新等问题在面对大量的IO需求的场景下如竞赛通常会加上如上中的三行代码来提高效率本文仅为入门基础具体原理不详细介绍 • IO流涉及类和对象运算符重载、继承等很多面向对象的知识这些知识我们还没有讲解所以这里我们只能简单认识一下C IO流的用法后续文章详细介绍IO流库。 五、缺省参数 • 缺省参数是声明或定义函数时为函数的参数指定一个缺省值常量或者全局变量。在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参缺省参数分为全缺省和半缺省参数。有些地方把缺省参数也叫默认参数 • 全缺省就是全部形参给缺省值半缺省就是部分形参给缺省值。C规定半缺省参数必须从右往左依次连续缺省不能间隔跳跃给缺省值。 • 带缺省参数的函数调用C规定必须从左到右依次给实参不能跳跃给实参。 #include iostream using namespace std; // 全缺省 void Func1(int a 10, int b 20, int c 30)//全缺省 {cout a a endl;cout b b endl;cout c c endl endl; } // 半缺省 void Func2(int a, int b 10, int c 20)//半缺省 {cout a a endl;cout b b endl;cout c c endl endl; } int main() {Func1();//使用缺省参数设置的默认值Func1(1);Func1(1, 2);//依次赋值Func1(1, 2, 3);Func2(100);Func2(100, 200);Func2(100, 200, 300);return 0; } // Stack.h #include iostream #include assert.h using namespace std; typedef int STDataType; typedef struct Stack {STDataType* a;int top;int capacity; }ST; void STInit(ST* ps, int n 4); // Stack.cpp #includeStack.h // 缺省参数不能声明和定义同时给 void STInit(ST* ps, int n) {assert(ps n 0);ps-a (STDataType*)malloc(n * sizeof(STDataType));ps-top 0;ps-capacity n; } // test.cpp #includeStack.h int main() {ST s1;STInit(s1);// 确定知道要插入1000个数据初始化时一把开好避免扩容ST s2;STInit(s2, 1000);return 0; } 对于缺省参数的一个应用场景是假如我们现在要开辟一个1000大小的栈因为我们不可能清楚每次要开辟的栈的代销所以初始化时只能赋予空值然后一次次判断再扩容知道满足1000大小的需求但这样的消耗是比较大的但是现在我们使用缺省参数如果知道要开辟的大小那么我们直接将数值传入初始化时就能开好如果不知道要开辟的大小那初始化时可以使用缺省值开一个4大小的栈。可以说很大提升了效率。 • 函数声明和定义分离时缺省参数不能在函数声明和定义中同时出现规定必须函数声明给缺省值。 如果同时出现就会出现不知道以谁为准的情况所以规定函数声明给缺省值。 六、函数重载 C支持在同一作用域中出现同名函数但是要求这些同名函数的形参不同可以是参数个数不同或者类型不同。这样C函数调用就表现出了多态行为使用更灵活。C语言是不支持同一作用域中出现同名函数的。 #includeiostream using namespace std; // 1、参数类型不同 int Add(int left, int right) {cout int Add(int left, int right) endl;return left right; } double Add(double left, double right) {cout double Add(double left, double right) endl;return left right; } // 2、参数个数不同 void f() {cout f() endl; } void f(int a) {cout f(int a) endl; } // 3、参数类型顺序不同(本质就是类型不同) void f(int a, char b) {cout f(int a,char b) endl; } void f(char b, int a) {cout f(char b, int a) endl; }int main() {Add(10, 20);Add(10.1, 20.2);f();f(10);f(10, a);f(a, 10);return 0; } 对于C语言来说我们在定义函数时比如一个数的相加与两个数的相加、整形与整形的相加或者浮点型与整形的相加这些在我们朴素认知中这些只是是参数的类型或者个数不同本质上应该都可以通过一个函数实现但1是C语言中不允许同名函数的参数类型或者个数不同。因此为了实现这些基本没有差别的功能我们不得不使用不同的名字去定义函数调用起来也非常麻烦。 但是C支持在同一作用域中出现形参不同或者个数不同的同名函数这样C函数调用就表现出了多态行为使用更灵活比如在上述代码中的两个Add尽管定义不同但是使用起来就像是同一的函数。这样实际上就相当于众多相似的功能都集中到一个函数上函数更完善、更方便。 // 下面两个函数构成重载 // f()但是调用时会报错存在歧义编译器不知道调用谁void f1() {cout f() endl; }void f1(int a 10) {cout f(int a) endl; } 但是需要注意的是像上述代码实现函数重载会出现问题两个函数的参数不同构成函数重载但是因为第二个函数又使用的了缺省参数对于缺省参数来说不传参会使用缺省值所以第二个函数可以不传递参数但是第一个函数定义的也不同传参所以调用f1()会出现问题编译器不知道调用谁。 七、引用 7.1.引用的概念和定义 引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。别名本质上还是那一块空间只是换了一个称呼.比如水浒传中李逵宋江叫铁牛江湖上人称黑旋风林冲外号豹子头使用格式:类型 引用别名 引用对象; C中为了避免引入太多的运算符会复用C语言的一些符号比如前面的 和 这里引用也和取地址使用了同一个符号大家注意使用方法角度区分就可以。 7.2.引用的特性 • 引用在定义时必须初始化 • 一个变量可以有多个引用引用只是给变量取别名本身不开空间不论给变量取别名还是给别名取别名指向空间都不变 #includeiostream using namespace std; int main() {int a 0;// 引用b和c是a的别名int b a;int c a;// 也可以给别名b取别名d相当于还是a的别名int d b;//// 这里取地址我们看到是一样的cout a endl;cout b endl;cout c endl;cout d endl;return 0; } • 引用一旦引用一个实体再不能引用其他实体 #includeiostream using namespace std; int main() {int a 10;// 编译报错“ra”: 必须初始化引用//int ra;int b a;int c 20;cout a endl;cout b endl;cout c endl;// 这里并非让b引用c因为C引用不能改变指向// 这里是一个赋值b c;cout a endl;cout b endl;cout c endl;cout a endl;cout b endl;cout c endl; } 7.3.引用的使用 • 引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被引用对象。 • 引用传参跟指针传参功能是类似的引用传参相对更方便一些。 • 引用返回值的场景相对比较复杂我们在这里简单讲了一下场景还有一些内容后续类和对象章节中会继续深入讲解。 void Swap(int rx, int ry) {int tmp rx;rx ry;ry tmp; }void Swap(int* px, int* py) {}以实现交换功能为例,之前实现我们都需要传递指针才能通过函数形参改变实参但是现在引用是函数的别名函数形参是引用的格式实参直接传变量就行这样我们不用传指针通过引用就可以改变外面的值同时引用不会开辟空间我们还节省了空间。 此外对于取栈顶的STTOP过去在C语言内我们是无法通过返回值去直接修改栈内的数据的但是现在因为引用就是对应空间的别名我们返回引用我们就可以通过返回值直接修改栈内的值。 vectorint v; for (size_t i 0; i v.size(); i) {v[i] i; } 上诉的功能使我们之后修改vector内的值可以像数组那样处理。本文仅用于说明引用的价值不详细介绍 • 一些主要用C代码实现版本数据结构教材中使用C引用替代指针传参目的是简化程序避开复杂的指针但是很多同学没学过引用导致一头雾水。 #includeiostream using namespace std; typedef struct SeqList {int a[10];int size; }SLT; // 一些主要用C代码实现版本数据结构教材中使用C引用替代指针传参目的是简化程序避开复 //杂的指针但是很多同学没学过引用导致一头雾水。 void SeqPushBack(SLT sl, int x) {} typedef struct ListNode {int val;struct ListNode* next; }LTNode, * PNode; // 指针变量也可以取别名这里LTNode* phead就是给指针变量取别名 // 这样就不需要用二级指针了相对而言简化了程序 //void ListPushBack(LTNode** phead, int x) //void ListPushBack(LTNode* phead, int x) void ListPushBack(PNode phead, int x) {PNode newnode (PNode)malloc(sizeof(LTNode));newnode-val x;newnode-next NULL;if (phead NULL){phead newnode;}else{//...} } int main() {PNode plist NULL;ListPushBack(plist, 1);return 0; } 7.4.const引用 • 可以引用一个const对象但是必须用const引用。const引用也可以引用普通对象因为对象的访问权限在引用过程中可以缩小但是不能放大。 int main() {const int a 10;//int rd a;//int ra a;int b 0;const int rb b;b;//rb;//只可以读值不可以通过别名修改值const int ra a;const int rc 30;return 0; } const int a的权限是只可以使用值不可以修改值本身权限不能放大因此我们以相同的权限以const int 为a1起别名不能使用 int 起别名这样可以通过别名修改a,权限放大了。对于int b 我们即可以int起别名也可以以const int(只可以使用不可以修改)的权限缩小的方式为b起别名。此外const int也可以为常量起别名。 需要注意的是权限放大的问题只存在引用与对应对象之间两个对象间所使用的空间不同不存在权限相互干扰的问题。 • 不过需要注意的是类似int rb a*3; double d 12.34; int rd d; 这样一些场景下a*3的和结果保存在一个临时对象中 int rd d 也是类似在类型转换中会产生临时对象存储中间值也就是时rb和rd引用的都是临时对象而C规定临时对象具有常性所以这里就触发了权限放大必须要用常引用才可以。 • 所谓临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象C中把这个未命名对象叫做临时对象。 需要注意的是对表达式的值的结果使用引用、以不同类型引用变量发生类型转换或者整形提升等情形下我们都需要使用const引用。 以上图第一种类型为例当我们对表达式的结果引用时表达式的结果是先暂存在一个临时对象未命名的空间内这个未命名空间具有常性我们需要使用const引用。为这块未命名空间取别名后这块未命名空间不会立刻销毁就相当于成了一个正常的变量。 第二种情况由于引用的类型与对象的类型不同对象会发生整形提升或者类型转换这个过程的结果同样暂时存放在临时对象然后再对这个临时对象引用。 注以下图的push_back为例,const引用的价值在于使用之后我们可以往参数内放任意值表达式、常量、变量的别名不需要再开辟空间传值既方便又节省。这个在进一步学习C后会有更深的感受。 7.5.指针和引用的关系 C中指针和引用就像两个性格迥异的亲兄弟指针是哥哥引用是弟弟在实践中他们相辅相成功能有重叠性但是各有自己的特点互相不可替代。 • 语法概念上引用是一个变量的取别名不开空间指针是存储一个变量地址要开空间但是实际上在底层引用与指针是一致的引用就是指针不过一般来说我们更关心语法层面的效果。 引用与指针在汇编层面的指令区别一模一样 • 引用在定义时必须初始化指针建议初始化但是语法上不是必须的。 • 引用可以直接访问指向对象指针需要解引用才是访问指向对象。 • 引用在初始化时引用一个对象后就不能再引用其他对象而指针可以在不断地改变指向对象。以链表为例删除节点之后再插入节点需要改变指向所以这也进一步说明引用无法替代指针。 • sizeof中含义不同引用结果为引用类型的大小只是起了别名空间还是原大小的空间但指针始终是地址空间所占字节个数(32位平台下占4个字节64位下是8个字节) • 指针很容易出现空指针和野指针的问题引用很少出现引用使用起来相对更安全一些。也会出现空引用和野引用的概念但是几率很小 八、 inline 在C语言中我们会通过将简短常用的函数改写为宏函数来减少反复的开销。但是如下图所示想正确使用的宏函数的难度较大说不定什么时候就踩到坑里。因此C中就有inline内联函数来替代。 #includeiostream using namespace std; // 实现一个ADD宏函数的常见问题 //#define ADD(int a, int b) return a b; //#define ADD(a, b) a b; //#define ADD(a, b) (a b) // 正确的宏实现 #define ADD(a, b) ((a) (b)) // 为什么不能加分号? // 为什么要加外面的括号? // 为什么要加里面的括号? int main() {int ret ADD(1, 2);cout ADD(1, 2) endl;cout ADD(1, 2) * 5 endl;int x 1, y 2;ADD(x y, x | y); // - (xyx|y)return 0; } • 用inline修饰的函数叫做内联函数编译时C编译器会在调用的地方展开内联函数不是通过开辟栈桢来调用函数而是类似宏函数直接在调用的地方将函数的逻辑替换过去这样调用内联函数就不需要建立栈帧了就可以提高效率。 • inline对于编译器而言只是一个建议也就是说你加了inline编译器也可以选择在调用的地方不展开不同编译器关于inline什么情况展开各不相同因为C标准没有规定这个。inline适用于频繁调用的短小函数对于递归函数代码相对多一些的函数加上inline也会被编译器忽略。 之所以设计成这样是因为如果不加节制的将所有函数都展开那么函数过大且调用频繁的函数全部展开会出现代码膨胀的问题。 假设一个函数有大约一百条指令但是这个函数在10000个位置调用那么不展开是1000100不展开始终开辟栈桢调用同一份指令一但展开是10000*100条每个地方都替换展开。 这样代码膨胀可执行程序就会变大安装包会非常大严重影响用户体验。 • C语言实现宏函数也会在预处理时替换展开但是宏函数实现很复杂很容易出错的且不方便调试C设计了inline目的就是替代C的宏函数。 • vs编译器 debug版本下面默认是不展开inline的这样方便调试debug版本想展开需要设置一下以下两个地方。 #includeiostream using namespace std; inline int Add(int x, int y) {int ret x y;ret 1;ret 1;ret 1;return ret; } int main() {// 可以通过汇编观察程序是否展开// 有call Add语句就是没有展开没有就是展开了int ret Add(1, 2);cout Add(1, 2) * 5 endl;return 0; } • inline不建议声明和定义分离到两个文件分离会导致链接错误。因为inline被展开就没有函数地址链接时会出现报错。推荐将加了inline的函数直接写在头文件内。因为链接时看到有函数声明编译器会去符号表找到定义的地址来跳转但是因为加了inline,函数会替换展开因此不会将地址存到符号表内自然无法找到地址此外我们一般是包包含声明的头文件因此也无法直接找到定义最终链接错误。 // F.h #include iostream using namespace std; inline void f(int i); // F.cpp #include F.h void f(int i) {cout i endl; } // main.cpp #include F.h int main() {// 链接错误无法解析的外部符号 void __cdecl f(int) (?fYAXHZ)f(10);return 0; } 九、nullptr NULL实际是一个宏在传统的C头文件(stddef.h)中可以看到如下代码 #ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif #endif • C中NULL被定义为字面常量0或者C中被定义为无类型指针(void*)的常量。不论采取何种定义在使用空值的指针时都不可避免的会遇到一些麻烦遇到重载函数时本想通过f(NULL)调用指针版本的f(int*)函数但是由于NULL被定义成0调用了f(int x)因此与程序的初衷相悖。f((void*)NULL);调用会报错。 #includeiostream using namespace std; void f(int x) {cout f(int x) endl; } void f(int* ptr) {cout f(int* ptr) endl; } int main() {f(0);// 本想通过f(NULL)调用指针版本的f(int*)函数但是由于NULL被定义成0调用了f(int//x)因此与程序的初衷相悖。f(NULL);f((int*)NULL);// 编译报错error C2665: “f”: 2 个重载中没有一个可以转换所有参数类型// f((void*)NULL);f(nullptr);return 0; } 如上图需要补充的是C中NULL被定义为字面常量0因为C在相关检查方面比C语言更加严格与C语言中void*的指针可以给任意类型的指针不同C中void*无法给任意类型同时如果是隐式类型转换那又该转换成哪一个呢所以总的来说NULL在C内的使用是有不小的风险的。 • C11中引入nullptrnullptr是一个特殊的关键字nullptr是一种特殊类型的字面量它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题因为nullptr只能被隐式地转换为指针类型而不能被转换为整数类型。nullter替代NULL,称为空指针专用
http://www.dnsts.com.cn/news/35999.html

相关文章:

  • 北京云网站建设专业网站建设公司首选
  • 手机怎样下载安装建设银行网站xsl做书店网站
  • 重庆网站建站建设平台做设计在哪个网站上找高清图片大全
  • 免费seo网站诊断彩票走势图网站建设
  • 企业网站加视频专业做胶粘剂招聘网站
  • 爱奇艺会员做任务送十天网站中国有哪些网站可以做兼职
  • 建设网站首页应该采用网站制作企业首页
  • 网站如何做好用户体验建一个网站需要什么手续
  • 自己做的网站给人攻击了怎么办长清网站建设电话
  • 网站开发的技术问题顺义制作网站
  • 网站主题如何制作建筑工程师的工作内容
  • 做网站的公司好坑啊广州网站优化推荐
  • 合肥网站设计goz怎么查企业
  • 微信公众号授权给网站重庆网站建设公司推荐
  • 个人如何学习做网站软件开发学校排名
  • 有什么做视频的免费素材网站好自助建站免费永久
  • 网站建设大赛策划书郴州新网交友
  • 网站开发进度表成都 网页设计
  • 云南建设投资集团网站首页装饰工程造价
  • 推广网站如何做互联网营销师报考条件
  • 带做网站价位719y你会回来感谢我的
  • 高中生自己做网站推广链接赚钱
  • 临海建设局官方网站山东泰安微平台
  • 云信网站建设网站索引量暴增
  • 如何做网站推广最有效做网站连接数据库怎么显示图片
  • 怎么给网站加友情链接wordpress 搭建个人网站
  • 常州中小企业网站制作工程公司财务制度及流程
  • 如何wordpress建站卢松松博客源码 wordpress博客模板
  • 郑州老牌做企业网站7个免费的ui素材网站
  • 南宁信息建设网站wordpress rossi 汉化