const 常量 解释 引用 函数

本文深入探讨了C++中const常量的行为特性及引用的多种应用场景,包括const常量的存储机制、函数返回引用的效率优势以及不同类型的引用初始化方式等。

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

#include "iostream"
using namespace std;
//在c语言里面可以修改 const 是一个冒牌货 a是一个只读的常量, 通过地址绕过去
//在c++ 里面不能被修改?
void main()
{
	//在c++ 里面通过const修饰的东西,会变成什么样子?
	const int a = 10;
	int *p = NULL;
	//当对a取地址的时候,有一个内存空间(&a a的内存空间)
	p = (int*)&a;//对a 取地址,对符号表中的value 重新分配内存 这里为10
	printf("%x  %x\n",p,&a);
//	printf("*p:%d\n", *p);  
	printf("修改之前a:%d\n",a);
	*p = 11;//这里的修改和符号表 没有任何关系
	printf("修改之后a:%d\n",a);
	//printf("*p:%d\n",*p);

	system("pause");
}
//解释:
 //c++ 编译器对const 常量的处理
//当碰见常量声明时,在符号表中放入常量 问题:那有如何解释取地址
//编译过程中若发现使用常量则直接以符号表中的值替换
//结论:
//1,c语言中的const 变量是只读变量,有自己的存储空间
//2,c++ 中的const常量
 //  可能分配存储空间,也可能不分配存储空间
// 当const 常量为全局,并且需要在其他文件中使用
//当使用& 操作符 取const常量的地址

//补充
//符号表示key valuse形式的。

void fun1()
{
#define a 10

}
void fun2()
{
	printf("a=%d\n",a);
}
void main11()
{
	fun1();
	fun2();
}
</pre><pre name="code" class="cpp">


#include "iostream"
using namespace std;
class Test1
{
public:
	Test1(int a=0,int b=0)
	{
		this->a=a;
		this->b=b;
	}
	Test1& T_add(Test1 &t2)
	{
	  this->a=this->a+t2.a;
	  this->b=this->b+t2.b;
	  return *this;
	}
	//从成员函数转化为全局函数 只需加一个this 指针(指向本类的类指针)

public :
	int a;
	int b;
};
//从全局函数转化为类的成员函数时,需要减一个左操作数参数
Test1 T_add(Test1 &t1,Test1 &t2)
{
	Test1 t3;
	t3.a=t1.a+t2.a;
	t3.b=t1.b+t2.b;
	return t3;
}
void main()
{
	Test1 t1(1,2),t2(3,4);
	system("pause");
}


对于函数的返回值,看似简单,但并非如此,比如:

int func(int a);该函数会返回一个int型,如果进行一个调用int result=func(3);会发生什么情况?

首先,func将返回值复制到一个匿名临时变量中,在这里假设该临时变量为anony(其实是没有名字的,这里方便阐述);然后,再将anony的值复制到result,可以看出,这里是进行了两次复制的。而并非一次复制。

 

对于返回引用的函数:

int & func(int &a);假设该函数传入一个int的引用,然后再func中修改它,再返回其引用,如果调用int reslut=func(b);会发生如下情况:

返回的是b的引用,因此相当于直接将b的值复制给了result。这里就只有一次复制(少了临时变量的复制,当然也创建了一个临时变量,只是该临时变量是b的一个引用)。

需要特别注意的是,按很多人的理解,这里返回的是一个引用,因此result就是b的引用,其实并非如此,这里返回引用只是减少了一次临时变量值的复制。如果真的要让result能够引用b,可以这样做:int &result = func(b);

注:返回普通变量的引用看不出效率的差异,但是返回比较大的类或者结构体的时候效率差异比较明显。

 

那如果是这样申明函数int func(int a);注意,这里返回的不是引用。然后int &result=func(a);会发生什么情况呢?

如果是这样,编译器将报错:不能用一个临时变量来初始化一个非常量的引用变量。

要消除这种报错,可以这样写const int &result=func(a);这样虽然返回的不是引用,但是由于最后赋给的是一个引用变量,因此在返回过程中也只有一次复制过程。但是这样的result是不能修改其引用的内容的。

 

还有一种看似更为诡异但却十分合理的情况:

int &func (int &a);同样假设该函数传入一个int的引用,在func中修改它,然后返回其引用。然后这样调用func(b)=3;这样的后果是,传入的b的值变为3。原因是func返回了一个b的引用,然后再将该引用赋为3,因此b的值也变成了3。

如果要禁止这种情况的发送,可以这样声明函数:const int &func(int &a);这样返回的是一个const引用,它不允许使用该引用修改其指向的值。因此如果有func(b)=3这样的调用,将通不过编译。

http://blog.youkuaiyun.com/piratejk/article/details/6162554


#include "iostream"
using namespace std;
int myf1()
{
  int a;
  a=11;
  return a;
}
int & myf2()
{
  int a;
  a=11;
  return a;
}
void main()
{
	int b1=myf1();
	int b2=myf2();//返回了一个值,返回赋给b2
	int &b3=myf2();
	printf("%d\n",b1);
	printf("%d\n",b2);
	printf("%d\n",b3);//b3 是一个引用类型 c++编译器会帮助我们执行*p
	system("pause");
}

int &j()
{
  static int a;
  a=10;
  printf("a:%d\n",a);
  return a;
}
void main()
{
	int b1=j();
	int &b2=j();
	//int b1=myf1();
	//int b2=myf2();//返回了一个值,返回赋给b2
	//int &b3=myf2();
	//printf("%d\n",b1);
	//printf("%d\n",b2);
	//printf("%d\n",b3);//b3 是一个引用类型 c++编译器会帮助我们执行*p
	//system("pause");
}

引用当左值

int &mygg()
{
	
  static int a=11;//a初始化的时候为10,只初始化一次
  printf("a:%d\n",a);
  return a;
}
void main()
{
	mygg()=11;//引用当左值
	mygg();
	system("pause");
}

int& myA(int &a)
{
  a++;
  return a;
}
void main()
{
	int b=10;
	int b1=myA(b);
	int b2=myA(b);
	int &b3=myA(b);
	printf("%d,%d,%d",b1,b2,b3);//11 12 13
	system("pause");
}
#include "iostream"
using namespace std;


struct AdvTeacher
{
	char name[32];
	int age ;
};

//
void getTeacher01(AdvTeacher **p)
{
	AdvTeacher *tmp = (AdvTeacher *)malloc(sizeof(AdvTeacher));
	tmp->age = 30;
	*p = tmp;
}

//这个是 结构体变量指针的引用 指针的引用
//p2是 t2 的别名
//
void getTeacher02(AdvTeacher * &p2)
{
	p2 = (AdvTeacher *)malloc(sizeof(AdvTeacher));
	p2->age = 30;
}

//如果不加引用 那么 myT3会copy给t3 
void getTeacher03(AdvTeacher &t3)
{
	t3.age = 11;
}

void main()
{
	AdvTeacher *t1 = NULL;
	AdvTeacher *t2 = NULL;
	getTeacher01(&t1);
	getTeacher02(t2);

	{
		AdvTeacher  myT3;
		myT3.age = 10;
		getTeacher03(myT3);

	}

	system("pause");
}

常引用

#include "iostream"
using namespace std;
struct Adv2Teacher
{
	  char name[32];
	  int age;
};
//const Adv2Teacher * const t3;
void getTeacher03(const Adv2Teacher &t3)//常引用
{
	//t3.age=110; 智能读不能写
	printf("%d\n",t3.age);
}
//const引用的作用,让变量所指向的内存空间只读
void main()
{
	//给const 引用初始化 有两种方法
	//1,让变量初始化const 引用
	int b=11;
	const int &a=b;
	//a=11; error 不能通过a间接修改b
	b=12;

	//2  const 引用的第二种初始化方法 
	///当使用常量(字面量)对const引用进行初始化的时候,c++ 编译器会为常量值分配内存空间,并将引用名作为这段空间别名
	//使用常量对const 引用初始化 以后将生成一个只读变量
	const  int &c=10;//10 是字面量
	//两种方式都分配了内存空间
	system("pause");
}

#include "iostream"
using namespace std;
//inline 请求关键字 内联编译
//内联函数体,需要和实现写在一起
#define MYFUNC(a,b)((a)<(b)?(a):(b))
inline int myfunc(int a,int b)
{
	return a<b?a:b;
}
void main1()
{
  int a=1;
  int b=3;
  //int c= myfunc(++a,b);;//2 3 2
  int c=MYFUNC(++a,b);//==>((++a)<(b)?(++a):(b)) 3 3 3 
  printf("a=%d\n",a);
  printf("b=%d\n",b);
  printf("c=%d\n",c);
  system("pause");
}
void myPrint(int x=3)
{
	printf("x:%d\n",x);
}
//如果函数参数带默认值,那么右边的参数都带默认值
void myPrint2(int z,int x=3,int y=4)
{
	printf("x:%d\n",x);
}
void main默认参数()
{
	myPrint(5);
	myPrint(3);
	 system("pause");
}
//函数占位参数
//占位符参数一般用于程序扩展和对c代码的兼容
int func(int a ,int b, int =0)
{
   return a+b;
}
void main()
{
	func(1,2);
	system("pause");
}


#include "iostream"
using namespace std;
void myprintf(int a,int b)
{
	printf("a:%d,b:%d",a,b);
}
void myprintf(int a,int b,int c)
{
	printf("a:%d,b:%d,c:%d",a,b,c);
}
int func(int a,int b)
{
  return a+b;
}
int func(const char * s)
{
	return strlen(s);
}
int func(int x)
{
  return x;
}

//定义个数组类型
typedef int (Array)[10];
//定义一个(指向数组的类型)指针类型
typedef int(*pArray)[10];
//定义了一个指针 类型(指向函数的指针类型)
typedef int (*PFUNC)(int a);
void main()
{
	int c=0;
	//myprintf(1,2); //二义性
	Array my;//相当于 int my[10]
	pArray pmy=NULL;//相当于 Array* pMy;
	PFUNC p=func;
	c=p(1);
	printf("c=%d\n",c);
	system("pause");
}



<think>好的,用户的问题是关于全局const常量和局部const常量的区别及作用域规则。我需要先理解这两个概念,然后结合提供的引用资料来组织回答。 首先,全局const常量是在全局作用域中声明的,作用范围是整个程序,而局部const常量则是在函数或代码块内部声明的,作用域仅限于其所在的块内。根据引用[3],使用let/const声明变量具有块级作用域,这可能也适用于const常量。但要注意,全局常量的作用域是全局的,而局部的只能在块内访问。 接下来,生命周期方面,全局const常量在程序整个运行期间存在,而局部的则随着作用域的结束而释放。引用[4]提到,全局变量(包括const)需要是常量表达式,在编译时确定,而局部const可能允许某些运行时计算的值?不过根据引用[4],const必须是在编译时可计算的值,不管全局还是局部。这点可能需要确认。 初始化方面,全局const必须在声明时初始化,且使用常量表达式。局部const同样需要声明时初始化,但可能允许更复杂的表达式,只要在编译时能确定?比如函数内部的const是否可以由函数参数计算?或者是否必须是编译时常量?根据引用[4],全局的必须使用编译时可计算的值,而局部的可能允许某些情况,但可能还是需要编译时确定,比如在Rust中,但用户的问题可能不特定于Rust,而是通用的。需要区分不同语言的规则,但用户可能指的是JavaScript或一般编程概念,但根据引用资料,引用了Rust和JavaScript的内容,可能需要综合说明。 内存分配方面,全局const可能在程序的只读数据区,而局部的可能在栈上?或者是否会被编译器内联优化?例如,引用[4]提到全局变量需要处理线程安全,但const是不可变的,所以可能没有这个问题。 作用域规则方面,全局const可以被整个程序访问,但需要注意可能的命名冲突。局部const仅在块内有效,避免污染全局空间。引用[3]强调了块作用域的重要性,推荐使用let/const,这可能适用于局部const的优势。 然后需要总结它们的区别,包括作用域、生命周期、初始化要求、内存分配和使用场景。同时,根据引用中的内容,比如引用[2]提到const在调试时保留类型和作用域信息,这可能适用于局部const的好处,而全局const在程序全局可用。 最后生成相关问题,可能需要涵盖应用场景、选择建议、不同语言中的差异等。</think>### 全局 `const` 常量与局部 `const` 常量的区别及作用域规则 #### 1. **作用域范围** - **全局 `const` 常量** 在全局作用域(如文件顶层或全局代码块)中声明,作用域覆盖整个程序或文件。例如在 JavaScript 中: ```javascript const GLOBAL_CONST = 100; // 全局常量 function example() { console.log(GLOBAL_CONST); // 可访问 } ``` 引用[3]指出,全局作用域中声明的变量(包括 `const`)可在任意位置访问[^3]。 - **局部 `const` 常量** 在函数、代码块(如 `if`、`for`)或模块内部声明,作用域仅限于其所在的块。例如: ```javascript function example() { const LOCAL_CONST = 10; // 局部常量 if (true) { const BLOCK_CONST = 5; // 块级常量 console.log(BLOCK_CONST); // 可访问 } // console.log(BLOCK_CONST); // 报错:超出作用域 } ``` 根据引用[3],块级作用域通过 `const` 实现,变量仅在当前块内有效。 --- #### 2. **生命周期** - **全局 `const` 常量** 生命周期与程序运行周期一致,在程序启动时初始化,结束时释放。 - **局部 `const` 常量** 生命周期随作用域结束而终止。例如函数内的 `const` 常量函数执行完毕后被销毁。 --- #### 3. **初始化要求** - **全局 `const` 常量** 必须在声明时初始化,且值必须是**编译期常量表达式**(如字面量或数学表达式)。例如在 Rust 中: ```rust const GLOBAL_PI: f32 = 3.1415; // 合法 // const INVALID: f32 = calculate_value(); // 非法:非编译期可计算值 ``` 引用[4]强调,全局常量需通过常量表达式初始化[^4]。 - **局部 `const` 常量** 同样需在声明时初始化,但某些语言(如 JavaScript)允许通过运行时表达式赋值: ```javascript function calc() { const result = 10 * Math.random(); // 合法(JavaScript 中允许) return result; } ``` (注:Rust 等强静态语言中局部 `const` 仍需编译期确定值。) --- #### 4. **内存分配** - **全局 `const` 常量** 通常存储在程序的**只读数据段**,不可修改且无内存分配开销。 - **局部 `const` 常量** 可能存储在栈或寄存器中,编译器可能直接内联优化。 --- #### 5. **典型应用场景** - **全局 `const` 常量** 用于定义跨模块共享的固定值(如数学常数、配置参数)。 - **局部 `const` 常量** 用于限制作用域,避免命名冲突或临时计算(如循环中的固定步长)。 --- ### 总结对比 | 特性 | 全局 `const` 常量 | 局部 `const` 常量 | |---------------------|-----------------------------------|----------------------------------| | **作用域** | 全局 | 块级(函数、代码块) | | **生命周期** | 程序运行期间 | 随作用域结束终止 | | **初始化要求** | 必须为编译期常量表达式 | 允许运行时表达式(视语言而定) | | **内存分配** | 只读数据段 | 栈/寄存器(可能被内联优化) | | **线程安全** | 隐式安全(不可变) | 隐式安全(不可变) | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值