北京市建设厅网站首页,大型网站建设地址,爱链接外链购买,小程序定制开发报价专栏#xff1a;C/C 个人主页#xff1a; C/C基础知识 前言C关键字(C98)命名空间命名空间的定义正常的命名空间的定义如何使用命名空间 命名空间可以嵌套同一个工程中允许存在多个相同名称的命名空间#xff0c;编译器最后会合成同一个命名空间中(一个工程中的.h文件和test.…专栏C/C 个人主页 C/C基础知识 前言C关键字(C98)命名空间命名空间的定义正常的命名空间的定义如何使用命名空间 命名空间可以嵌套同一个工程中允许存在多个相同名称的命名空间编译器最后会合成同一个命名空间中(一个工程中的.h文件和test.cpp中的同名也会合并成一个) C的输入和输出std 缺省参数缺省参数定义格式缺省参数分类 函数重载函数参数个数不同参数类型顺序不同 引用引用的特性常引用做参数做返回值引用和指针的区别 auto关键字(C11)基于范围的for循环(C11)内联函数NULL和nullptr语法糖简介 前言
C是在C语言的基础上添加了面向对象编程使程序更加模块化利于维护和扩展还增加了很多标准库库中包含了许多实用的数据结构算法容器输入输出等。
C关键字(C98)
C总计63个关键字C语言32个关键字 命名空间
在用C语言编写程序时要注意命名冲突这一个问题比如全局变量和局部变量名字相同或者函数名字冲突等。 C语言并没有给出这种问题的解决方法那么C呢 在C中给出了namespace关键字来解决这一问题。
命名空间的定义
命名空间是一种机制可以将全局作用域划分为更小的独立作用域从而利于解决命名冲突的问题。
那么命名空间是如何创建的呢
namespace namespace_name {// 变量、函数、类等定义
}正常的命名空间的定义
#include iostream
#include stdlib.husing namespace std;namespace test
{int rand 100;
}int main()
{return 0;
}这样就不会在出现由于命名冲突造成的警告了。 但是怎么去访问test这块空间呢
using namespace这两个单词是什么意思呢—使用命名空间 如何使用命名空间
在C中使用命名空间有两种常见的方式一种是使用using关键字另一种是直接使用命名空间限定符。
第一种方式是使用using关键字将命名空间引入当前作用域中这样就可以在当前作用域中直接使用命名空间中的函数、变量等定义。
也可以这样理解就是将std中的东西暴漏在全局变量中供使用。
#include iostream
using namespace std; // 引入 std 命名空间int main() {cout Hello, world! endl; // 可以直接使用 cout而不必使用 std::coutreturn 0;
}需要注意的是使用using关键字引入命名空间可能会带来命名冲突的问题因此最好只在函数内部或局部范围内使用而不要在全局范围内使用。
第二种方式是使用命名空间限定符来访问命名空间中的元素。在访问命名空间中的元素时需要使用名称前面加上命名空间限定符来表示要访问的元素属于哪个命名空间。例如
#include iostream
#include stdlib.husing namespace std;namespace test
{int rand 100;
}int main()
{cout test::rand endl;return 0;
}使用命名空间限定符的方式可以避免命名冲突问题但是在代码中会显得比较繁琐。因此建议在代码中使用合适的方式来使用命名空间。 命名空间可以嵌套
在C中可以使用命名空间嵌套的方式来实现更加复杂的命名空间划分。
#include iostream
#include stdlib.husing namespace std;namespace test_1
{int rand_1 100;namespace test_2{int rand_2 200;}
}int main()
{cout namespace test_1: test_1::rand_1 endl;cout namespace test_2: test_1::test_2::rand_2 endl;return 0;
}需要注意的是命名空间的嵌套使用并不是局限于两层可以根据现实需求来设计层数。
同一个工程中允许存在多个相同名称的命名空间编译器最后会合成同一个命名空间中(一个工程中的.h文件和test.cpp中的同名也会合并成一个)
同一个工程中可以存在多个相同名称的命名空间。当编译器编译完这些命名空间中的代码后编译器会将它们合并为同一个命名空间不同于相同名称的类。这个过程发生在编译期间因此当程序运行时只有一个命名空间被使用。
//.h文件
#include iostreamusing namespace std;namespace test_1
{int rand_3 300;
}//.cpp文件#include test_2.hnamespace test_1
{int rand_1 100;namespace test_2{int rand_2 200;}
}int main()
{cout namespace test_1: test_1::rand_1 endl;cout namespace test_2: test_1::test_2::rand_2 endl;cout namespace test_3: test_1::rand_3 endl;return 0;
}C的输入和输出
C的输入和输出与C语言不太一样C的输入和输出可以自动识别数据类型doublecharint等类型的数据都可以直接进行输入和输出。
输入操作为std::cin
输出操作为std::cout 在这里要先介绍一下C的std
std
在C中命名空间std是标准库的命名空间C标准库包含一组头文件类型定义变量和函数主要主要用于提供常用的基本功能如文件输入输出、字符串处理、数学计算、容器、算法等等。这些功能和对象被封装在std命名空间中以防止名称冲突和命名混乱。
具体而言命名空间std中包含了大量的常用C库的定义和声明它的完整的名称是std::。例如std::cout、std::cin、std::endl等等这些都是C标准库中定义的常用对象和函数。
需要注意的是在使用C标准库中的功能之前需要包含对应的头文件。例如为了使用标准输出流cout需要在程序中包含头文件iostream。
缺省参数
在C中缺省参数是指函数或方法在定义时可以给某些参数指定默认值当调用该函数或者方法的时候如果没有为这些参数提供实参时系统会默认使用默认值。
缺省参数定义格式
return_type function_name(type1 param1 default_value1, type2 param2 default_value2, ...);缺省参数必须位于参数列表的末尾且每个参数只能有一个缺省值。
下面是一个使用缺省参数的函数定义
#include iostreamvoid add(int a -1)
{std::cout add-a: a std::endl;return;
}int main()
{add();add(10);return 0;
}这个代码中函数add中的参数a有一个默认值-1所以在调用add这个函数的时候如果没有给实参则a的值默认为-1如果给了实参则a的值就是实参的值。
缺省参数分类
全缺省参数
#include iostreamvoid add(int a -1,int b 1,int c 2)
{std::cout a b c std::endl;return;
}int main()
{add();return 0;
}半缺省参数
#include iostreamvoid add(int a -1,int b 1,int c 2)
{std::cout a b c std::endl;return;
}int main()
{add(100);return 0;
}缺省参数不能在函数声明和定义中同时出现。 函数重载
函数重载指的是在同一作用域中定义多个同名函数的行为这些同名函数具有不同的参数列表他们的参数类型参数个数或者参数类型顺序不同。在调用这些同名函数时编译器会根据实参的类型和个数匹配最合适的函数。
//函数参数类型不同
#include iostreamfloat max(float a, float b)
{return a b ? a : b;
}int max(int a, int b)
{return a b ? a : b;
}int main()
{std::cout float max max(1.2, 1.3) std::endl;std::cout int max max(1, 2) std::endl;return 0;
}这里定义了两个同名函数max一个接受两个int类型的参数另一个接受两个float类型的参数。这两个函数实现的功能是计算传入的两个数的最大值。在调用这两个函数时编译器会根据传入的参数类型的不同来匹配最合适的函数。 需要注意的是函数的返回值类型并不影响函数重载也就是说返回值类型相同的函数也可以重载。但是如果只有函数的返回值类型不同则会发生编译错误。 函数重载的底层原理是利用了名字修饰特性名字修饰指的是将函数名和参数列表的信息编码为一定格式的字符串的过程。
当函数被调用时编译器会根据函数名和参数列表的类型、数量、顺序等信息生成一个唯一的名字也就是名字修饰后的字符串。这个名字被用来表示函数在符号表中的位置以便于链接器在连接时正确地找到函数的地址。 在Linux下采用g编译完成后函数名字的修饰发生改变float max(float a, float b)的名字就变成了 _Z3maxff
函数参数个数不同
void func()
{std::cout func() std::endl;
}void func(int a)
{std::cout func(int a) std::endl;
}int main()
{func();func(100);return 0;
}参数类型顺序不同
#include iostreamvoid f(int a, char b)
{std::cout f int a char b std::endl;
}void f(char a, int b)
{std::cout f char a int b std::endl;
}int main()
{f(1, b);f(b, 1);return 0;
}引用
引用并不是定义一个新的变量而是对一个已经存在的变量取一个外号(别名)(比如张三的外号叫大牛那么我问大牛吃过饭没是不是等价于问张三吃过饭没)编译器不会为引用变量新开一个空间他和他引用的变量共用一块空间。
int x 10;
int ref x;
//这里的int 和变量名字都是自己定义的可以修改注引用类型必须和引用实体是同种类型
引用的特性
引用必须在定义时初始化因为引用是已存在的变量的别名如果没有初始化就没有引用的对象
#include iostreamint main()
{int a 1;int ra;return 0;
}引用一旦初始化后就不能在改变他锁引用的对象(引用一旦绑定就不能解绑了)引用使用时可以像变量一样自然的直接调用引用同样需要遵循作用域的规则。在引用的作用域内它所引用的对象必须处于有效状态一个变量可以有多个引用
常引用
void TestConstRef()
{
const int a 10;
//int ra a; // 该语句编译时会出错a为常量a只有读的权限不能提升为不加const的引用之后权限提升---读和写
const int ra a;
// int b 10; // 该语句编译时会出错b为常量
const int b 10;
double d 12.34;
//int rd d; // 该语句编译时会出错类型不同
const int rd d;//临时空间具有常性
}做参数
#include iostreamvoid func(int a)
{a--;
}int main()
{int a 10;func(a);std::cout a std::endl;return 0;
}当我们想要在函数内部修改某个变量的值并使这个变化保持在函数外部可以考虑使用引用作为参数。使用引用作为函数参数的好处在于可以避免函数传递过程中对变量的拷贝提高程序的效率同时也可以方便地在函数内部修改变量的值。
做返回值
在用引用做返回值使用引用作为函数的返回值使得函数的返回值是某个已经存在的变量而不是函数内部新创建的一个变量。
#include iostreamint func()
{static int n 0;n;return n;
}int main()
{std::cout func() std::endl;return 0;
}n被static修饰了所以这个代码没什么问题。 #include iostream
#include cstdlib
int func()
{int n 0;n;return n;
}int main()
{int ret func();std::cout ret std::endl;rand();std::cout ret std::endl;return 0;
}这个代码输出随机值的原因是因为在函数func()中我们返回的是一个局部变量n的引用。当函数返回时局部变量n会被销毁它的内存空间被系统回收所以返回的引用实际上是指向了一个不存在的内存空间。
因此当我们在主函数中使用返回的引用变量ret时由于其指向了一个已经被销毁的内存空间所以输出的结果是不确定的可能是随机值也可能是0也可能是程序崩溃。
解决这个问题的方法是我们需要将相关变量的生命周期扩展到函数外部。例如在这个例子中我们可以将变量n定义为静态变量使其生命周期和程序的生命周期相同或者动态分配一个内存空间并将其地址作为引用返回。
引用和指针的区别
C中引用和指针都可以用来间接访问变量在某些情况下它们可以相互替换使用但是它们仍然有着不同的实现和应用。
内存占用指针需要额外使用内存来存放指针变量的地址而引用本质上并不需要占用额外的内存空间。使用限制指针可以被初始化为空指针或指向任意地址而引用必须在声明时被初始化并且不能更改其指向的变量。空值引用永远不会为空而指针可以为空。作为函数参数引用作为函数参数时可以避免对象的复制提高效率。而指针作为函数参数时可以方便地修改指针指向的变量。在sizeof中的含义不同指针是地址占用空间为32/64 — 4/8引用自加即引用的实体增加1指针自加即指针向后偏移一个类型的大小
auto关键字(C11)
C11引入了auto关键字使得程序员可以使用类型推导的方式定义变量让编译器自动推断变量的类型。它的语法形式为
auto variable value;其中variable为变量名value为变量的初始值编译器会根据value的类型推导出variable的类型。
auto关键字的使用可以使代码更加简洁、易读、易维护同时也方便定义一些复杂类型的变量例如迭代器、函数返回值等。以下是auto关键字的一些应用场景
推导迭代器类型
mapstring, int m;
for(auto it m.begin(); it ! m.end(); it) {...
}推导函数返回值类型
auto sum(int a, int b) {return a b;
}
...
auto result sum(1, 2);推导类成员变量类型
class Example {auto num 10;
};需要注意的是在使用auto关键字时需要确保变量的类型能够被准确地推导出来否则会导致编译错误。
基于范围的for循环(C11)
C11引入了基于范围的for循环语句它提供了一种便捷的方式遍历容器如数组、向量和映射等及其它支持begin()和end()函数的对象。
基于范围的for循环的语法形式如下
for (declaration : sequence) {statement
}其中declaration是用于定义循环的迭代变量的声明推荐使用auto关键字来进行类型推导sequence是需要遍历的容器或数据结构statement是每一次循环要执行的操作。
下面是一个使用基于范围的for循环对数组进行遍历的例子
int arr[] {1, 2, 3, 4, 5};
for (auto x : arr) {cout x ;
}输出结果为1 2 3 4 5
下面是使用基于范围的for循环对向量进行遍历的例子
vectorint vec {1, 2, 3, 4, 5};
for (auto x : vec) {cout x ;
}输出结果为1 2 3 4 5
基于范围的for循环相比于传统的for循环更加简洁、易读并且能够避免因数组越界而导致的程序崩溃等问题。此外它也支持使用const关键字限制迭代变量使得其值在循环体内不能被修改。 除此之外也可以修改值
int arr[] {1, 2, 3, 4, 5};
for (auto x : arr) {x 1;
}
for (auto x : arr) {cout x ;
}输出结果为2 3 4 5 6
内联函数
内联函数指的是使用关键字inline定义的函数表示该函数是一个内联函数。内联函数与普通函数的区别在于内联函数的调用不是通过函数栈帧的方式而是直接在调用位置进行代码展开从而减少了函数调用的开销提高了程序的执行效率。
具体来说当我们使用内联函数时编译器会将函数体的代码直接展开到调用的位置从而避免了函数调用时压栈和出栈的操作减少了开销。但是内联函数也有一些限制如函数体不能过于复杂否则可能会导致代码大小明显增加进而扰动了缓存、提高了指令访问路径instruction access path的长度降低了CPU Pipeline的效率。
一个内联函数的定义通常是放在头文件中它的定义一般是在函数体前加上inline关键字。例如下面的图片
注内联函数只是像编译器发出一个请求编译器可以选择忽略这个请求 NULL和nullptr
NULL和nullptr都表示空指针。它们的区别在于NULL实际上是一个宏定义通常被定义为0而nullptr是C11中引入的关键字它是一个真正意义上的空指针可以避免一些因为0被隐式转换为指针而导致的错误。
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endifC语言中我们通常使用NULL来表示空指针例如
int* ptr NULL;而在C11中我们可以使用nullptr来表示空指针例如
int* ptr nullptr;关于这两者的使用在一些情况下它们可以互换使用。但是nullptr减少了一些因为0被隐式转换为指针而导致的错误例如在函数重载时
void func(int);
void func(char*);func(NULL); // 该调用将会调用 func(int);
func(nullptr); // 该调用将会调用 func(char*);上述代码中由于NULL是一个宏定义实际上被展开为0因此编译器无法通过调用参数来区分是调用func(int)还是func(char*)。而使用nullptr则可以避免这种情况因为它是一个真正的指针类型不会被隐式转换为其他类型。
注意
在使用nullptr表示指针空值时不需要包含头文件因为nullptr是C11作为新关键字引入在C11中sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。为了提高代码的健壮性在后续表示指针空值时建议最好使用nullptr。 语法糖简介
在程序设计中语法糖Syntactic sugar指的是一种让代码更加易读、易写的语法修饰特性它并不会引入新的功能特性但却可以减少代码的输入量、提高代码的可读性或可维护性。
常见的语法糖包括但不限于
运算符重载使用运算符进行类对象的操作使得代码更加简洁易懂如C中的operator等。容器类的简化通过STL等标准库提供的容器类简化复杂的数据结构操作如C中的vector、list、map等。面向对象的语言特性如继承、多态、虚函数等可以使得代码更具有可扩展性和易维护性。内置函数和函数库如C提供的sort等算法库可以使得编写代码的效率更高同时代码也更加简洁。