文章目录一命名空间 namespace1namespace的价值2namespace的定义①namespace的形式②namespace的本质-C中的域相关3namespace的使用访问namespace内的内容 ::PS-头文件和命名空间的展开各自代表什么含义二输入输出1输出2输入代码三缺省参数1全缺省2半缺省四函数重载代码注意-偶然发现的编译、链接报错小知识五引用1定义2写法3特性有没有觉得最后两点莫名矛盾例题分析4使用5const引用常引用注意指针和引用的区别六inline 和 nullptr1inline内联函数的写法2nullptrC11后才添加注void*和任意类型的指针之间的转换一命名空间 namespace1namespace的价值一言以蔽之用于避免命名冲突。原因同一域中不能定义同名变量。2namespace的定义①namespace的形式1.写在全局处namespace域名{ 可以定义变量、函数和类型等定义/声明 }namespaceyuan{//定义rand变量intrand10//定义自定义类型student结构体structstudent{intname;doubleweight;}//定义函数intAdd(intx,inty){returnxy;}}intmain(){intayuan::rand;structyuan::studentpNULL;printf(%d,Add(1,2));}不能写在namespace里所有可执行语句函数调用、循环分支、表达式、赋值语句…… 一旦写了编译器直接报错2.namespace只能定义在全局处可以嵌套定义3.项目工程中多文件的同名namespace会被认为是同一个namespace不会冲突逻辑上的合并4.C标准库放在一个名为std(standard)的命名空间里。②namespace的本质namespace的本质是定义出一个域这个域和全局域各自独立。由于不同的域里是可以定义同名变量的所以命名冲突就可以被解决了。-C中的域相关C中有局部域、全局域、命名空间域和类域。域影响编译时语法查找一个变量/函数/类型出处(声明/定义)的逻辑。全局域和局部域除了会影响查找出处逻辑还会影响变量的生命周期命名空间域和类域不影响变量的生命周期。命名空间内的内容属于全局变量生命周期是全局的只是加了一层限制罢了。3namespace的使用访问namespace内的内容 ::①指定命名空间单个访问namespaceyuan{//定义rand变量intrand10//定义自定义类型student结构体structstudent{intname;doubleweight;}//定义函数intAdd(intx,inty){returnxy;}}intmain(){//指定命名空间单个访问intryuan::rand;//命名空间yuan 通配符:: 命名空间内的变量randsturct yuan::student pNULL;//命名空间中的结构体访问命名空间yuan和通配符::写在struct和标签名之间printf(%d,Add(1,2));}PSa.如果只使用通配符::默认访问全局同名变量。命名空间之外b.在嵌套定义中想访问最里层命名空间的某个内容就需要逐层使用: :;#includestdio.hnamespaceyuan{inta0;namespaceserverus{intb10;}}inta20;intmain(){inta10;intb30;printf(%d\n,yuan::serverus::b);}②using将命名空间内的某个成员展开#includestdio.hnamespaceyuan{inta0;namespaceserverus{intb10;}}usingyuan::serverus::b;//将yuan命名空间中的serverus命名空间中的b成员展开inta20;intmain(){inta10;//int b 30;//printf(%d\n, yuan::serverus::b);printf(%d,b);//这样就可以直接访问到yuan命名空间中的serverus命名空间中的b了}③展开命名空间内的所有成员日常练习可以使用但实际的项目开发不建议用#includestdio.hnamespaceyuan{inta0;namespaceserverus{intb10;}}usingnamespaceyuan;//展开yuan命名空间usingnamespaceyuan::serverus;//展开yuan中的serverus命名空间//格式using namespace 命名空间名intmain(){//int a 10;//int b 30;printf(%d\n,a);printf(%d\n,b);}-头文件和命名空间的展开各自代表什么含义①头文件展开#include 是直接把整个头文件的全部文本原封不动复制粘贴到当前.c文件里不管是声明、宏、结构体、注释全部照搬不会自动筛选只复制函数声明更不会复制函数声明对应的函数实现。由于初学C有些地方习惯先从C的角度理解②命名空间的展开8可以理解成打通了一条通向命名空间内部的道路让编译器找东西时可以进入命名空间的房间里搜查。二输入输出【由于作者初学C故而只摆概念不解释。】 iostream是Input Output Stream的缩写是标准的输⼊、输出流库定义了标准的输⼊、输出对象我目前理解它就是一个C输入输出函数需要包含的头文件放在std命名空间里。std::cin是istream类的对象它主要⾯向窄字符narrow characters (of type char)的标准输⼊流。std::cout是ostream类的对象它主要⾯向窄字符的标准输出流。std::endl是⼀个函数流插⼊输出时相当于插⼊⼀个换⾏字符加刷新缓冲区(换行作用类似于C语言中的\n转义字符。1输出① 是流插⼊运算符是流提取运算符。C语⾔还⽤这两个运算符做位运算左移/右移②使⽤C输⼊输出更⽅便不需要像printf/scanf输⼊输出时那样需要⼿动指定格式C的输⼊输出可以⾃动识别变量类型(本质是通过函数重载实现的这个以后会讲到)其实最重要的是C的流能更好的⽀持⾃定义类型对象的输⼊输出。2输入注意输入用 , 输出用 ,不要用反了代码//输入输出#includeiostreamusingnamespacestd;intmain(){inta10;doubleb2.5;charcx;couta b cendl;couta b c\n;couta b c\n;cinabc;couta b c;return0;}三缺省参数补充解释一下最后一点如果只有函数定义在函数定义上写缺省值是合法的但是如果同时有函数定义和函数声明那么就规定只能在声明处给缺省值。文字描述说了也是一知半解看看具体代码实现1全缺省全缺省调用函数不传任何实参形参的值全部采用缺省值。//全缺省#includeiostreamusingnamespacestd;//函数声明intAdd(intx5,inty10);intmain(){//函数调用intretAdd();coutretendl;return0;}//函数定义intAdd(intx,inty){returnxy;}在这里也能看到想给缺省值是放到函数声明里的。2半缺省半缺省只能从右往左给缺省值函数调用是实参只能从左往右给。理解实参拷贝的顺序就能明白为啥函数声明里的缺省值是从右往左的了两者要互补。#includeiostreamusingnamespacestd;//半缺省//函数声明//只能从右往左给缺省值intAdd(intx,inty20);intmain(){intretAdd(2);coutretendl;return0;}//函数定义intAdd(intx,inty){returnxy;}四函数重载C⽀持在同⼀作⽤域中出现同名函数但是要求这些同名函数的形参不同可以是参数个数、类型或者顺序不同对返回值类型和函数体无要求。这样C函数调⽤就表现出了多态⾏为使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。好处C中有了函数重载就不需要写一堆逻辑一样但形参不同的函数了。代码返回值无所谓 函数名必须相同 形参个数、类型或者顺序必须不同//函数定义doubleAdd(intx,doubley){returnxy;}//形参类型不同intAdd(intx,inty){returnxy;}//形参个数不同intAdd(intx,doubley,intz){returnxyz;}//形参顺序不同doubleAdd(doubley,intx)//double Add(double x, int y) 同样可以{returnxy;}注意①下面三个函数定义不构成重载它们是完全一样的。当函数交换顺序的形参是同一类型或者只是改变了形参的名字这两种情况下不会构成重载编译会报错。intAdd(intx,inty){returnxy;}intAdd(inty,intx){returnxy;}intAdd(inta,intb){returnab;}②放在不同域内的同名函数不是函数重载。#includeiostreamusingnamespacestd;//形参类型不同intAdd(intx,inty){returnxy;}namespaceyuan{intAdd(intx,inty){returnxy;}intAdd(intx,doubley){returnxy;}}intmain(){return0;}一个是全局域一个是命名空间域当然不会构成重载这种编译不报错。-偶然发现的编译、链接报错小知识在VS中编译报错是以C开头的而链接报错是以LNK开头的。五引用1定义2写法类型引用别名引用对象#includeiostreamusingnamespacestd;intmain(){inta9;//b是a变量的别名b不开空间b代表a也就是9intba;//c又是b的别名其实也就是a的别名依旧不开空间c也代表a也就是9//可以给别名取别名intcb;coutaendl;coutbendl;coutaendl;coutbendl;return0;}3特性引用在定义时必须初始化一个变量可以有多个引用别名发生改变了就是变量发生改变了引用一旦引用了一个实体就不能再引用其他实体。有没有觉得最后两点莫名矛盾—— 既然都不能引用别的实体有何谈改变呢但其实不矛盾。不能改变一个已经引用了实体的引用去引用其他实体但是可以改变引用自身的值。简单说就是如果a10b是a的引用那么b可以改变值为20、30但是b不能成为c的引用。例题分析#includeiostreamusingnamespacestd;intmain(){inta10;intba;intc20;bc;//这是让b引用c还是将c的值赋给bcoutbendl;couta bendl;coutc bendl;return0;}将c的值赋给bb20由于b是a的别名和它共用同一块存储空间a的值也会变成20。打印a、b地址时自然也是一样的地址。4使用5const引用常引用使用指针和引用是间接访问变量相当于开辟出一条额外的通道而这条通道的权限是有限制的它可以缩小但是不能放大即通过指针、引用访问对象的权限可以不变、缩小但是不能放大。#includeiostreamusingnamespacestd;intmain(){//情况一讲明权限放大的报错情况constinta10;// a不能被修改intba;//会报错此时b是a的别名如果通过b是可以改变a的值的//这说明对象的访问权限放大了constintrba;//不会报错//使用常引用rb也不能修改则a访问权限并未发生变化//情况二: 讲明权限缩小的合法情况intc20;//c变量可以修改constintrcc;//不会报错rc引用不能修改值也就是不能修改c的值权限缩小了但是是合法的//注意: 引用的访问权限不会改变原变量的访问权限rc;//报错c;//不报错//情况三讲明C中规定临时对象具有常性不能被修改//临时对象数值字面量、返回值、表达式的值、类型转换后的值无论显隐//数值字面量intd10;//会报错//10是一个临时对象具有常性不可修改生命周期在本语句结束后结束//使用普通引用一会放大访问权限二是在本条语句结束后临时对象的空间被销毁了可它的别名却还存在会造成“悬空引用”野引用//普通引用无法延长临时对象的生命周期但是常引用可以延长临时对象的生命周期那常引用什么时候销毁临时对象就什么时候销毁constintrd10;//表达式的值inte10;intf20;intgef;//会报错表达式的值会放在一个临时对象里临时对象具有常性应该用常引用constintrgef;//类型转换后的值inth10;doubleih;//会报错i引用的不是h而是类型转换后放在临时对象里的值//而临时对象不能修改它的引用要是常引用constdoublerih;return0;}注意①讲明C中规定临时对象具有常性不能被修改。临时对象常量数字、返回值、表达式的值、类型转换后的值②普通引用无法延长临时对象的生命周期但是常引用可以延长临时对象的生命周期那常引用什么时候销毁临时对象就什么时候销毁。const引用的价值不止以上几条在一些需要用引用接收参数的形参上也可以使用const这样就可以接收更多类型的值常量数字、表达式的值、类型转换后的值、函数返回值…如果是普通引用就不能接收这些放在临时变量里具有常性的值了因为这是权限放大不合法。指针和引用的区别六inline 和 nullptr1inline也就是说针对一些频繁调用的短小函数可以使用内联编译器也会听你的但是如果是一些复杂的函数即使加上内联编译器也可能不会听你的。内联函数的写法直接在普通函数的返回值前加一个inline就好。inlineintADD(inta,intb){returnab;}2nullptrC11后才添加NULL实际是⼀个宏在传统的C头⽂件(stddef.h)中可以看到如下代码#ifndefNULL#ifdef__cplusplus#defineNULL0#else#defineNULL((void*)0)#endif#endif可以看到在C中NULL宏定义为0那么在如下的函数调用时就会存在问题#includeiostreamusingnamespacestd;voidf(intx){coutf(int x)endl;}voidf(int*ptr){coutf(int* ptr)endl;}intmain(){f(0);//调用f(int x)函数f(NULL);// 本想通过f(NULL)调⽤指针版本的f(int*)函数但是由于NULL被定义成0调⽤了f(int x)因此与程序的初衷相悖。f((int*)NULL);//成功调用f(int*)函数f((void*)NULL);// 编译报错// void*既不适合int参数又不适合int*参数——注f(nullptr);//成功调用f(int*)函数return0;}C11中引⼊nullptrnullptr是⼀个特殊的关键字nullptr是⼀种特殊类型的字⾯量它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题因为nullptr只能被隐式地转换为指针类型⽽不能被转换为整数类型。注void*和任意类型的指针之间的转换在C语言中void类型的指针和任意类型的指针之间的转换都支持隐式转换不写强转编译器也不会报错但是在C中void类型的指针和任意类型的指针之间的转换不支持隐式转换编译时会报错。除非显示强制类型转换。初学C如有错误欢迎指出——end——