山西省建设厅网站官网,html5手机论坛网站模板,网站建设用哪个,百度人工1. 前言
1. 什么是C C语言是结构化和模块化的语言#xff0c;适合处理较小规模的程序。对于复杂的问题#xff0c;规模较大的程序#xff0c;需要高度的抽象和建模时#xff0c;C语言则不合适。为了解决软件危机#xff0c; 20世纪80年代#xff0c; 计算机界提出了OOP(…1. 前言
1. 什么是C C语言是结构化和模块化的语言适合处理较小规模的程序。对于复杂的问题规模较大的程序需要高度的抽象和建模时C语言则不合适。为了解决软件危机 20世纪80年代 计算机界提出了OOP(object oriented programming面向对象)思想支持面向对象的程序设计语言应运而生。 1982年Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念发明了一种新的程序语言。为了表达该语言与C语言的渊源关系命名为C。因此C是基于C语言而产生的它既可以进行C语言的过程化程序设计又可以进行以抽象数据类型为特点的基于对象的程序设计还可以进行面向对象的程序设计。 2. 命名空间
2.1 由来和性质 在C/C中变量、函数和后面要学到的类都是大量存在的这些变量、函数和类的名称将都存在于全局作用域中可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化以避免命名冲突或名字污染namespace关键字的出现就是针对这种问题的。 namespace 空间还可以嵌套定义
2.2 命名空间的使用
命名空间的使用有三种方式
1 加命名空间名称及作用域限定符
#include iostream//std C官方库内容定义的命名空间
int main()
{std::cout hello world std::endl;std::cout hello world std::endl;std::cout hello world std::endl;return 0;
}2 使用using将命名空间中某个成员引入
#include iostream
// 指定展开 -- 常用展开自己定义的时候避免跟常用重名即可
using std::cout;int main()
{cout hello world std::endl;cout hello world std::endl;std::cout hello world std::endl;return 0;
}3 使用using namespace 命名空间名称引入
#include iostream
using namespace std;int main()
{cout Hello world!!! endl;cout Hello world!!! \n;return 0;
}3. C输入输出 说明 使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时必须包含 iostream 头文件以及按命名空间使用方法使用std。cout和cin是全局的流对象endl是特殊的C符号表示换行输出他们都包含在包含 iostream 头文件中。是流插入运算符是流提取运算符。使用C输入输出更方便不需要像printf/scanf输入输出时那样需要手动控制格式。 C的输入输出可以自动识别变量类型 注意早期标准库将所有功能在全局域中实现声明在.h后缀的头文件中使用时只需包含对应头文件即可后来将其实现在std命名空间下为了和C头文件区分也为了正确使用命名空间规定C头文件不带.h旧编译器(vc 6.0)中还支持iostream.h格式后续编译器已不支持因此推荐使用iostreamstd的方式。
std是C标准库的命名空间如何展开std使用更合理呢
在日常练习中建议直接using namespace std即可这样就很方便。using namespace std展开标准库就全部暴露出来了如果我们定义跟库重名的类型/对象/函数就存在冲突问题。该问题在日常练习中很少出现但是项目开发中代码较多、规模大就很容易出现。所以建议在项目开发中使用像std::cout这样使用时指定命名空间 using std::cout 展开常用的库对象/类型等方式。
4. 缺省参数
4.1 缺省参数概念 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时如果没有指定实参则采用该形参的缺省值否则使用指定的实参。 4.2 缺省参数分类
全缺省参数半缺省参数 缺省值必须是常量或者全局变量 缺省值的使用场景
5. 函数重载 函数重载是函数的一种特殊情况C允许在同一作用域中声明几个功能类似的同名函数。 这些同名函数的形参列表(参数个数 或 类型 或类型顺序)不同常用来处理实现功能类似数据类型不同的问题。 5.1 函数重载概念 5.2 C支持函数重载的原理–名字修饰(name Mangling)
为什么C支持函数重载而C语言不支持函数重载呢? 在C/C中一个程序要运行起来需要经历以下几个阶段预处理、编译、汇编、链接。 实际项目通常是由多个头文件和多个源文件构成而通过C语言阶段学习的编译链接我们 可以知道【当前a.cpp中调用了b.cpp中定义的Add函数时】编译后链接前a.o的目标文件中没有Add的函数地址因为Add是在b.cpp中定义的所以Add的地址在b.o中。那么怎么办呢所以链接阶段就是专门处理这种问题链接器看到a.o调用Add但是没有Add的地址就 会到b.o的符号表中找Add的地址然后链接到一起。那么链接时面对Add函数链接接器会使用哪个名字去找呢每个编译器都有自己的函数名修饰规则由于Windows下vs的修饰规则过于复杂而Linux下g的修饰规则简单易懂通过观察来理解名字的修饰规则通过下面我们可以看出gcc的函数修饰后名字不变。而g的函数修饰后变成【_Z函数长度 函数名类型首字母】。 Linux下的名字修改规则 Windows下的名字修改规则 相较于Linuxwindows下vs编译器对函数名字修饰规则相对复杂难懂但大致思想相同对C语言进行名字修饰的优化 这里不过多赘述。C语言没办法支持重载因为同名函数没办法区分。 而C是通过函数修饰规则来区分只要参数不同修饰出来的名字就不一样就支持了重载。如果两个函数函数名和参数是一样的返回值不同是不构成重载的因为调用时编译器没办法区分。 6. 引用
6.1 引用概念
引用不是新定义一个变量而是给已存在变量取了一个别名编译器不会为引用变量开辟内存空间它和它引用的变量共用同一块内存空间。 比如李逵在家称为 “铁牛”江湖上人称 “黑旋风”。
6.2 引用特性 引用在定义时必须初始化一个变量可以有多个引用引用一旦引用一个实体再不能引用其他实体 6.3 使用场景
6.3.1 做参数 6.3.2 做返回值 注意如果函数返回时出了函数作用域如果返回对象还在(还没还给系统)则可以使用引用返回如果已经还给系统了则必须使用传值返回。 6.4 常引用 当然如果是需要交换变量的函数Swap(int x,int y)参数不能采用常引用
7. 内联函数
7.1 概念 以inline修饰的函数叫做内联函数编译时C编译器会在调用内联函数的地方展开没有函数调用建立栈帧的开销内联函数提升程序运行的效率。 如果在上述函数前增加inline关键字将其改成内联函数在编译期间编译器会用函数体替换函数的调用。 查看方式 在release模式下查看编译器生成的汇编代码中是否存在call Add在debug模式下需要对编译器进行设置否则不会展开(因为debug模式下编译器默认不 会对代码进行优化以下给出vs2013的设置方式) 7.2 特性 inline是一种以空间换时间的做法如果编译器将函数当成内联函数处理在编译阶段会用函数体替换函数调用 缺陷可能会使目标文件变大优势少了调用开销提高程序运行效率。inline对于编译器而言只是一个建议不同编译器关于inline实现机制可能不同一般建议将函数规模较小(即函数不是很长具体没有准确的说法取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰否则编译器会忽略inline特性。 下图为 《Cprime》第五版关于inline的建议 inline不建议声明和定义分离分离会导致链接错误。因为inline被展开就没有函数地址了链接就会找不到。 内联函数的编译链接过程
小知识点: 8. auto关键字(C11)
8.1 类型别名思考
随着程序越来越复杂程序中用到的类型也越来越复杂经常体现在
类型难于拼写含义不明确导致容易出错
8.2 auto简介 在早期C/C中auto的含义是使用auto修饰的变量是具有自动存储器的局部变量但遗憾的是一直没有人去使用它 为什么 C11中标准委员会赋予了auto全新的含义即auto不再是一个存储类型指示符而是作为一个新的类型指示符来指示编译器auto声明的变量必须由编译器在编译时期推导而得。 8.3 auto的使用细则
auto与指针和引用结合起来使用 用auto声明指针类型时用auto和auto*没有任何区别但用auto声明引用类型时则必须加 【注意】 使用auto定义变量时必须对其进行初始化在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明而是一个类型声明时的“占位符”编译器在编 译期会将auto替换为变量实际的类型。 在同一行定义多个变量 当在同一行声明多个变量时这些变量必须是相同的类型否则编译器将会报错。 因为编译器实际只对第一个类型进行推导然后用推导出来的类型定义其他变量
void TestAuto()
{auto a 1, b 2; auto c 3, d 4.0; // 该行代码会编译失败因为c和d的初始化表达式类型不同
}8.3 auto不能推导的场景
1. auto不能作为函数的参数
// 此处代码编译失败auto不能作为形参类型因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}2. auto不能直接用来声明数组
void TestAuto()
{int a[] {1,2,3};auto b[] {456};
}3. 为了避免与C98中的auto发生混淆C11只保留了auto作为类型指示符的用法
9. 基于范围的for循环(C11)
9.1 范围for的语法
在C98中如果要遍历一个数组可以按照以下方式进行
void TestFor()
{
int array[] { 1, 2, 3, 4, 5 };
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循环后的括号由冒号“ ”分为两部分第一部分是范围内用于迭代的变量第二部分则表示被迭代的范围。 9.2 范围for的使用条件 for循环迭代的范围必须是确定的 对于数组而言就是数组中第一个元素和最后一个元素的范围 对于类而言应该提供 begin和end的方法begin和end就是for循环迭代的范围。 注意以下代码就有问题因为for的范围不确定 void TestFor(int array[])
{for(auto e : array)cout e endl;
}在C中数组不能作为参数接收函数只会接收作为数组的首元素地址
10. 指针空值nullptr(C11)
10.1 C98中的指针空值 在良好的C/C编程习惯中声明一个变量时最好给该变量一个合适的初始值否则可能会出现不可预料的错误比如未初始化的指针。 如果一个指针没有合法的指向我们基本都是按照如下方式对其进行初始化 void TestPtr()
{int* p1 NULL;int* p2 0;// ……
}NULL实际是一个宏在传统的C头文件(stddef.h)中可以看到如下代码
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif可以看到NULL可能被定义为字面常量0或者被定义为无类型指针(void*)的常量。不论采取何种定义在使用空值的指针时都不可避免的会遇到一些麻烦比如 程序本意是想通过f(NULL)调用指针版本的f(int*)函数但是由于NULL被定义成0因此与程序的初衷相悖。 在C98中字面常量0既可以是一个整形数字也可以是无类型的指针(void*)常量但是编译器默认情况下将其看成是一个整形常量如果要将其按照指针方式来使用必须对其进行强转(void *)0。 注意
在使用nullptr表示指针空值时不需要包含头文件因为nullptr是C11作为新关键字引入的。在C11中sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。为了提高代码的健壮性在后续表示指针空值时建议最好使用nullptr。