c++学习之c++对c的扩展2

news2024/11/15 20:04:09

目录

1.c/c++中的const

1 const概述

2 c/c++中const的区别

c中的:

c++中的const:

c/c++中的const异同

c++中const修饰的变量,分配内存情况

尽量以const替换define

2.引用

函数的引用:

引用的本质

指针的引用

5 常量引用

内联函数

内联函数的基本概念

2 宏函数和内联函数的区别


1.c/c++中的const

1 const概述

const 修饰的对象为一个常量,不能被改变

2 c/c++中const的区别

c语言中的const1 const修饰的局部变量,存在栈区,虽然不能通过const修饰的变量去修改栈区内容,但是可以 通过地址去修改

 const修饰的全局变量是保存在常量区,不能通过变量名去修改.也不能通过地址去修改。

 const修饰的全局变量,如果其他文件想使用,直接extern声明外部可用即可。

c中的:

在main.c中

#include <stdio.h>
const int b = 10;//const修饰的全局变量保存在常量区

 void test03()
 {
 extern const int num;//声明num是外部可用的
 printf("num=%d\n",num);

 }
 //const修饰的全局变量
 void test02()
 {
 //b = 100;
 int *p = &b;
 *p = 100;//错误的 不能修改常量区的内容
 printf("b=%d\n",b);
 }
 //const修饰的局部变量
 void test01()
 {
 //在c语言中const修饰的变量保存在栈区
 const int a = 10;
 //a = 100;
 int *p = &a;
 *p = 100;
 printf("a=%d\n",a);
 }
 int main()
 {
 test03();
 return 0;
 }

onst int num = 1;

c++中的const:

1 const修饰的局部变量赋值常量时,局部变量保存在符号表中,修改不了,是一个常量

2 const修饰的全局变量保存在常量区,不能被修改

3 const修饰的全局变量默认是内部链接属性,加上extern修饰变成外部链接属性

mian.cpp

#include <iostream>
 using namespace std;
 const int b = 1;

 void test03()
 {
 extern const int num;
 cout << num << endl;

 }
 void test02()
 {
 //const修饰的全局变量存在常量区
 int *p = (int *)&b;
 *p = 100;//错误的
 cout << b<< endl;

 }
 //c++中const修饰的局部变量
 void test01()
 {
 //c++中const修饰的局部变量存在符号表中
 const int a = 10;
 //a = 100;
 //对const修饰的局部变量取地址 编译器会产生一个临时变量来保存a的地址
 // int tmp = a; int *p = &tmp
 int *p = (int *)&a;
 cout << a << endl;
}
int main()
 {

 test03();
 return 0;
 }

extern const int num = 1;// const修饰的全局变量默认是内部链接属性.

c/c++中的const异同

相同的点:

c和c++中的const修饰的全局变量都是保存在常量区,不能被修改

不同的点:

c语言中const修饰的局部变量赋值为常量时,,局部变量保存在栈区,可以被指针修

c++中,const修饰的局部变量赋值为常量时,局部变量保存符号表中,不能被修改

c语言中const修饰的全局变量默认是外部链接属性

c++语言中const修饰的全局变量默认是内部链接属性

c++中const修饰的变量,分配内存情况

const修饰的全局变量在常量区分配了内存

对const修饰的局部变量赋值为常量时,对其取地址,会在栈区分配临时的内存空间

const修饰的局部变量赋值为变量时,局部变量保存在栈区

const修饰的局部变量时一个自定义变量,也是在栈区分配内存

只有一种情况,const'修饰的局部变量被赋值为常量时,这个局部变量保存在符号表中,

符号表后中的变量,编译器直接访问,不会为其开辟空间。

#include <iostream>
 using namespace std;
 const int a = 1;//const修饰的全局变量在常量区分配了内存
 void test01()
{
 const int a = 10;//const修饰的局部变量赋值为常量没有分配内存,存在符号化表中
 int *p = (int *)&a;//对const修饰的局部变量赋值为常量的变量取地址 会分配一个临
时的空间 int tmp =a *p =&tmp
 }
void test02()
 {
 int b = 2;
 //const修饰的局部变量赋值变量时 局部变量存在栈区
 const int a = b;
 int *p = (int *)&a;
 *p = 100;
 cout << a << endl;

 }
 struct stu
 {
 int a;
 int b;
 };
 void test03()
 {
 //const修饰的变量为自定义变量时,保存在栈区
 const struct stu obj = {1,2};
 struct stu *p = (struct stu *)&obj;
 p‐>a = 3;
 p‐>b = 4;
cout << obj.a << " " << obj.b << endl;
 }
 int main()
 {
test03();
return 0;

 }

尽量以const替换define

有两点原因:
1. const修饰的全局变量或const修饰的局部变量赋值为常量,是有类型的,而define的
宏没有 类型
2. const修饰的全局变量或const修饰的局部变量赋值为常量有作用域的,而define的
宏没有作用域
#include <iostream>
 using namespace std;
 namespace A
 {
 const int max = 1024;
 const short max1 = 1024;
 #define MAX 1024
 }
 // 宏没有作用域 宏没有类型(int)
 void fun(int a)
 {

 }
 void fun(short a)
 {


 }

 void test01()
 {
 cout << A::max << endl;
 cout << MAX << endl;
 fun(MAX);//void fun(int a)
 fun(A::max);//void fun(int a)

 fun(A::max1);//void fun(short a)
 }
 int main()
30 {
31
32 return 0;
33 }

宏定义的变量是做替换的,并且是全局的没有作用限制,但是const修饰的变量为常量是有范围的,范围即为原来变量的范围。

2.引用

引用的作用为给变量取别名。

引用的基本用法
原类型 &别名 = 旧名  这里的&不是取地址符,而是引用。

 

 在这里取别名后,两者的地址是一样的,且对a操作,b也会跟着操作。

注意事项:
引用一旦初始化,不能更改引用的指向
引用定义时必须初始化
不能引用NULL
引用可以引用任意类型包括数组
&在等号的左边是引用,在等号的右边是取地址
#include <iostream>
 using namespace std;
 void test01()
 {
 int a = 10;
 //引用一旦初始化之后不能改变引用的标识
 int &b = a;
 b = 100;
 cout << a << endl;
int c = 1;
 //b = c; 代表把c的值赋值给b 不是给c取别名为b
 //int &d; 引用定义时必须初始化
 }
 void test02()
 {
 int a[5] = { 1,2,3,4,5 };
 //int(&arr)[5] = a;
 typedef int ARR[5];
 //type & 别名 = 旧名
 ARR & arr = a;
 for (int i = 0; i < 5; i++)
 {
 cout << arr[i] << " ";

 }
 cout << endl;

 }
 int main()
 {
 test02();
return 0;

 }
变量的引用并不会为他开辟空间,只想相当于多了个名字。

函数的引用:

注意:
引用可以作为函数的形参
函数调用时 就是  &实参=形参  实参的改变影响形参,我们不用指针也可以做到这一点。
不能返回局部变量的引用
//指针方式交换
 void swap(int *x, int *y)
 {
 int tmp = *x;
 *x = *y;
 *y = tmp;
 }
 void test01()
 {
 int a = 10;
 int b = 20;
 swap(&a,&b);
 cout << a << " " << b << endl;

 }


//引用交换

 void swap_ref(int &x, int &y)// int &x =a, int &y =b)
 {
 int tmp = x;
 x = y;
 y = tmp;

 }
int main()
 {
 int a = 10;
 int b = 20;
 swap_ref(a, b);
 swap(&a,&b);
 }

引用的本质

引用的本质是一个指针常量
type &b = a; 编译器底层这么实现的:
type *const b = &a;
#include <iostream>
 using namespace std;
void test01()
 {
 int a = 10;
 int &b = a;//编译器优化 int* const b = &a
 //指针常量 不能改变指针变量的指向
 // b =0x100;

 b = 1000;// *b =1000

 }
 void fun(int *&q)//int *&q = p ==> 编译器 int * const q =&p
 {

 }

 void test02()
 {
 int *p = NULL;
 fun(p);

 } 

指针的引用

套用引用公式: type &q = p
假设:
type为指针类型
  void fun ( int * & q ) // int* &q = p
  {
  }
  void test ()
  {
  int * p = NULL ;
  fun ( p );
  }

5 常量引用

const type &p = q;
常量引用代表不能通过引用去修改引用标识的那块空间
#include <iostream>
 using namespace std;
 void test01()
 {
 int a = 10;
 // const修饰的是引用& 不能通过引用去修改引用的这块空间的内容
 const int &b = a;
 //b = 1000;//err
 }

 void test02()
 {
 //int &b = 100;//不能引用常量
 const int &b = 1;//int tmp =1 ,const int &b= tmp

 }
 int main()
 {

 return 0;
 }

内联函数

内联函数的基本概念

c++ 中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。
内联函数具有普通函数的所有行为。唯一不同之处在于 内联函数会在适当的地方像预定义宏
一样展开,所以不需要函数调用的开销。 因此应该不使用宏,使用内联函数。 在普通函数 ( 非成员函数 ) 函数前面加上 inline 关键字使之成为内联函数。
但是必须注意必须
函数体和声明结合在一起,否则编译器将它作为普通函数来对待。
inline void func(int a);
以上写法没有任何效果,仅仅是声明函数,应该如下方式来做 :
inline int func(int a){return ++;}
注意 : 编译器将会检查函数参数列表使用是否正确,并返回值 ( 进行必要的转换 ) 。这些事预
处理器无法完成的。
但是函数调用就会占用空间:,但是内联函数相对于普通函数的优势只是省去了函数调用时候的压
栈,跳转,返回的开销。我们可以理解为内联函数是以空间换时间。
内联函数就是继承了宏函数的高效,并且不会出错,还可以当成类的成员函数用

2 宏函数和内联函数的区别

宏函数的替换是发生在 预处理阶段
内联函数的替换是发生在 编译阶段
宏函数容易出错,内联函数不会
内联函数和宏函数一样,都省去了调用函数的开销。
举个例子
//宏函数
#define MYCOMPARE(a,b) (a)<(b)?(a):(b)
//内联函数
 inline int mycpmpare(int a, int b)
 {
 return a < b ? a : b;
 }

 void test02()
 {
 int a = 1;
 int b = 5;
 //int c = MYCOMPARE(++a, b);// ++a<b?++a:b
 int c = mycpmpare(++a, b);
 cout << c << endl;

 }
类的成员函数默认编译器会将它做成内联函数
class Person{
public:
 Person(){ cout << "构造函数!" << endl; }
 void PrintPerson(){ cout << "输出Person!" << endl; }
 }

类里的成员函数默认为内联函数。

内联仅仅只是给编译器一个建议,编译器不一定会接受这种建议 ,如果你没有将函数声明为
内联函数,那么编译器也可能将此函数做内联编译。一个好的编译器将会内联小的、简单的
函数

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/420322.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(排序7)归并排序(递归)

归并排序 归并排序采用的是两个有序数组的归并。比如说现在想让一个数组有序。之前我们讲过&#xff0c;如果说你现在有两个有序数组的话&#xff0c;那么我们就可以把这两个有序数组给他合并成一个有序数组。两个有序区间归并的思路其实很简单&#xff08;这个也是归并的单趟…

Android 自定义View 之 计时文字

计时文字前言正文一、XML样式二、构造方法三、API方法四、使用五、源码前言 在Android开发中&#xff0c;常常会有计时的一些操作&#xff0c;例如收验证码的时候倒计时&#xff0c;秒表的计时等等&#xff0c;于是我就有了一个写自定义View的想法&#xff0c;本文效果图。 正文…

Vue2-黑马(八)

目录&#xff1a; &#xff08;1&#xff09;router-动态路由 &#xff08;2&#xff09;router-重置路由 &#xff08;3&#xff09;router-页面刷新 &#xff08;1&#xff09;router-动态路由 我们有这样一个需求&#xff0c;不同的用户根据自己的身份不一样&#xff0c;…

Seaborn 数据可视化基础

目录 介绍 知识点 Seaborn 介绍 快速优化图形 Seaborn 绘图 API 一、散点图&#xff1a; 参数hue hue hue_order 参数style 二 、线形图 三、类别图 绘制箱线图 绘制小提琴图 绘制增强箱线图 绘制点线图 绘制条形图 绘制计数条形图 四、分布图 五、回归图 …

nginx配置

单线程应用 稳定性高 系统资源消耗低 线程切换消耗小 对HTTP并发连接处理能力高 单台服务器可支持2w个并发请求 nginx与apache区别 Nginx相对于Apache的优点: 轻量级&#xff0c;同样是 web 服务&#xff0c;比Apache 占用更少的内存及资源&#xff0c;高并发&#xff0…

攻防世界-file_include(convert.iconv的使用)

代码审计&#xff0c;存在文件包含&#xff0c;直接上伪协议 发现不行&#xff0c;应该是存在字符过滤 知识盲区&#xff1a; 1.file://协议&#xff0c;需要填写绝对路径&#xff0c;只能读取txt文件&#xff0c;后面直接跟绝对路径。 file:///etc/passwd 2.php://filter …

深入浅出 Golang 内存管理

了解内存管理~ 前言&#xff1a; 本节课主要介绍了内存管理知识与自动内存管理机制&#xff0c;并对目前 Go 内存管理过程中存在的问题提出了解决方案&#xff0c;同时结合了上次课程学习的《Go 语言性能优化》相关知识&#xff0c;提供可行性的优化建议 … 自动内存管理 Go…

spring-boot怎么扫描不在启动类所在包路径下的bean

前言&#xff1a; 项目中有多个模块&#xff0c;其中有些模块的包路径不在启动类的子路径下&#xff0c;此时我们怎么处理才能加载到这些类&#xff1b; 1 使用SpringBootApplication 中的scanBasePackages 属性; SpringBootApplication(scanBasePackages {"com.xxx.xx…

C++linux高并发服务器项目实践 day5

Clinux高并发服务器项目实践 day5程序和进程单道、多道程序设计时间片并行和并发进程控制块&#xff08;PCB&#xff09;进程状态转换进程的状态进程相关命令进程号和相关函数进程创建父子进程的关系GDB多进程调试程序和进程 程序是包含一系列信息的文件&#xff0c;这些信息描…

你知道怎么实现定时任务吗?

诸位读者都知道笔者写东西都是用到才写&#xff0c;笔者的学习足迹自从参加工作之后就是 非系统 学习了&#xff0c;公司里源代码只要有笔者不知道的技术细节&#xff0c;笔者就会仔细的研究清楚&#xff0c;笔者是不喜欢给自己留下问题的那种学习习惯。 为何要写 笔者最近负…

如何使用Thymeleaf给web项目中的网页渲染显示动态数据?

编译软件&#xff1a;IntelliJ IDEA 2019.2.4 x64 操作系统&#xff1a;win10 x64 位 家庭版 服务器软件&#xff1a;apache-tomcat-8.5.27 目录一. 什么是Thymeleaf&#xff1f;二. MVC2.1 为什么需要MVC&#xff1f;2.2 MVC是什么&#xff1f;2.3 MVC和三层架构之间的关系及工…

AI绘图体验:想象力无限,创作无穷!(文生图)

基础模型&#xff1a;3D二次元 PIXEL ART &#xff08;1&#xff09;16-bit pixel art, outside of caf on rainy day, light coming from windows, cinematic still(电影剧照), hdr (2) 16-bit pixel art, island in the clouds, by studio ghibli&#xff08;吉卜力工作室…

配置基于WSL2的Docker环境并支持CUDA

导言 Content 正如前文windows 10 开启WSL2介绍的&#xff0c;我们可以在windows10中使用linux子系统。今天本文介绍如何在此基础上安装Docker并支持在wsl中使用GPU。 准备工作 加入windows insider preview。建议选Dev通道&#xff0c;不要选Beta。 安装Nvidia WSL2-compa…

【数据结构】-计数排序

&#x1f387;作者&#xff1a;小树苗渴望变成参天大树 &#x1f389; 作者宣言&#xff1a;认真写好每一篇博客 &#x1f38a;作者gitee:link 如 果 你 喜 欢 作 者 的 文 章 &#xff0c;就 给 作 者 点 点 关 注 吧&#xff01; 文章目录前言一、计数排序二、排序算法复杂度…

Nginx网站服务配置

一、Nginx概述 1.1 Nginx概述 Nginx&#xff1a; Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器&#xff0c;而且支持热部署&#xff0c;几乎可以做到 7 * 24 小时不间断运行&#xff0c;即使运行几个月也不需要重新启动&#xff0c;还能在不间断服务的情况下对软件…

分布式计算技术(上):经典计算框架MapReduce、Spark 解析

当一个计算任务过于复杂不能被一台服务器独立完成的时候&#xff0c;我们就需要分布式计算。分布式计算技术将一个大型任务切分为多个更小的任务&#xff0c;用多台计算机通过网络组装起来后&#xff0c;将每个小任务交给一些服务器来独立完成&#xff0c;最终完成这个复杂的计…

07 -全局状态管理

全局状态管理 7-1&#xff1a;开篇 在上一章中我们完成了 “一半” 的文章搜索功能&#xff0c;并且留下了一些问题。那么这些历史残留的问题&#xff0c;我们将会在本章节中通过 全局状态管理工具 进行处理。 那么究竟什么是 全局状态管理工具&#xff0c;如何在 uniapp 中…

【Flutter进阶】聊一聊组件中的生命周期、状态管理及局部重绘

前言 说到生命周期&#xff0c;熟悉Android开发的小伙伴一定第一时间会想到Activity的生命周期&#xff0c;由于在Flutter中一切都是组件&#xff0c;所以组件的生命周期其实是类似的。 在这个过程中组件的状态——State就非常重要&#xff0c;它记录这整个组件内可变部分的状…

【SSM整合】1—Spring和Mybatis整合

⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/Code_Learning ⭐⭐⭐⭐⭐⭐ Spring专栏&#x1f449;https://blog.csdn.net/weixin_53580595/category_12279588.html SpringMVC专栏&#x1f449;htt…

linux安装kafka

目录 目录 一.安装包准备&#xff1a; 二.解压安装&#xff1a; 先将该安装包放入到/opt/install目录&#xff1a; 解压该文件到soft目录中&#xff1a; 改名&#xff0c;方便后续使用&#xff1a; 三修改其中配置和配置环境变量&#xff1a; 3.1 修改/opt/soft/kafka2…