上海网站开发公司外包,怎么做粉丝福利购网站,个人做外贸怎样起步,建设工程有限公司 网站文章目录 前言命名空间命名空间的使用 缺省参数缺省参数的使用 函数重载函数重载的作用函数重载的使用函数重载原理 引用引用的使用引用的使用场景引用和指针 extern Cinlineauto范围fornullptr 前言
大家好我是jiantaoyab#xff0c;这篇文章给大家带来的是c语言没有的一些特… 文章目录 前言命名空间命名空间的使用 缺省参数缺省参数的使用 函数重载函数重载的作用函数重载的使用函数重载原理 引用引用的使用引用的使用场景引用和指针 extern Cinlineauto范围fornullptr 前言
大家好我是jiantaoyab这篇文章给大家带来的是c语言没有的一些特性之一是c的基础语法对后面的学习有帮助后面我将逐步编写类和对象、STL容器等C笔记欢迎大家关注我
命名空间 当程序达到一个规模后会不可避免的使用到相同的函数名和标识符导致冲突可以使用namespace封装到一个命名空间中一个命名空间就定义了一个新的作用域命名空间中的所有内容都局限于该命名空间中 同一个工程中能存在相同的命名空间相同名字的命名空间会合并到一起 作用域:变量的有效范围从定义的起点开始到定义变量之前最近的一对括号确定
命名空间的使用
//1.使用using使用命名空间成员之一
using jiantao::n;
//2.using namespace jiantao
//全部展开失去封装的效果不建议这样使用
namespace jiantao
{int n 10;//可以嵌套使用namespace id{int b 2;}
}int main()
{cout n endl; //using jiantao::n; 直接用cout jiantao::n endl; //一般的使用方法cout jiantao::id::b endl; return 0;
}缺省参数 缺省参数是声明或定义函数时为函数参数指定的一个默认值在调用该函数时如果没用指定实参则采用该默认值 缺省参数的使用
注意点
半缺省参数必须从右往左给不能间隔给缺省参数不能在定义和声明中同时出现建议在声明中写缺省值必须是全局变量或者是常量
void fun()
{cout fun() endl;
}//默认这里的参数会给值
void fun(int a ,int b10,int c20)
{cout a endl;cout b endl;cout c endl;}
int main()
{fun(1); fun(1,2);//传2的话b的值就是2
}函数重载 函数重载是指在同一作用域内声明几个功能类似的同名函数这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同这组函数被称为重载函数 函数重载的作用
重载函数通常用来命名一组功能相似的函数这样做减少了函数名的数量避免了名字空间的污染对于程序的可读性有很大的好处
函数重载的使用
int add(int a, int b)
{return a b;
}
double add(double a, double b)
{return a b;
}
int main()
{add(1, 2);add(1.1, 1.2); //2个add构成重载return 0;
}函数重载原理
Linux下我定义了2个函数名字相同的函数在gcc编译的时候报错 什么原因
回忆一下一个程序编译链接的过程在汇编的时候会生成.o文件.o文件是没有函数的地址的声明和定义分开写那么在链接的时候会根据函数的名字去找地址而在c语言中不存在函数名字的修饰根据函数的名字去符号表中找假设在符号表中有Add(0x33123)和Add(0x332323)这在符号表中都冲突了人家链接也不知道链接哪个 而在c中存在函数名修饰规则不同平台下不一样在linux下大概为作用域返回类型函数名参数列表就能找到的函数的地址能链接上了 重载大概的流程图 我的知识有限大家可以看看这篇 c函数重载 里面有更详细的介绍
引用
引用不是新定义一个变量而是给已经存在的变量起了一个别名引用的变量和原来的变量共用一块内存空间
引用的使用
1.引用的类型和引用的实体必须的同类型的int a10; int ba;2.引用在定义的时候必须初始化int c10 //error 引用的必须是一个对象const int a10; int ba;//error a为常量
3.一旦一个引用被初始化指向一个对象它就不能变为另一个对象的引用但一个对象可以有多个引用int a 0;int b a;int b c; //ERRORint c a; //RIGHT
4.引用权限只能缩小不能放大int a10;const int ba;//✔ 权限缩小const int d 10;int f d; //ERROR这是权限的放大
5 临时变量本来是右值是常量不能修改的double b 1.1;int i d; //ERROR存在类型转换类型转换有临时变量const int i2 d //可以 引用的使用场景
1.引用做参数 提高效率,形参的改变能影响实参
int swap(int x,int y)
{}
使用引用传参如果不改变参数的值建议使用const引用2.引用做返回值提高效率修改返回变量临时变量本来是右值是常量不能修改的
int Add(int a,int b)
{int cab; return c;
}
int main()
{int retAdd(1,2);coutretendl;
}引用做返回值有一个重要的点引用的这个返回对象在这个函数栈帧销毁的时候还存在着才能使用引用返回。
int Sub(int x, int y)
{int ret x - y;return ret;
}int f()
{int *p(int *)malloc(4);return *p;//是可以的p指向的空间还在return p;//error 指针p已经给销毁
}
int main
{int a Sub(2, 1);
} 虽然编译通过了但是上面的代码会存在非法访问的问题Sub的返回值原来是临时变量这里用了引用返回那相当于int ret tmp在调用完Sub后会回去访问ret的空间假如Sub栈帧给销毁了a所取到的值就是随机值这里我们并没有马上使用这块空间才没报错。 引用和指针 引用在定义的时候必须初始化指针不初始化也不报错引用一个对象之后就不能再引用别人对象指针可以随意更改对象引用的大小是引用实体的大小指针的大小是固定是4/8个字节引用不会开辟空间不占用内存引用加1是引用的实体加1指针加1是向后偏移一个类型的大小 通过下面的汇编代码看出引用和指针在使用的汇编指令是一样的 extern C
大家有没有想过如果我用c写的程序想去调用别人c写的库怎么去调用呢
一会再回答这个问题我们先来看看extern的用法。 extern是C语言中的一个关键字一般用在变量名前或函数名前作用是用来说明此变量/函数是在别处定义的要在此处引用在遇到此变量或函数的时候在其他文件中寻找其定义 extern int a;//声明变量a是在别的文件定义的只是声明没有分配内存
举个例子
比如我在test.cpp中 extern int num; extren void Print();
然后在fun.cpp 中
int num 1; //这里是定义变量
void Print(){printf(Print());} //这里是定义函数
这样子就能在test.cpp中使用这个num变量了 和 Print 这个函数了想要在C中调用c语言写的库在vs2013下需要执行下面操作 调试-属性-链接器 常规 -附加库目录把库加上Debug 调试-属性-链接器 输入- 附加依赖项 手动加入库的名字 xxx.lib extern C
{//把头文件所在的目录包含#include ../../DS/DS/Stack.h
}告诉C编译器extern “C”{}里面的函数是C编译器编译的链接的时候用C的函数名规则去找就可以链接上, 定义为extern的变量也会在外面去找
在C中调用c写的库要在cpp库中修改
//如果有__cplusplus 这个宏就用 extern C 替换 EXTRERN_C
#ifdef __cplusplus
#define EXTRERN_C extern C // C静态库extern C告诉编译器以下函数按C的函数名修饰规则去处理
#else
#define EXTRERN_C //这里走的是else 就是EXTRERN_C 什么都没有
#endifEXTRERN_C void StackInit(ST* ps); //运行后相当于 void StackInit(ST* ps)
EXTRERN_C void StackDestroy(ST* ps);
EXTRERN_C void StackPush(ST* ps, STDataType x);
EXTRERN_C void StackPop(ST* ps);
EXTRERN_C STDataType StackTop(ST* ps);
EXTRERN_C int StackSize(ST* ps);
EXTRERN_C bool StackEmpty(ST* ps);inline 以inline修饰的函数叫做内联函数编译时C编译器会在调用内联函数的地方展开没有函数压栈的开销使用内联函数在.o文件符号表不会生成函数的地址内联函数提升程序运行的效率。 一般调用函数在汇编语言中是用call来调用的 当把Add函数设置成内联函数后,再看看汇编想要显示出效果需要在vs2013Debug下设置 可以看到直接以函数的本体代替就像宏一样但是会增加目标码的大小会导致额外的换页行为降低指令高速缓存装置的击中率。还有要注意的是inline只是对编译器的一个申请不一定会采用
还有inline不建议声明的定义分离分离会导致链接错误在小型、频繁调用的函数上可以使用inline
小练习 A.使用inline关键字的函数会被编译器在调用处展开 B.头文件中可以包含inline函数的声明 C.可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数 A.不一定因为inline只是一种建议需要看此函数是否能够成为内联函数 B. inline函数不支持声明和定义分离开因为编译器一旦将一个函数作为内联函数处理就会在调用位置展开即该函数是没有地址的也不能在其他源文件中调用故一般都是直接在源文件中定义内联函数的 C.inline函数会在调用的地方展开所以符号表中不会有inline函数的符号名不存在链接冲突。 auto 自动推导类型类型声明时的“占位符”编译器在编译期会将auto替换为变量实际的类型 auto会忽略顶层的const而底层const被保留顶层const是指针本身是个 常量底层const是指针指向的对象是一个常量 int main()
{const int a 10;auto b a; // int cout typeid(b).name() endl;//不管原来对象是不是const 要想成为const的auto 要在 auto前加上const //例如 const auto b a 这样定义出来的b就是带const属性的int x 1;auto a x; //intautob x; //intauto*c x;//int *return 0;
}auto不能使用的场景
1.不能作为函数的参数
void Add(auto a) //error 编译器无法对a的实际类型进行推导
auto b //error
2.不能用来声明数组
auto arr[]{1,2,3}; //error 范围for
int main()
{int arr[] { 1, 2, 3, 4, 5, 6, 7 };//范围for这个地方必须是数组名for (auto e: arr) //一个一个取arr的值放到e中{cout e; }cout endl;//每个值加1for (autoe : arr){e;}cout endl;for (auto e : arr){cout e;}cout endl;}void printf_(int arr[]) for(auto e:arr)//error 数组传参的时候会退化为指针不再是数组名字{couteendl;}
}nullptr
c中用nullptr中比null好
void f(int)
{cout f(int) endl;
}
void f(int *)
{cout f(int *) endl;
}
int main()
{f(NULL);// NULL 0 调用了f(int),本意是调用f(int*)f(nullptr);//调用f(int *)
}