公司网站优化推广方案,中国建设教育协会网站培训中心,太平阳建设集团网站,番禺网站设计1、C第一个程序
C是脱胎于C语言的#xff0c;所以也包含了C语言绝大多数的内容#xff0c;C兼容C语言绝大多数的语法,在C语言中能实现的程序在C中也是可以执行的#xff0c;但需要将定义文件代码的后缀改为.cpp
就比如hello world程序
// test.cpp
#includestdio.h第一个程序
C是脱胎于C语言的所以也包含了C语言绝大多数的内容C兼容C语言绝大多数的语法,在C语言中能实现的程序在C中也是可以执行的但需要将定义文件代码的后缀改为.cpp
就比如hello world程序
// test.cpp
#includestdio.h
int main()
{
printf(hello world\n);
return 0;
}
但在C中它是自成体系的有自己的输入输出 c版的hello world程序为
#includeiostream
using namespace std;int main()
{couthello world\nendl;return 0;
}
初学C的小伙伴出看见这个代码会有很多疑惑但不要慌张接下来让我来为你详细解释
2、命名空间namespace
在看很多书上的C代码都很看到这样的一行代码“using namespace std;”
都会疑惑namespace是什么在这里有什么作用
不要急我们慢慢来
我们要知道在C语言中我们经常会因为变量和函数的命名冲突而感到困惑而我们的C的祖师爷可能也是深受它的毒害吧就创造了一个新概念叫做命名空间namespace
使用命名空间的目的就是对标识符的名称进⾏本地化以避免命名 冲突或名字污染namespace关键字的出现就是针对这种问题的
namespace的定义
1、
定义命名空间需要使⽤到namespace关键字后⾯跟命名空间的名字然后接⼀对{}即可{}中
即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
namespae 空间名
{}
命名空间的本质是域在C语言中我们学过了全局域和局部域而我们在C中学习的命名空间这个域独立于全局域又因为不同的域可以定义同名的变量在这里我们也可以知道命名空间域中的变量也和全局变量一样只有在程序结束后才会结束生命周期
总结就是命名空间域内的变量和全局变量类似
命名空间会影响编译查找变量的规则使得C中查找规则是先查找局部域在查找全局域一般不会在命名空间中查找
2、
namespace只能定义在全局域中而且它还可以嵌套定义就是在一个namespace中再定义一个namespace可以无限套娃但最多嵌套两三次就足够了
这里的嵌套定义就类似于一个公司里面有很多部门每个部门就相当于一个namespace而每个部门里面又有很多员工每个员工又是一个独立的namespace
嵌套定义后使用命名空间就需要由外向里的进行寻找
3、
多个文件中定义了多个同名的namespace的时候并不会发生冲突它会自动合并为一个命名空间
4、
C的标准库都放在std的命名空间中这就是为什么许多C代码都会有“using namespace std;
namespace的使用
1、指定命名空间访问项目中推荐使用
2、using将命名空间中的某个成员展开这适用于项目中经常使用而且不存在冲突的成员
3、using将命名空间中全部的成员展开这种方法在项目中不推荐使用而且当出现多个using的展开可能会出现冲突
using n::b;
int main()
{printf(%d ,n::a);//指定命名空间访问 printf(%d ,b);//using将命名空间中的某个成员展开 return 0;
} using namespace n;//展开命名空间中的全部成员int main()
{printf(%d,a);return 0;
} 3、C的输入输出
iostream 是 Input Output Stream 的缩写是标准的输⼊、输出流库定义了标准的输⼊、输出对象。
std::cin 是 istream 类的对象它主要⾯向窄字符narrow characters (of type char)的标准输入流。
std::cout 是 ostream 类的对象它主要⾯向窄字符的标准输出流。
std::endl 是⼀个函数流插⼊输出时相当于插⼊⼀个换⾏字符加刷新缓冲区。
是流插⼊运算符是流提取运算符。C语⾔还⽤这两个运算符做位运算左移/右移
使⽤C输⼊输出更⽅便不需要像printf/scanf输⼊输出时那样需要⼿动指定格式C的输⼊
输出可以⾃动识别变量类型(本质是通过函数重载实现的这个以后会讲到)其实最重要的是
C的流能更好的⽀持⾃定义类型对象的输⼊输出。
IO流涉及类和对象运算符重载、继承等很多⾯向对象的知识这些知识我们还没有讲解所以这
⾥我们只能简单认识⼀下C IO流的⽤法后⾯我们会有专⻔的⼀个章节来细节IO流库。
cout/cin/endl等都属于C标准库C标准库都放在⼀个叫std(standard)的命名空间中所以要 通过命名空间的使⽤⽅式去⽤使用它们
4、缺省参数默认参数
缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值也就是给函数的形参一个默认值。在调用该函数时如果没有给明实参就采用该形参的默认值缺省值否则就使用给明的实参
缺省参数又分为全缺省和半缺省参数
全缺省函数的全部形参都给缺省值
半缺省就是部分形参给缺省值
C规定半缺省参数必须从右往左依次连续缺省不能中间跳跃给实参而且带缺省参数的函数调用时C规定必须从左往右依次给实参不能跳跃给实参
缺省参数不能在函数声明和定义中同时出现且规定必须函数声明给缺省值
#include iostream
using namespace std;void Func(int a 0)
{cout a endl;
}
int main()
{Func(); // 没有传参时使⽤参数的默认值Func(10); // 传参时使⽤指定的实参return 0;
}#include iostream
using namespace std;
// 全缺省
void Func1(int a 10, int b 20, int c 30)
{cout a a endl;cout b b endl;cout c c endl endl;
}
// 半缺省
void Func2(int a, int b 10, int c 20)
{cout a a endl;cout b b endl;cout c c endl endl;
}
int main()
{Func1();Func1(1);Func1(1,2);Func1(1,2,3);Func2(100);Func2(100, 200);Func2(100, 200, 300);return 0;
}
5、函数重载
C⽀持在同⼀作⽤域中出现同名函数但是要求这些同名函数的形参不同可以是参数个数不同或者 类型不同。这样C函数调⽤就表现出了多态⾏为使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。
函数重载主要是根据形参来进行区别
1、形参类型不同
2、形参个数不同
3、形参顺序不同
#includeiostream
using 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 f(int a)
{cout f(int a) endl;return 0;
}void f(int a)
{cout f(int a) endl;
}int main()
{f(10);f();return 0;
}
在多个同名函数中其中有函数中含有缺省参数时我虽然在传参时构成重载但是在不传参时会发生编译错误因为编译器不知道应该调用哪个函数这里就涉及到了二义性
void f1()
{cout f() endl;
}
void f1(int a 10)
{cout f(int a) endl;
}
int main()
{f();f(10);return 0;
}6、引用
C中引入了一个新的概念叫做引用引用顾名思义就是用别人的而不是自己的所以引⽤不是新定义⼀个变量⽽是给已存在变量取了⼀个别名编译器不会为引⽤变量开辟内存空间它和它引⽤的变量共⽤同⼀块内存空间在这里是是指代语法层。
类型 引用别名 引用对象
引用的特性
1、引用在定义时必须初始化
2、一个变量可以有多个引用
3、引⽤⼀旦引⽤⼀个实体再不能引⽤其他实体
int main()
{int a 0;// 引用b和c是a的别名int b a;int c a;// 也可以给别名b取别名d相当于还是a的别名int d b;d;// 编译报错“ra”: 必须初始化引用//int ra;int* c a;int* rc c;//指针也可以取别名// 这里取地址我们看到是一样的cout a endl;cout b endl;cout c endl;cout d endl;int x 10;d x;//在这里是赋值拷贝return 0;
}
引用的使用
引用的实践主要是在引用传参和引用做返回值两个方面
引用传参
引用传参跟指针传参功能是类似的它其实就是代替了指针的传址而且也不需要像指针一样要解引用和取地址并且引用不需要开辟空间减少了拷贝极大地提高了效率
//引用传参
void Swap(int ra, int rb)
{int temp ra;ra rb;rb temp;
}
int main()
{int a 1, b 2;printf(a%d,b%d\n, a, b);Swap(a, b);printf(a%d,b%d\n, a, b);return 0;
} 引用做返回值
引用做返回值的本质是不会生成临时变量直接返回对象别名这里的别名指的是给被引用对象所处空间起的别名 在引用做函数返回值时函数结束后函数栈帧会被销毁却不会影响实参及其别名所处的空间所以用引用做返回就能起到指针传址调用一样的作用改变实参
typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;
void STInit(ST rs, int n 4)//在这里也是用到引用传参
{rs.a (STDataType*)malloc(n * sizeof(STDataType));rs.top 0;rs.capacity n;
}// 栈顶
void STPush(ST rs, STDataType x)
{//assert(ps);// 满了 扩容if (rs.top rs.capacity){printf(扩容\n);int newcapacity rs.capacity 0 ? 4 : rs.capacity * 2;STDataType* tmp (STDataType*)realloc(rs.a, newcapacity *sizeof(STDataType));if (tmp NULL){perror(realloc fail);return;}rs.a tmp;rs.capacity newcapacity;}rs.a[rs.top] x;rs.top;
}
/*引用做返回值*/
int STTop(ST rs)
{//assert(rs.top 0);return rs.a[rs.top - 1];
}int main()
{ST s;STInit(s);STPush(s, 1);STPush(s, 2);STPush(s, 3);STPush(s, 4);//传值/*int top STTop(s);top10;cout top endl;*///引用传参/*STTop(s) 10;cout STTop(s) endl;*/return 0;
} 在这里是使用传值返回在C/C中规定返回类型除引用以外返回对象是先存储在一个临时变量或者寄存器当中再由临时变量传到主函数中临时变量的建立需要开辟空间再由主函数中建立一块空间接收所以主函数中就需要在建立一个同类型变量接收临时变量的值
/*传值*/
int STTop(ST rs)
{return rs.a[rs.top - 1];
} 错误示范不能返回局部对象的引用
在该函数结束后函数栈帧销毁top也会被销毁这时的别名就会像free(指针)后没有给指针置空一样成为野别名 虽然编译器不会报错但是也无法实现功能
int STTop(ST rs)
{int top rs.a[rs.top - 1];return top;
}
const引用
可以引用一个const对象但必须const引用这里就涉及了权限的平移const-const
const int a1;
const int raa;//权限的平移
const引用也可以引用普通对象这里就涉及了权限的缩小由变量变为常量
int a10;
const int raa;
//权限的缩小
但权限不能放大
const int a1;
int raa;//错误
在对象权限的访问中权限可以平移、缩小但不能放大 这里的对象指的是指针和引用
临时变量临时对象 所谓临时对象就是编译器需要⼀个空间暂存表达式的求值结果时临时创建的⼀个未命名的对象
C中把这个未命名对象叫做临时对象
在被引用对象为常量、表达式、类型不同会涉及到类型转换时要用到临时变量而C规定临时对象具有常性所以这⾥就触发了权限放⼤必须要⽤常引⽤const才可以。
int main()
{//临时变量的常性具有const的属性//常量//int x 10;//错误的要引用一个常量就要在前面加一个constconst int x 10;cout x endl;//表达式int p 2;//错误//int rp p*2;//在这里就涉及到了临时变量//p*2的和存储在一个临时变量里面而临时变量具有const属性//所以接收值也应该具有const属性就需要在前面加一个const都是涉及权限的放大const int rp p*3;cout rp endl;//类型不同double d 1.1;//int rd d;//在这里值也是存储在一个临时变量当中所以也要用const来接收都是涉及权限的放大const int rd d;cout d endl;return 0;
}
当我们在函数传参用到引用的时候传入的值就如上面的三种情况时就需要用const引用了 在现在学习到的场景下类型转换和传值返回会用到临时对象 指针与引用的关系
C中指针和引⽤就像两个性格迥异的亲兄弟指针是哥哥引⽤是弟弟在实践中他们相辅相成功能有重叠性但是各有⾃⼰的特点互相不可替代。
1、
语法概念上引⽤是⼀个变量的取别名不开空间指针是存储⼀个变量地址要开空间。但在汇编层面上其实引用还是以指针的形式运行的 2、
引⽤在定义时必须初始化指针建议初始化但是语法上不是必须的。
3、
引⽤在初始化时引⽤⼀个对象后就不能再引⽤其他对象⽽指针可以在不断地改变指向对象。
4、
引⽤可以直接访问指向对象指针需要解引⽤才是访问指向对象。
5、
sizeof中含义不同引⽤结果为引⽤类型的⼤⼩但指针始终是地址空间所占字节个数(32位平台下占4个字节64位下是8byte)
6、
指针很容易出现空指针和野指针的问题引⽤很少出现引⽤使⽤起来相对更安全⼀些。 7、nullptr
在C语言中NULL其实是一个宏头文件为(stddef.h)
#ifndef NULL#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif
#endif
由代码可得C中NULL可能被定义为0或者被定义为无类型(void*)的常量
void f(int x)
{cout f(int x) endl;
}void f(int* ptr)
{cout f(int* ptr) endl;
}int main()
{//f(NULL); // 调用这个函数时原本要为f(int* ptr)但因为被定义为0所以结果为f(int x)错误//f((void*)0); // 该函数调用会报错f(nullptr);return 0;
}
所以在C11中引入了nullptr它是一个特殊关键字他可以转换成任意类型的指针类型使用它可以避免类型转换的问题因为它可以隐式转换为指针类型而不会转换为整数类型