C/C++易忘点复习(一)

本文介绍了C++的基础语法,包括三目运算符、switch和goto语句;空指针和野指针;内存分区模型,如堆、栈等及new运算符;引用的概念、应用和本质;函数的默认参数、占位参数、重载和重写等内容,为C++编程学习提供基础。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

运算符

三目运算符
<表达式1> ? <表达式2> : <表达式3>; 

也称三元运算符,"?"运算符的含义是:先求表达式1的值,如果为真,则执行表达式2,并返回表达式2的结果;如果表达式1的值为假,则执行表达式3,并返回表达式3的结果。

程序流程结构

选择结构——switch
switch (i) {
	case 1: System.out.println("1");break;
	case 2: System.out.println("2");break;
	case 3: System.out.println("3");break;
	default: System.out.println("default");// last default
}

(1)switch语句关键地方是进入点,有了进入点没有break的情况下会执行到底
(2)没有匹配值的时候default就是进入点,进入default以后会和普通进入点一样,如果没有break继续执行下面语句
(3)如果有break 则是从进入点到 break中间的语句执行

跳转语句——goto

(1)goto语句也称为无条件转移语句,其一般格式如下: goto 语句标号; 其中语句标号是按标识符规定书写的符号, 放在某一语句行的前面,标号后加冒号(:)。语句标号起标识语句的作用,与goto 语句配合使用。

label: i++;
loop: while(x<7);

(2)C语言不限制程序中使用标号的次数,但各标号不得重名。goto语句的语义是改变程序流向, 转去执行语句标号所标识的语句。
(3)goto语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。
(4)但是,在结构化程序设计中一般不主张使用goto语句, 以免造成程序流程的混乱,错过某些变量或函数声明,使理解和调试程序都产生困难。

指针

空指针和野指针

空指针:
(1)没有存储任何内存地址的指针就称为空指针(NULL指针)。
(2)空指针就是被赋值为0的指针,在没有被具体初始化之前,其值为0,是指针的值,不是*p的值。
(3)定义指针时,令其为NULL即是空指针;或者使用 void *。
(4)不支持解引用,不能获取指向对象的值;不能进行指针运算,比如移位操作。
野指针:
指向一个非法的或已销毁的内存的指针,使用它会对系统造成不可预知的错误。

内存分区模型

C分为四个区:堆,栈,静态全局变量区,常量区
C++内存分为5个区域(堆栈全常代 )
之所以分成这么多个区域,主要基于以下考虑:

(1)一个进程在运行过程中,代码是根据流程依次执行的,只需要访问一次,当然跳转和递归有可能使代码执行多次,而数据一般都需要访问多次,因此单独开辟空间以方便访问和节约空间。
(2)临时数据及需要再次使用的代码在运行时放入栈区中,生命周期短。
(3)全局数据和静态数据有可能在整个程序执行过程中都需要访问,因此单独存储管理。
(4)堆区由用户自由分配,以便管理。

堆 heap

由new分配的内存块,其释放编译器不去管,由我们程序自己控制(一个new对应一个delete)。如果程序员没有释放掉,在程序结束时OS会自动回收。涉及的问题:“缓冲区溢出”、“内存泄露”;由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命,由于malloc属于C库函数,其存储区不同于c++的堆区,是自由存储区。

栈 stack

是那些编译器在需要时分配,在不需要时自动清除的存储区。存放局部变量、函数参数。
存放在栈中的数据只在当前函数及下一层函数中有效,一旦函数返回了,这些数据也就自动释放了。

全局/静态存储区 (.bss段和.data段)

全局和静态变量被分配到同一块内存中。在C语言中,未初始化的放在.bss段中,初始化的放在.data段中;在C++里则不区分了。

常量存储区 (.rodata段)

存放常量,不允许修改(通过非正当手段也可以修改)

代码区 (.text段)

存放代码(如函数),不允许修改(类似常量存储区),但可以执行(不同于常量存储区)

new运算符

new运算符使用的一般格式为:new 类型(初值) /new 类型[大小] 
用new分配数组空间时不能指定初值。
如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。

new int; 
//开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针)    
new int(100); 
//开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址 
new char[10]; 
//开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址    
new int[5][4]; 
//开辟一个存放二维整型数组(大小为5*4)的空间,返回首元素的地址    
float *p=new float (3.14159); 
//开辟一个存放单精度数的空间,并指定该实数的初值为//3.14159,将返回的该空间的地址赋给指针变量p  

delete运算符使用的一般格式为:delete [ ] 指针变量 /delete 指针变量
//在指针变量前面加一对方括号,表示是对数组空间的操作
例如:要撤销上面用new开辟的存放单精度数的空间(上面第5个例子),应该用delete p;   
前面用“new char[10];”开辟的字符数组空间,如果把new返回的指针赋给了指针变量pt,则应该用以下形式的delete运算符撤销该空间:   
delete [] pt;//在指针变量前面加一对方括号,表示是对数组空间的操作

引用

什么是引用

引用,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作完全等价。

语法:类型 &引用名=目标变量名;

1.&不是求地址运算符,而是起标志作用。
2.引用的类型必须和其所绑定的变量的类型相同。

double a=10.3;
int &b=a; //错误,引用的类型必须和其所绑定的变量的类型相同

3.声明引用的同时必须对其初始化,否则系统会报错。

int main(){
    int &a; //错误!声明引用的同时必须对其初始化
    return 0;
 }

4.引用相当于变量或对象的别名,因此不能再将已有的引用名作为其他变量或对象的名字或别名。
5.引用不是定义一个新的变量或对象,因此内存不会为引用开辟新的空间存储这个引用。

//两次输出地址相同
int value=10;
int &new_value=value;
cout<<"value在内存中的地址为:"<<&value<<endl;
cout<<"new_value在内存中的地址为:"<<&new_value<<endl;

6.对数组的引用

语法:类型 (&引用名)[数组中元素数量]=数组名;
int a[3]={1,2,3};
int (&b)[3]=a;//对数组的引用

7.对指针的引用(用起来有点麻烦)

语法:类型 *&引用名=指针名;//可以理解为:(类型*) &引用名=指针名,即将指针的类型当成类型*
int a=10;
int *ptr=&a;
int *&new_ptr=ptr;
引用的应用
A.引用作为函数的参数
 int value1=10,value2=20;
 swap(value1,value2);
 void swap(int &a,int &b){//引用作为函数的参数,直接改变了原来变量的值
      int temp=a;
      a=b;
      b=temp; 
 }

1.当用引用作为函数的参数时,其效果和用指针作为函数参数的效果相当。当调用函数时,函数中的形参就会被当成实参变量或对象的一个别名来使用,也就是说此时函数中对形参的各种操作实际上是对实参本身进行操作,而非简单的将实参变量或对象的值拷贝给形参

2.通常函数调用时,系统采用值传递的方式将实参变量的值传递给函数的形参变量。此时,系统会在内存中开辟空间用来存储形参变量,并将实参变量的值拷贝给形参变量,也就是说形参变量只是实参变量的副本而已;并且如果函数传递的是类的对象,系统还会调用类中的拷贝构造函数来构造形参对象。而使用引用作为函数的形参时,由于此时形参只是要传递给函数的实参变量或对象的别名而非副本,故系统不会耗费时间来在内存中开辟空间来存储形参。因此如果参数传递的数据较大时,建议使用引用作为函数的形参,这样会提高函数的时间效率,并节省内存空间。

3.使用指针作为函数的形参虽然达到的效果和使用引用一样,但当调用函数时仍需要为形参指针变量在内存中分配空间,而引用则不需要这样,故在C++中推荐使用引用而非指针作为函数的参数。

4.如果在编程过程中既希望通过让引用作为函数的参数来提高函数的编程效率,又希望保护传递的参数使其在函数中不被改变,则此时应当使用对常量的引用作为函数的参数。

5.数组的引用作为函数的参数:C++的数组类型是带有长度信息的,引用传递时如果指明的是数组则必须指定数组的长度。

B.常量引用
语法:const 类型 &引用名=目标变量名;

常引用不允许通过该引用对其所绑定的变量或对象进行修改

int a=10;
const int &new_a=a;
new_a=11;//错误!不允许通过常引用对其所绑定的变量或对象进行修改 
C.引用作为函数的返回值(函数可以变为左值修改应用形参)

1.引用作为函数的返回值时,必须在定义函数时在函数名前加&
2.用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本

引用的本质

引用本质在c++中是一个指针常量,一旦初始化后不可以再修改。
在这里插入图片描述

函数高级

函数的默认参数

① 有函数声明(原型)时,默认参数可以放在函数声明或者定义中,但只能放在二者之一。

double sqrt(double f = 1.0); //函数声明
double sqrt(double f)  //函数定义
{
  // ....  
} 

② 没有函数(原型)时,默认参数在函数定义时指定。

//没有 函数声明
double sqrt(double f = 1.0)  //函数定义

③ 在具有多个参数的函数中指定默认值时,默认参数都必须出现在不默认参数的右边,一旦某个参数开始指定默认值,它右边的所有参数都必须指定默认值。

int f (int i1, int i2 = 2, int i3 = 3);     // 正确
int g (int i1, int i2 = 2, int i3);         // 错误, i3未指定默认值
int h (int i1 = 1, int i2, int i3 = 3);     // 错误, i2未指定默认值

④ 在调用具有默认参数的函数时, 若某个实参默认,其右边的所有实参都应该默认。

//例如, 一个函数声明如下
int f(int i1 = 1, int i2 =2, int i3 = 3);

//调用函数 f()
f();             //正确, i1=1, i2=2, i3=3
f(3);            //正确, i1=3, i2=2, i3=3
f(2, 3);         //正确, i1=2, i2=3, i3=3
f(4, 5, 6);      //正确, i1=4, i2=5, i3=6
f(, 2, 3);       //错误, i1默认,其右边的i2和i3没有默认,也就是2,3不应该出现,让其为空
函数的占位参数

1.函数占位参数函数调用时,必须写够参数

void func1(int a,int b,int)
{
    cout<<"a="<<a<<" b="<<b<<endl;
}

func1(1,2,3);

2.占位参数中使用了默认参数时,可以不用写够参数

void func1(int a,int b,int =10)
{
    cout<<"a="<<a<<" b="<<b<<endl;
}

func1(1,2);
函数重载(overload,C不可用,C++可用)

C++代码在编译时会对函数进行重命名,从这个角度讲函数重载本质上还是不同的函数,占不同的内存,入口地址一也不一样,当函数调用时,编译器会跟据传入的实参去逐个匹配,已选择对应的函数,如果匹配失败,编译器会报错(链接时),这叫重载决议。但是C不会进行重命名。
作用:函数名可以相同,提高复用性。
充要条件
(1)同一个作用域下;
(2)函数名称相同;
(3)函数参数类型不同;或者个数不同;或者顺序不同;
注意
(1)函数返回值不可以作为函数重载的条件。
(2)尽量避免函数重载和默认参数同时出现,容易出现二义性问题。
(3)引用作为重载函数形参时,要注意加const和不加const的区别。

函数重写(override)

函数重写,也被称为覆盖,是指子类重新定义父类中有相同名称和参数的虚函数,主要在继承关系中出现。
基本条件
(1)重写的函数和被重写的函数必须都为virtual函数,并分别位于基类和派生类中;
(2)重写的函数和被重写的函数,函数名和函数参数必须完全一致;
(3)重写的函数和被重写的函数,返回值相同,或者返回指针或引用,并且派生类虚函数返回的指针或引用的类型是基类中被替换的虚函数返回的指针或引用的类型的字类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值