Cpp基础入门语法

c++入门
1.基础的C++程序编写
1.1:第一个程序
#include<iostream>

using namespace std;

int main(){
    cout<<"hello world!"<<endl;
    return 0;
}
1.2:C++与C语言第一点区别(输入,输出和头文件)

关于C语言中 #include<stdio,h> 和c++中 #include<iostream> 的异同请查看

https://blog.csdn.net/weixin_36250487/article/details/80293539

1.cin: 表示标准输入(standard input)的istream类对象。通过 cin 往程序中写入数据

2.cout : 表示标准输出(standard output)的ostream类对象。通过cout 从设备输出或者写数据。

2: 命名空间

细心的你会发现第一个c++程序中有一句 using namespace std; 了吧。

其原因为:所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。

2.1引入namepase 的目的

解决命名冲突的问题

在C语言中不能出现两个名字相同的变量或者函数,因此C++引入命名空间的目的就是解决命名空间冲突的问题

#include<iostream>
using namepase std;
namespase A{
    int a=10;
}

namespace B{
    int a=20;
}

int main(){
    cout<<A::a<<endl;
    cout<<B::a<<endl;
    return 0;
}

该程序输出的两个a值是不同的,就像这样引入命名空间后你想要使用那个a值只需要用作用域限定符 :: 告诉编辑器即可。

2.2:命名空间的使用

1.加命名空间名称及作用域限定符

int main()
{
   printf("%d\n", A::a);
   return 0;    
}

2.使用using 将命名空间中成员引入

using A::a;
int main()
{
   printf("%d\n", A::a);
   return 0;    
}

3.使用using namespace 命名空间名称引入

using namespce N;
int main()
{
   printf("%d\n", N::a);
   printf("%d\n", b);
   Add(10, 20);
   return 0;
   }

4.其他用法

   namespace N1{
        int a = 10;
        int b = 20;
        int Add(int a, int b){
       //命名空间中可以有函数的存在
            return a + b;
        }
        namespace N2{
    //命名空间中可以出现嵌套式的命名空间 
    //N1::N2::swap(x,y);
            int Swap(int x, int y){
                return x*y;    
            }    
        }    
   }

命名空间总结见:

https://blog.csdn.net/jack_wang128801/article/details/90272870


3.缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个默认值在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参

就像是备用钥匙,只有我没有带钥匙的时候才用到缺省参数(对,他就是个备胎)

在这里插入图片描述

void TestFunc(int a = 0)
{
cout<<a<<endl;
}
int main()
{
TestFunc();     // 没有传参时,使用参数的默认值
TestFunc(10);   // 传参时,使用指定的实参
}
3.1全缺省参数
void Date(int year=2019,int month=6,int day=3){
    cout<<"year= "<<year<<endl;
    cout<<"month= "<<month<<endl;
    cout<<"day= "<<day<<endl;
}
3.2半缺省参数
void Date(int year,int month=3,int day=6){}
//【注意】半缺省参数必须从右往左来给出,不可以间隔给出
// 缺省参数不能在函数声明和定义中同时出现
//缺省值必须是常量或者全局变量

【总结】:所谓的缺省参数是指形参是否被赋予初始值

4.函数重载
4.1 概念:c++允许在同一作用域中声明几个功能相似的几个同名函数,这些函数的形参列表(参数的个数或类型或顺序)必须不同。

常用来处理实现功能类似数据类型不同的问题

//函数重载实例
#include<iostream>
#include<windows.h>
using namespace std;
int Add(int a,int b){
    return a + b;
}

double Add(double a, double b){
    //相较第一个而言形参的类型不同
    return a + b;
}

double Add(double a, int b, float c){
    //相较第一个而言形参的类型不同并且有参数个数的不同
    return a + b + c;
}

int main(){
    int a = 10;
    int b = 20;
    double c = 2.0;
    double d = 3.0;
    float f = 3;
    cout<<Add(a,b)<<endl;
    cout << Add(c, d) << endl;
    cout << Add(d, a, f) << endl;
    system("pause");
    return 0;
}
//c语言中函数名相同会提示重定义错误

[小结]:构成函数重载时参数需满足的条件

1.函数参数个数不同

2.参数类型不同

3.参数顺序不同

【注】:函数返回值类型不能作为函数重载的标记


4.2名字修饰

在c/c++中,一个程序要运行需要经历:预处理、编译、汇编、链接等步骤

  • 4.2.1 预处理:去注释,宏替换,头文件展开
  • 4.2.3 编译:汇编代码————>机器码
  • 4.2.3 汇编:语法检查,转换成汇编代码
  • 4.2.4 链接:生成可执行文件
4.3 在Linux下的修饰

void F1(int a);

——Z2F1I

其中—Z为前缀,2代表函数名有两个字符,F1为函数名,而i(int)为参数的类型的首字母
void F1(char b);
//_Z2F1C
void F1(int a,char b);
//_Z2F1IC

【小结】:在Linux系统下函数的编译按一下几个步骤分块

1.前缀_z

2.函数名的字符个数

3.函数的名字

4.函数参数列表中参数类型的首字母(所有参数的首字母)


4.3 C语言的名字修饰规则非常简单,只是在函数名字前面加一个下划线(_函数名),
  • 而在C++文件(xx.cpp)中在函数名字前面加一个 extern "c"既是告诉编辑器该函数按照c语言规则来编译
extern "c" int Add(int a,int b)
//一个函数的形式
// 若需要全部都用C语言的风格来编辑,形式如下

extern "c"{
    //很多行C语言代码;
}
  • 函数重载总结
  • 1 C语言不支持,C++支持
  • 2 特点:函数名相同,参数不同(1.类型不同。2.顺序不同 3.参数个数不同)
  • 3 C++语言支持重载:函数名修饰规则 name mangling
  • 4 C语言中底层函数名:_ 函数名
  • 5 C++底层函数名: 前缀+函数名+参数类型首字母

5:引用
5.1 引用概念:

引用不是重新定义了一个变量,而是给已经存在的变量取个别名,编译器不会为引用变量开辟内存空间。它和它引用的变量公用同一块内存空间

  • 示例代码如下:
  • 引用格式:类型名& 引用变量名(对象名) = 引用对象
#include<iostream>
#include<windows.h>
using namespace std;

void Testfun(){
    int a = 10;
    int &ra = a;
    //int b=20;
    cout << ra  << endl;
    //结果出现一个10
    cout << ra <<endl<< a<<endl;
    //结果出现两个10
}


int main(){
    Testfun();
    system("pause");
    return 0;
}
//ra=b 不是引用,表达式含义是将变量 b 的值放到 a
//&ra = b 不是引用,单独的&ra表示ra取地址
  • 监视结果:&ra与&a的值相同,ra与a值都为10;

【注意】:定义的引用类型必须与被引用对象的类型相同

如上例中如果出现 double &ra=a; 则会报错


5.2 引用特性
  • 1.引用在定义时必须初始化

  • 错误示例: int &ra;

  • 2.一个变量可以有多个引用

 //正确用法示例:
        int a=10;
        int &ra=a;
        int &rra=a;
        int &rrra=a;
  • 3.一个引用变量只能指向一个指定的引用对象.(例如;黑旋风只能是李逵,不能是其他人)
5.3 常引用
 void TestContRef(){
        const int a=10;//常引用
        //int & ra=1;   //该语句编译时出错,a为常量
        const int &ra=a; //常引用
        //int& b=10;    //该语句编译时出错,b为常量
        const int& b=10; //常引用
        double d=12.34;
        const int& rd=d;//常引用(rd指向的是隐式类型转换时生成的临时变量)
        int c=d; //隐式类型转换--->临时变量具有常性
        //int& rd=d;     //该语句编译时出错,类型不同    
    } 
5.4 使用场景
  • 1. 做参数
  • 传指针和传引用效果一样;

示例代码:

#include<iostream>
#include<stdlib.h>

using  namespace std;

void swap(int *pa, int *pb){
	int temp=*pa; //pa的地址与主函数中x地址相同
	*pa = *pb;   //pb的地址与主函数中x地址相同
	*pb = temp;
	//交换的是两个参数原来地址空间中的值,
	//既原函数中定义的变量x,y的地址空间中的值被交换了
}

//void swap(int &a, int &b){
//	int temp = a;
//	a = b;
//	b = temp;
//   //交换的是两个参数原来地址空间中的值,
//   //既原函数中定义的变量x,y的地址空间中的值被交换了
//}


void  swap(int a, int b){
	int temp = a;
	a = b;
	b = temp;
	//交换的是临时拷贝创建的空间里的值
	//其原地址空间内的值并没有被交换 
}
int main(){
	int x = 10;
	int y = 20;
	swap(&x, &y);
	swap(x, y);
	system("pause");
	return 0;
}

【小结】:传指针与传引用的效果是一样的(都是与原来的变量的地址空间打交道),而传值时会发生临时拷贝(代价太大),所以相比较而言传指针和传引用的效率比较高

  • 2.做返回值
   #include<iostream>
   #include<windows.h>

  	using namespace std;
        int& Testfuc(int &a){
            a+=10;
            return 0;
        }
          int main(){
          int a = 10;
          int &ra=Testfuc(a);
          system("pause");
          return 0;
     }
//此程序中的&ra和&a都指向变量a的地址

【注意】:如果函数返回时,离开函数作用域后,其栈上空间已经还给了系统,因此不能用函数栈帧上的空间做为引用类型的返回值,如果函数类型返回,返回值的生命周期必须不受函数的限制(既函数周期比较长)


#include<iostream>
#include<windows.h>
#include<time.h>

using namespace std;

int main(){
    size_t beginl = clock();
    for (int i = 0; i < 10000; i++){
      cout << "i=" << i << endl;
    }
    size_t endl = clock();
    cout << (endl- beginl) /CLOCKS_PER_SEC<< endl;
        system("pause");
    return 0;
}

//以毫秒的形式输出循环的执行时间
//引用传值的效率高于值拷贝的形式
5.6 指针与引用的区别
  • 引用语法层面:和指针指向同一块内存空间,引用本身没有开辟新的空间
  • 引用底层实现:引用开辟新的空间,和指针的的实现相同
int a=10;                               int a=10;

mov  dword ptr [a], OAh                  mov      dword ptr [a], 0Ah

 

int& ra = a;                             int* pa = &a;

lea       eax, [a]                       lea  еах,[a]

 mov      dword ptr [ra], eax            mov  dword ptr [pa], eax

 //指针的解引用和引用的解引用底层实现是一样的

ra = 20;                                 *pa = 20;

mov       eax, dword ptr [ra]             mov    eax, dword ptr[ра]

mov       dword ptr [eax], 14h            mov    dword ptr [eax], 14h 

引用和指针的不同点:

1. 引用在定义时必须初始化,指针没有要求

2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体

3. 没有NULL引用,但有NULL指针

4在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数

5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

.6. 有多级指针,但是没有多级引用

7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理

8.引用比指针使用起来相对更安全 (引用使用时必须初始化)


6.内联函数
6.1 概念:

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。

inline Add(int a,int b){
    return a+b;
}
int main(){
    int ret=Add(1,3) ------->//若是内敛函数的话,函数体直接展开成int ret=1+3;
        //如果不是内联函数的话,底层实现会调用add的函数栈帧call add(0X001)
    return 0;
}
6.2:对于普通函数和内联函数的比较
#include<iostream>
using namespace std;
int add(int a,int b){
    return a+b;
}

inline int add(int a,int b){
    return a+b;
}

int main(){
    int ret=add(1,3)
    return 0;
}

普通函数

int add(int a,int b){
return a+b;
}

底层实现:普通函数,编译时调用add的函数栈帧

push 3

push 1

call add(0821042h)

内联函数

inline int add(int a,int b){
return a+b;
}

底层实现:内联函数,编译时函数体展开

mov eax ,2

00012220 add

00012223 mov dword ptr[ret] ,eax

6.3 inline函数的特性
  1. inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用内联函数。
  2. inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等,编译器优化时会忽略掉内联。
  3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到

面试题:
  • 【面试题】宏的优缺点?
优点:
  • 1.增强代码的复用性。
  • 2.提高性能。
缺点:
  • 1.不方便调试宏。(因为预编译阶段进行了替换)
  • 2.导致代码可读性差,可维护性差,容易误用。
  • 3.没有类型安全的检查 。
C++有哪些技术替代宏
  • 1. 常量定义 换用const
  • 2. 函数定义 换用内联函数

7.关键字auto
  • 特点:使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化
  • 表达式来推导auto的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
#include<iostream>
#include<windows.h>
 
using namespace std;
int main(){
    int a = 10;
    auto b = a;
    double c = 20.3;
    auto d = c;
    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    cout << typeid(d).name() << endl;
    system("pause");
    return 0;
}
//显示出了类型的名称
7.1. auto与指针和引用结合起来使用用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
int main()
{
    int x = 10;
    auto a = &x;  //定义了一个引用
    auto* b = &x;  //(int *)
    auto& c = x;    //推导出引用变量的类型
    cout << typeid(a).name() << endl;
    cout << typeid(b).name() << endl;
    cout << typeid(c).name() << endl;
    *a = 20;
    *b = 30;
     c = 40;
    return 0;
}
7.2 在同一行定义多个变量
  • 当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,
  • 为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。
void TestAuto()
{
    auto a = 1, b = 2; 
    auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}
  • 若同一个程序中同时定义了多个 auto ,其会根据第一个变量表达式去推导类型。
7.3 auto不能推导的场景。

7.3.1 auto不能做函数的参数

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}

7.3.2 auto不能直接用来声明数组
void TestAuto()
{
    int a[] = {1,2,3};
    auto b[] = {456};
}

7.3.3为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

7.3.4 auto在实际中最常见的优势用法就是跟以后会讲到的C++11提供的新式for循环,还有lambda表达式等进行配合使用。

7.3.5 auto不能定义类的非静态成员变量

7.3.6实例化模板时不能使用auto作为模板参数


8 基于范围的for循环(C++11)
8.1 for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。
#include<iostream>
#include<windows.h>

using namespace std;

int main(){
    int arr[] = {1,2,3,4,5,6,7,8,9};
    int size = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    for (; i < size; i++){
        cout << "arr1[i]=" << arr[i] << endl;
    }
    cout << endl;
    for (int e : arr){
        cout << "arr2[i]=" << e << endl;
    }
    cout << endl;
    for (auto& e : arr){
        cout << "arr3[i]=" <<e << endl;
    }
    system("pause");
    return 0;
}

【注意】:与普通循环类似,基于范围的for循环可以用continue来结束本次循环,也可以用break来跳出整个循环

8.2 范围for的使用条件
1. for循环迭代的范围必须是确定的
  • 对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提
  • 供begin和end的方法,begin和end就是for循环迭代的范围。
  • 注意:以下代码就有问题,因为for的范围不确定
void TestFor(int array[]) 
{ 
 for(auto& e : array) 
 cout<< e <<endl; 
}

9.指针空值nullptr(C++11)
#include<iostream>
#include<windows.h>

using namespace std; 

int main(){
    int *p = NULL;//c++中此处的NULL被定义成常数 "0"
    int *pp = nullptr;
    cout << typeid(pp).name() << endl;
    cout << typeid(nullptr).name() << endl;
    system("pause");
    return 0;
}
结果如下: 
//int *
//std::nullptr_t

程序本意是想通过f(NULL)调用指针版本的f(int)函数,但是由于NULL被定义成 0,因此与程序的初衷相悖。在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void*)0。**

9.2 nullptr 与 nullptr_t

nullptr代表一个指针空值常量。nullptr是有类型的,其类型为nullptr_t,仅仅可以被隐式转化为指针类型,nullptr_t被定义在头文件中

typedef decltype(nullptr) nullptr_t;

注意:

1 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。

2 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。

3 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值