全网有哪些网站可以做淘客,竞价网络推广托管,泰安招聘,p2p网站建设要多少钱目录
一、关键字
二、命名空间
问题引入(问题代码)#xff1a;
域的问题
1.::域作用限定符 的 用法#xff1a;
2.域的分类
3.编译器的搜索原则
命名空间的定义
命名空间的使用
举个#x1f330;栗子#xff1a;
1.作用域限定符指定命名空间名称
2. using 引入…目录
一、关键字
二、命名空间
问题引入(问题代码)
域的问题
1.::域作用限定符 的 用法
2.域的分类
3.编译器的搜索原则
命名空间的定义
命名空间的使用
举个栗子
1.作用域限定符指定命名空间名称
2. using 引入命名空间中的成员 即 展开命名空间中某一个
3. usinng namespace 命名空间名称 展开命名空间
三、C输入、输出
四、缺省参数
概念
全缺省参数
半缺省参数
实践中的应用场景举个例子:
声明和定义分离
回顾 声明 和定义的概念
再来分析上述程序
理解编译与链接的过程
理解函数与文件的关系
五、函数重载
代码示例
C是如何支持函数重载的
函数名修饰 一、关键字
asmdoifreturntrycontinueautodoubleinlineshorttypedefforbooldynamic_castintsignedtypeidpublicbreakelselongsizeoftypenamethrowcaseenummutablestaticunionwchar_tcatchexplicitnamespacestatic_castunsigneddefaultcharexternoperatorswitchvirtualregisterconstfalseprivatetemplatevoidtrueconst_castfloatprotectedthisvolatilewhiledeletegotoreinterpret_cast
增加的关键字: C增加了一些关键字来支持面向对象编程如类、继承、多态等和模板编程。例如classpublicprotectedprivatevirtualfriendtemplatetypename等。这些关键字没有在C语言中。
类型增强C增加了一些用于类型安全和方便的关键字如booltruefalseusingnamespace等。
异常处理为了支持异常处理C引入了trycatchthrow等关键字。
新的转换操作符C提供了static_castdynamic_castconst_cast和reinterpret_cast等关键字进行类型转换这是C语言中所没有的。
增强的存储类说明符C引入了mutable和thread_local等存储类说明符。
模板编程为了支持泛型编程C增加了template和typename关键字。
新增运算符C还定义了如newdelete等用于动态内存管理的关键字这些在C中通常通过库函数如malloc和free来实现。
特殊成员函数关键字C还有如default和delete等关键字用于特殊成员函数的声明这样设计是为了提供更好的控制。
二、命名空间
问题引入(问题代码)
下面代码存在命名冲突 : rand变量 和头文件stdlib.h中声明的函数 rand() 名字相同 导致冲突。 #includestdio.h
#includestdlib.h /*rand*/
int rand 0;
// C语言没办法解决类似这样的命名冲突问题所以C提出了namespaceguan来解决
int main()
{printf(%d\n,rand);return 0;
}
域的问题
1.::域作用限定符 的 用法
限定符左边是哪一个域名 就限定了访问该变量的范围
左边是空 默认是全局域
2.域的分类 全局域 局部域如果不用限定符默认访问局部域 局部优先 命名空间域为了防止命名冲突 eg.全局定义两个同名变量 防止重定义C提出就用关键字namespace把他们定义在不同命名空间域中。 类域 注意 全局域、局部域既会影响生命周期也会影响访问。命名空间只影响访问
3.编译器的搜索原则 1️⃣当前局部域 2️⃣全局域 3️⃣如果指定了直接去指定域搜索
命名空间的定义
正常定义 // 正常的命名空间定义
namespace hhh
{// 命名空间中可以定义变量/函数/类型int rand 10;int Add(int left, int right){return left right;}struct Node{struct Node* next;int val;};}
嵌套定义
举个栗子:
namespace aaa
{namespace bbb{void Push(){coutzsendl;}}namespace ccc{void Push(){coutyyyendl;}}
}
int main()
{//嵌套定义在命名空间的同名函数 各自调用bit::bbb::Push();bit::ccc::Push();return 0;
}
ps:命名空间可以重名编译器会把他们合并只要命名空间内部不冲突就可以
命名空间的使用
命名空间到底该如何使用
举个栗子
namespace yyy
{//命名空间中定义 变量 / 函数 /类型int a 0;int b 1;int Add(int left,int right){return leftright;}struct Node{struct Node* next;int val;};
}
1.作用域限定符指定命名空间名称
//指定访问
int main()
{//::作用域限定符printf(%d\n,yyy::a);return 0;
}
2. using 引入命名空间中的成员 即 展开命名空间中某一个
//展开一个
using yyy::b;
int main()
{printf(%d\n,yyy::a);//不可以 因为此时只展开了一个成员变量printf(%d\n,b);
}
3. usinng namespace 命名空间名称 展开命名空间
展开命名空间 影响的是 域的搜索规则。不展开命名空间默认情况编译器只会在局部域、全局域搜索。展开命名空间就可以在命名空间里搜索。
//展开全部
using namespace yyy;
int main()
{printf(%d\n,yyy::a);//指定去该命名空间找变量aprintf(%d\n,b)
} 注意 1. 日常练习展开为了方便使用可以展开std实际工程实践中慎重使用 2.展开命名空间 不是 等同于引入全局变量 3.展开命名空间 跟 包含头文件 也有本质区别包含头文件 在预处理过程中本质是拷贝头文件的内容 三、C输入、输出
解释Hello world代码
//包含标准输入输出流库
#includeiostream
// std是C标准库的命名空间名C将标准库的定义实现都放到这个命名空间中
using namespace std;int main(){//cout和cin是全局的流对象细说分别是ostream和istream类型的对象// 是流插入运算符是流提取运算符//endl是C符号表示endline换行//他们都包含在包含iostream头文件中coutHello world!!!endl;return 0;} 说明使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时必须包含 iostream 头文件 以及按命名空间使用方法使用std。 补充std命名空间的使用习惯 1.日常练习直接展开 using namespace std 2.项目开发std::cout 使用时指定命名空间 using std::cout 展开常用库对象 C 输入输出 自动识别变量类型 示例代码: #include iostream
using namespace std;
int main()
{int a;double b;char c;// 可以自动识别变量的类型cina;cinbc;coutaendl;coutb cendl;return 0;
} 说明
cina;这行代码从标准输入流键盘中接受一个整数并将其存储在变量a中。cin会根据提供的变量类型自动解释输入数据。cinbc;这行代码首先从标准输入流中接收一个双精度浮点数并将其存储在变量b中然后接收一个字符并存储在c中。 四、缺省参数 概念 声明或定义函数时为函数的参数指定缺省值。缺省值就是给形参设置一个默认值。调用函数时如果没有指定实参则使用参数的默认值。 缺省值必须是 常量或者全局变量。一般使用常量。 void Func(int a 0)
{coutaendl;
}
int main()
{Func(); //没有传参 使用参数默认值 Func(10); //传参时 使用指定的实参return 0;
} 全缺省参数 void Func(int a 10, int b 20, int c 30)
{couta aendl;coutb bendl;coutc cendl;
}
调用Func()时可以这样给参数int main()
{Func(1,2,3);Func(1,2);Func(1);Func();//注意不可以跳越传值//Func(,1,2);return 0;
} 半缺省参数 注意只能从右往左连续给缺省值,这样调用保证传的实参顺序不存在歧义 void Func(int a, int b 20, int c 30)
{couta aendl;coutb bendl;coutc cendl;
}
//调用 同样不能跳越给
int main()
{Func(1,2,3);Func(1,2);Func(1);
}
实践中的应用场景举个例子:
struct Stack
{int* a;int size;int capacity;//...
};
//StackInit()改造为半缺省函数 使得可以适用更多的需要开辟空间的场景
void StackInit(struct Stack* ps,int n4)
{ps-a(int*)malloc(sizeof(int)*n);
}
int main()
{struct Stack st1;//缺省参数 使得函数可以适应不同场景 // 1、确定要插入100个数据StackInit(st1, 100); // call StackInit(?)
// 2、只插入10个数据struct Stack st2;StackInit(st2, 10); // call StackInit(?)
// 3、不知道要插入多少个 //这时就可以使用函数定义里提供的 参数缺省值 //不知道插入多少个 可以先初始化四个空间struct Stack st3;StackInit(st3);
return 0;
} 声明和定义分离 回顾 声明 和定义的概念 函数声明告诉编译器函数的名称、返回类型以及参数列表类型、顺序和数量但不涉及函数的具体实现。函数声明经常出现在头文件.h中 函数定义提供了函数的实际实现它包括函数的主体即函数被调用时将执行的具体代码。函数定义包含了函数声明的所有信息并加上了函数体 //Stack.h 声明
struct Stack
{int* a;int size;int capacity;//...
};
void StackInit(struct Stack* ps,int n4);//*注意 必须在声明中给出缺省值
void StackPush(struct Stack* ps,int x);
//Stack.cpp 定义
void StackInit(struct Stack* ps,int n)//*注意声明和定义中缺省值不能同时给
{ps-a(int*)malloc(sizeof(int)*n);
}
void StackPush(struct Stack* ps , int x)
{}
//Test.cpp
#includeStack.h
int main()
{struct Stack st1;// 1、确定要插入100个数据StackInit(st1, 100); // call StackInit(?)//此时包含了头文件Test.cpp只有函数声明 用这个函数的名字找到该函数的地址 编译阶段会检查调用该函数是否存在匹配的函数经过检查 匹配// 2、只插入10个数据struct Stack st2;StackInit(st2, 10); // call StackInit(?)// 3、不知道要插入多少个 struct Stack st3;StackInit(st3);return 0;
} 但是试想一下1️⃣如果缺省值只在函数定义中给出编译阶段 无法用这个函数的名字找到该函数的匹配 因为调用传参跟函数声明并不匹配。另一种情况2️⃣如果在函数的声明和定义中都指定了缺省参数编译器也可能不确定应该使用哪个版本的默认值为了避免这种情况C标准规定了缺省参数应当只在一个地方指定 如果函数声明在头文件中进行那么就在头文件中的声明处指定缺省参数 如果函数没有在头文件中声明例如完全在一个.cpp文件内定义那么就在函数定义处指定缺省参数 综上 1️⃣在项目中声明和定义应当分离缺省值一定要在函数声明中给出!因为编译阶段只有函数声明从而保证编译阶段是没有问题的。 2️⃣声明和定义分离导致编译阶段无法找到函数的定义没有函数的地址。 再来分析上述程序 理解编译与链接的过程 1️⃣预处理阶段 展开头文件、宏替换、条件编译、删除注释 对于每个.c文件编译过程从预处理开始。预处理器会处理以#开头的指令例如#include stack.h会将stack.h中的内容文本上粘贴到stack.c和test.c文件中这样stack.c和test.c就可以看到这些函数声明了 2️⃣编译检查语法➡️生成汇编代码 编译器接着编译每个.c源文件将它们转换成目标代码通常是机器代码的一种中间形态称为目标文件扩展名为.o或.obj。此时编译器确保源代码符合语法规则对每个源文件进行类型检查确保所有函数调用都符合其声明但还不解决跨文件的函数引用问题。例如stack.c被编译成stack.otest.c被编译成test.o 3️⃣汇编汇编代码➡️二进制机器码 4️⃣链接合并、有些地方要用函数名去其他文件找函数地址 一旦所有的源文件被编译成目标文件链接器(linker)负责将这些目标文件以及必要的库文件链接成一个单一的可执行文件。在链接过程中如果test.c对应的是test.o调用了stack.c中对应的是stack.o的函数链接器负责“修补”这些调用使得test.o中的调用可以正确地连接到stack.o中定义的函数上链接器确保所有外部引用都能正确解析到它们所引用的实体。 理解函数与文件的关系 在stack.h中声明的函数让其他源文件知道这些函数的存在、它们的参数以及返回值类型。stack.h扮演了接口的角色。 stack.c提供了stack.h中声明的函数的具体实现。test.c作为使用这些函数的客户端代码通过#include stack.h能够调用这些函数。 编译过程中test.c和stack.c分别被编译成中间的目标文件。这些目标文件中的函数调用尚未解析到具体的地址 在链接过程链接器解析这些调用使得从test.o中的调用可以正确地定位到stack.o中的函数定义从而生成一个完整的可执行文件所有的函数调用都被正确地解析和连接这个地址修正的过程也叫做重定位 五、函数重载
C语言不允许同名函数
C允许同名函数。要求函数名相同参数不同构成 函数重载 函数重载是函数的一种特殊情况C允许在同一作用域中声明几个功能类似的同名函数这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同常用来处理实现功能类似但数据类型不同的问题。 代码示例
#includeiostreamusing 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语言不支持重载 链接时直接用函数名去找地址有同名函数的情况则区分不开。 C是如何支持函数重载的 通过函数名修饰实现的只要函数参数不同函数名就会被修饰成不同。然后直接用修饰好的名字去找该函数的地址。 函数名修饰 名字修饰是编译器自动进行的一种处理过程它将C源代码中的函数名和变量名转换成包含更多信息的唯一标识符。这些信息通常包括函数的参数类型、参数数量等甚至可能包括所属的类名对于类成员函数通过这种方式每个重载的函数都会被赋予一个独一无二的名字确保链接器在最后链接程序的时候能够区分它们
Linux下g的修饰规则简单易懂下面我们使 用了g演示了这个修饰后的名字。 通过下面我们可以看出gcc的函数修饰后名字不变。而g的函数修饰后变成【_Z函数长度 函数名类型首字母】。 采用C语言编译器编译后结果 结论在linux下采用gcc编译完成后函数名字的修饰没有发生改变。 采用C编译器编译后结果 结论在linux下采用g编译完成后函数名字的修饰发生改变编译器将函数参数类型信息添加到修改后的名字中。 通过以上这里就理解了C语言没办法支持重载因为同名函数没办法区分。而C是通过函数修 饰规则来区分只要参数不同修饰出来的名字就不一样就支持了重载。