c++_狄泰总结01c语言升级与特异

C++ 之c语言升级(c++ ==>++ 类型的增强,面向对象的加入)

1)实用性:
c++所有变量都可以在使用时定义  for (int i=1;i<=3 😉
C语言:必须在作用域开始位置定义  int i for(i;i<3;i++)

2)register: 
=>c语言
请求编译器尽可能的将变量存在CPU内部寄存器中,而不是通过内存寻址访问,以提高效率(从寄存器取比在内存中取快多了)
无法获取register 变量的地址 (对register变量取地址是不合法的)
==>c++编译器有字节的优化方式 可以取得register 变量地址 ,基本看不到,只是为了兼容C{老款c++还有一点用,但新版c++开发基本没用}

c++中: (1)register 关键字无法在全局中定义变量,否则会被提示为不正确的存储类。 (2)register
关键字在局部作用域中声明时,可以用 & 操作符取地址,一旦使用了取地址操作符,被定义的变量会强制存放在内存中。

在c中: (1)register 关键字可以在全局中定义变量,当对其变量使用 & 操作符时,只是警告“有坏的存储类”。
(2)register 关键字可以在局部作用域中声明,但这样就无法对其使用 & 操作符。否则编译不通过。

建议不要用register关键字定义全局变量,因为全局变量的生命周期是从执行程序开始,一直到程序结束才会终止,而register变量可能会存放在cpu的寄存器中,如果在程序的整个生命周期内都占用着寄存器的话,这是个相当不好的举措。

版权声明:本文为CSDN博主「计算机的小粽子」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/tangyuanzong/article/details/78012277

3)struct 关键字的加强
=>c语言 中的struct 定义了一组变量的集合(sturct 定义的标示符并不是全新的类型,需要用到typedef给类型:typedef struct _tag_student Student)
=>c++中的struct用于定义一个全新的类型,且访问权限是public

4)全局变量重定义
c:重复定义多个同名的全局变量是合法的 {多个同名的全局变量最终会链接到全局数据区的同一地址空间}
c++:不允许定义多个同名的全局变量

5)int f() 与int f(void) 有区别吗? 如果有区别是什么? //面试小问题 要看用什么编译器:如果是用c编译器类型.如果是c++编译器
c 语言:
int f():返回值为int,参数为任意的函数,二义性
int f(void):返回值为int的无参(不接受任何参数)函数,
c++:
返回值是int的无参(不接受任何参数)函数
//c语言的默认类型在c++是不合法的,c++不允许不写返回值类型
c 语言缺省为int类型

6)const

总结 c与c++的const
c const是只读变量,会分配空间 const int a=3 ; const int b=4; int p[a+b]={0}; //因为是变量,在编译过程中会报错
c++ const 定义的是真正的常量 ,值存储在符号表中 const int a=3 ; const int b=4; int p[a+b]={0}; //因为是常量,在编译过程不会报错
不过c++的const 常量也可能分配空间{ //只是为了兼容C
对const 全局常量使用了 extern //const extern c , 需要在其他文件中使用
对const常量使用了&操作符 // const int c int p = (int) &c; c}

问1:那么c++中的const与宏定义(#define c 5) 有什么区别
宏定义: 只是简单的文本替换,在预处理阶段执行 无作用域

c++ const:编译过程 会对const 常量进行 类型检查和作用域检查

C语言中的const
1)const 修饰的变量是只读的,本质上还是变量
2)修饰的局部变量上分配空间
(在栈上的值还是可以改变的==>const int c=0,定义一个指针int p,p=&c指向这个const变量的地址 ,然后p=5,运行结果c=5,p=5)
3)修饰的全局变量
***只读存储区分配空间(修改这个的话,程序会发生崩溃)
4)只在编译期有用,在运行期无用
(const 修饰的变量不是真的常量,他只是告诉编译器该变量不能出在赋值符合的左边)
(const 只是使变量具有只读属性,将具有全局生命周期的变量存储于只读存储区)
问1:你有办法在c语言中有办法定义一个真正的常量吗?
可以 ? =>怎么定义?const 定义 =>c语言基础不扎实=> 只有用enum枚举才可以

c++中的const :是一个真正意义上的常量 ,为这个分配空间,只是为了兼容C的语法特性
1)在符合表定义常量 (符合表:在编译过程中产生的表,程序的编译过程中会有各种数据结构,内部的数据结构里面会有一个表)
const int c=0, 给c分配了4个字节的内存空间,但c=0 存放在表中 ,分配空间是为了兼容c
int p,p=(int)&c 定义一个指针,指向这个const变量的地址 ,
然后*p=5,
运行结果c=0,*p=5 )
编译过程中若发现使用const常量,则直接以符合表中的值替换

2)在编译过程中,发现以下情况会给对应的常量分配存储空间,c++虽然可能为const常量分配存储空间,但并不会使用其存储空间中的值(无法用指针来修改栈空间的值,因为是存放在符合表中)
对const 全局常量使用了 extern //const extern c , 需要在其他文件中使用
对const常量使用了&操作符 // const int c int p = (int) &c; c

7)布尔值
c 语言 用int的整形值来表示布尔值 {0 为false 其他为true}

c++ ==>++ 类型的增强,面向对象的加入
7.1)明确增加 bool 基本类型
7.2)bool 只占用一个字节 {true :真值:编译器内部用1表示 ;false,用0表示 }
布尔类型支持数学运算,参与数学运算 ,但对输出结果会进行判断 非0值 为1

bool b=0  ==>bool b=false
b++	//b=1 ,非0值  结果:1
b=b-3 	//-2 ,非0值  结果:1
b=3,a=b // a=1,b=1;

7.3)基本类型能修饰的,bool类型一样可以

8)三目运算符

int a =1; int b=2
	(a <b ? a:b) =3 
	=>c语言编译不过,三目运算返回的是变量值:2
	=> c++  a=3;C++三目运算返回的是变量本身:b ,但可能返回的值都是变量的话,才能作为左值使用 . (a <b ? a:2) =3 ==>这样是不行的

**

C++特色篇

**

**9)C++引用 &

int& a=c ; => int const a*
变量
一段实际连续存储空间的别名
程序通过变量来申请并命名存储空间
通过变量的名字可以使用存储空间
问题1:一段连续的存储空间只能有一个别名吗?>引出c++的引用24:16
问题1:一段连续的存储空间只能有一个别名吗?
>引出c++的引用

引用:看做一个已定义变量的别名

C++引用总结
从使用上说:引用:作为变量别名而存在,旨在代替指针,可以尽可能的避开内存错误
引用的最终本质为指针 在编译起内部使用指针常量(在内存空间占用4个字节)实现 int& a=c ; => int *const a =c;
=>对引用的操作==>对指针所指地址的操作 ==>C++为了实用性隐藏了引用的存储空间的这个细节
特殊引用:const 引用
1)可以使得变量具有只读属性 const Type& name =var
int a=1 const int& b=a; =>a 只读
{2)当使用常量对const引用进行初始化时,c++编译器会为常量值分配空间,并将引用名作为}
//const int& b =1(特例,正常引用是不能是常量); b=5 fail; int p= (int )&b; *p=5;

引用的语法 Type& name = var;

eg:
	int a=4;
	int& b=a;
	b=5  =>a=5;

注意:普通引用在定义时必须用同类型的变量进行初始化

c++: 一门强类型的语言

int a=5;  int $b   	错,无声明是哪个变量的别名
int a=5;  float& b =a ;,必须与a是同类型
int a=5;  int& b =1 ;,常量就是常量,无法定义为变量的别名

14:49 19:!1 26:15 38:37 42:40 43:10 46:50 48:32
面试题:引用有自己的存储空间吗?
会的?=>功能像指针?==>本质是指针 ==>占用的空间大小与指针一样

**10) inline内联函数

(在c++开发中,内联函数是首选)**
c++ 中用const常量替代  宏常数定义
C++内联函数 =>c语言的 宏函数替换  (拥有匹美宏函数替换的效率,以及不容易出错)
2:31 3:33 4:25 5:13 23:28 37:10

10.2)inline 请求 =>编译器可以拒绝=>c++编译器不一定同意inline 内联函数=>不同意,转变为普通函数,会有出栈入栈等操作
10.3)内联函数成功 =>(效率媲美宏函数,省去了函数调用时压栈,跳转和返回的开销)
10.4)编译器直接将内联函数扩展到函数调用的地方

10.5)内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽视内联请求

inline int func(int a,int b)
{
	return a<b ?a:b;
}

10.6)现代c++编译起提供了扩展语法,能够对函数进行强制内联
g++ 内联请求失败 =>可以重新配置,接受内联函数 => _attribute((always_inline))属性
MSVC : __forceinline
vc++ :推荐inline
eclipse:宏代码块只是简单的替换

10.7)c++中inline 内联编译达到限制
10.7.1)不能存在任何形式的循环语句
10.7.2)不能存在过多的条件判断语句
10.7.3)函数体不能过于庞大,没有标准度量,取决于编译技术 //现代的c++编译器而已,只要不是太过复杂,前3个可以不考虑,
10.7.4)不能对函数进行取址操作
10.7.5)函数内联声明必须在调用语句之前

11)C++函数默认参数和占位参数

11.1)c++ 可以在函数声明时为参数提供一个默认值

int mul(int x =0);
int mul (int x)
{
}

问题1:函数定义是否可以出现参数的默认值? 不可以
问题2:当函数声明和函数定义中的默认参数不一样会怎样? //编译报错
问题3:定义给,声明不给?  不可以

函数默认参数的规则
1)参数的默认值从右向左提供  //设计函数时 
int add(int x,int y=0 ,int z) ==>这样是错的   如果只想给y提供默认值 =>需要替换位置 int add(int x,int z ,int y=0)
2)函数调用时使用了默认值,从左到右使用默认值,后续参数使用其默认值 //调用函数时

int add(int x,int y=0,z=0)
int add(int x ,int y, int z)
{
	return x+y+z;
}

add(1)  // 1
add(1,2) //3
add(1,2,3)//6

11.2)c++可以为函数提供占位参数 //为了规范这种写法,用于兼容c的不规则写法(c语言移植到c++,只需要很少的修改)

c语言不写函数参数
void fun();
fun(1,2)

解释:c语言 void func() 可以是任意参数,fun(1,2) 编译通过 => void func(void) fun(1,2) 报错,void不接受任何参数

但这个拿到c++编译: void fun() 相当于不接受任何参数,fun(1,2)编译会出错
针对这个,c++ 提供占位参数 void funt(int x , int) =>引入占位参数 void func(int,int) 编译通过

c语言移植到c++,只需要很少的修改,就可以编译通过

**

12)函数重载 overload:同一个标识符在不同的上下文有不同的意义

函数重载概念:
12.1)用同一个函数名定义不同的函数,函数名和不同参数的搭配   =>C语言会编译错误
12.2)本质为相互独立的不同函数
12.3)c++函数重载是由函数名和参数列表所决定的,函数返回值不能作为参数重载的依据无关
==>c++函数重载,可以编译
int func(int x) =>fun(3)
int func(int a,intb) =>fun(4,5)
int func(const char *s) =>fun(“D.T.software”)

12.4)函数重载条件:满足其一就可以
参数个数不同 参数类型不同 参数顺序不同 
int func(int a, const char *s) 与 int func(const char *s,int a)

12.5)编译器调用函数重载的准则: ==> 当函数默认参数遇上函数重载?
将所有同名函数作为后选者
尝试寻找可行的候补函数(通过匹配实参;通过默认参数匹配实参,通过默认类型转换匹配实参)
匹配失败情况:
1)最终寻找到的候选函数不唯一,出现二义性,编译失败
2)无法匹配所有候选者,函数未定义,编译失败 //int fun(int a,intb) fun(1)

12.6)c++的一个特性冲突  24:49 28:23  19:40
那么fun(1,2)会调用哪一个?==>因此编译器会报错,出现二义性
int fun(int a,int b ,int c=0);
{
return abc =>func(1,2),匹配默认参数规则
}
int fun(int a ,int b)
{
return a+b =>func(1,2) 符合函数重载特性
}

12.7)重载函数 拥有不同的内存地址 13:47
当重载函数碰上函数指针会怎样?
12.7.1)根据重载规则挑选与函数指针参数列表(函数指针所指向的函数类型)一致的候选表
12.7.2)严格匹配 候选者的函数类型与函数指针函数类型  =>(需要考虑函数重载的返回值)

7.1.1)int func(int x) 
7.1.2)int func(int a,int b) 
7.1.3)int func(const char*s)
typedef int(*PFUNC) (int a);	==>7.2.1)typedef void(*PFUNC) (int a); 
//当这个改成非int时,编译会报错,函数指针找不对对应重载函数
	int c=0
	PFUNC p=func;
	c= p(1)	=> 7.1.1)int func(int x)

12.8)函数重载必须发生在同一个作用域

12.9) 函数重载回顾(意义:拓展已经存在的功能)

12.9.1)函数重载的本质为相互独立的不同函数
12.9.2)C++通过函数名和函数参数确定函数调用
12.9.3)无法直接通过函数名得到重载函数的入口地址

	=> printf("%p\n",func);,编译报错,无法分辨 
	=> printf("%p\n",int(*)(int,int)  func); // int(*)(int,int) =>函数指针的类型,强制转换函数类型
**函数重载必然发生在同一个作用域中 //作用域都不一样了,那就不是函数重载了**

12.9.4)普通成员函数,静态成员函数,它们之间可以构成函数重载。
12.10)哪些可以构成函数重载
全局函数(全局函数位于全局的命名空间),类的成员函数(成员函数位于具体的类),它们之间不可以构成函数重载。(作用域不同)
类的成员函数之间是可以构成函数重载。

**

13)c++和c代码 相互调用

**
1)实际工程中c++和c代码相互调用是无可避免的 (有一些c代码不是源码了,已经编译了,c++ 代码要能使用c代码编写的库)
2)c++编译器会优先使用c++编译方式
3)extern 关键字能强制让c++编译器进行c方式的编译

extern "C"
{ //do C-style compile}

4)要保证一段代码即被c++编译,也被c编译==>

13.1)extern 关键字能实现C和C++的相互调用 --实例

注意点,c++编译器不能以c的方式编译重载函数
既extern 的代码块内不能有重载函数
{编辑方式决定函数名被编译后的目标名:c++将函数名和函数参数编译成目标名,而c函数将函数名编译成函数名,重载函数不适用}

*C++中调用C代码
=>
cpp.h文件声明接口以c的方式编译
.c 文件具体实现
cpp文件直接使用
编译g++ testC.c testA.cpp
C中调用C++代码
1)用一个c函数封装了c++的接口,用extern "C"说明以c语言方式命名
2)在c函数中调用这个c封装函数
/

背景:解析:
addc.  =>  gcc add.c -o add.o C语言编译成二进制文件add.o
#include "add.h"
int add(int a,int b)
{
	return a+b;
}

#ifndef ADD_H
 define ADD_H
	int add(int a,int b);
#end if;++ 用extern{} 强制规定用c编译
main.cpp:
	#include <stdio.h>
	#include "add.h"
	int main()
	{
		int c=add(3,5);
		printf("c=%d",c);
	}

1)问题1: 直接这样编译是不行的 g++ main.c -o main.o
nm add.o //查看有没有add函数 ,但add.o 已经是c编译的 ,而mian.cpp是c++代码的,想要调用c的代码,但c的代码已经编译成目标文件了,想要调用add函数,调用失败.
=>add.c已成了编译物,定义到add.o里面去了,因此如果要用,需要把这个转为add.o,因此用extern

extern "C"
{
	#include "add.h"		
}
==>这样才能c++里面调用c编译里的老代码里的函数.声明里面的是c代码,必须用c编译的方式来编译=>编译通过 ,g++ main.c -o main.o 

2)问题2:如果加了这个extern,C++可以编译,但在c那里编译不过 gcc main.c -o main.o
如果一段C代码只会以c的方式编译? =>extern “C” ==> C++才有,c是没有这种说法的=>需要保证既能被c++编译,也能被c编译=>对策,检验这个是不是c++代码 => __cplusplus
__cplusplus 是c++编译器内置的标准宏定义 :意义,用来测试当前的编译器是不是c++编译器

#ifdef __cplusplus
extern "C"
{
#end if
	#include "add.h"
	 
#ifdef __cplusplus	
}
#end if

C语言调用c++里的函数
C与C++相互调用案例解析

**

14)c++中新引入的成员

**

14.1)动态内存分配 new
C语言是通过malloc函数进行分配的 =>偏底层 =>也许有些硬件/编译器不支持malloc
malloc 是由库函数提供
malloc 基于字节来分配的
malloc 不具备内存初始化的特性
C++
=>通过new 关键字进行动态内存申请,new以具体类型为单位进行内存分配
=>通过delete 关键字进行动态内存释放
==>动态分配是基于类型的
new 在申请单个类型变量时可进行初始化操作
int * pi =new int(1); =>%d *pi=1  //申请单个int变量,将单个变量初始化为1
int *pa =new int[1] ;申请一片数组空间,这片数组空间大小为1=> pa 指向一片数组,里面只有一个元素
float *pf =new float(2.0f)l =>%f *pf=2.000000
char *pc =new char(‘c’) =>%c *pc =c

//变量申请
Type
pointer = new Type; //从堆空间获取一个type类型的变量出来 ,并且将pointer 指向type 变量所占地址的地址空间
//…
delete pointer;
*

//数组申请 7:31
int
pointer = new int[10]; //从堆空间获取一个type 数组大小 ->动态申请的字节空间会比实际申请的数组空间会比实际大一点 => 答案是至少是40个字节,而不是40个字节
//…
delete[] pointer; => 释放整个数组空间 => delete point=>只释放了指针指向的第一个结构的空间,会造成内存泄露.
*

**

15)c++的命名空间 namespace

**
命名空间概念
命名空间将全局作用域分成不同的部分 解决全局变量名字冲突
=>不同命名空间中的标识符可以同名而不会发生冲突
=>命名空间可以相互嵌套
=>全局作用域也叫做默认命名空间

C++命名空间的使用
使用整个命令空间:using namespace name;
使用命名空间中的变量:usingname::variable
使用默认命名空间中的变量::variable

代码案例:

#include <stdio.h>

namespace First	//将全局空间分成各个命名空间
{
    int i = 0;
}

namespace Second
{
    int i = 1;
    
    namespace Internal
    {
        struct P
        {
            int x;
            int y;
        };
    }
}

int main()
{
    using namespace First;
    using Second::Internal::P;
    
    printf("First::i = %d\n", i);	//i=0;
    printf("Second::i = %d\n", Second::i);	//i=1;
    
    P p = {2, 3};
    
    printf("p.x = %d\n", p.x);	//p.x = 2
    printf("p.y = %d\n", p.y);	//p.y=3
    
    return 0;
}

C++ 新型的类型转换,以关键字的方式出现
强制类型转换在实际工程是很难避免的
c 语言强制类型转换 =>太粗暴 难定位(无法快速定位内型转换方式)=>

3:23 12:45

*****C++四种强制类型关键词转换(编译出问题,但运行不会出问题)
用法: xxxx_cast< Type >(Expressio)
static_cast  ==>静态
用于 基本类型 间的转换(int double float)
用于有继承关系类对象之前的转换和类指针之间的转换
(不能在 基本类型指针(reinterpret_cast)间进行转换)

void static_cast_demo()
{
    int i = 0x12345;
    char c = 'c';
    int* pi = &i;
    char* pc = &c;
    c = static_cast<char>(i);//int-> char ,ok
    pc = static_cast<char*>(pi);
    //int*->char* error,基本指针类型转换用 pc=reinterpet_cast<char*>(pi*)
    //error: invalid static_cast from type ‘int*’ to type ‘char*’
}

const_cast
用于去除变量的只读属性 (const int&)
只能用于引用或者指针之间的强制类型转换
const int x= 2
int& y=const_cast<int&>(x); =>将这个常量的内存空间起了个别名y

void const_cast_demo()
{
    const int& j = 1;
    int& k = const_cast<int&>(j);
    
    const int x = 2; ==>定义了一个常量,数值存放在符号表中
    int& y = const_cast<int&>(x);==>这里虽然可以编译同,但实质是将这个常量的内存空间起了个别名y
    
    int z = const_cast<int>(x);==>error,const_Cast只能用于指针或引用 x是个常量
    =>error: invalid use of const_cast with type ‘int, which is not a pointer, reference, nor a pointer-to-data-member type
    k = 5;	=>解引用后是一个普通变量,所以可以赋值
    printf("k = %d\n", k);
    printf("j = %d\n", j);
    y = 8;>相当于给那个空间赋值为8
    printf("x = %d\n", x);
    printf("y = %d\n", y);
    printf("&x = %p\n", &x);
    printf("&y = %p\n", &y);
}

reinterpret_cast :
可以指针类型间进行转换,
用于整形和指针间类型转换也是可以的,//嵌入式开发有可能将整数强制转换为指针
不能进行基本类型转换

void reinterpret_cast_demo()
{
    int i = 0;
    char c = 'c';
    int* pi = &i;
    char* pc = &c;
    
    pc = reinterpret_cast<char*>(pi);
    pi = reinterpret_cast<int*>(pc);
    pi = reinterpret_cast<int*>(i);
    c = reinterpret_cast<char>(i); //Error,基本类型转换应该用 c= static_cast<char>(i);
    => invalid cast from type ‘int’ to type ‘char}

dynamic_cast //c语言所没有的强制转换
前提:需要虚函数的支持 //定义的类里面必须要有虚函数才行
用于有继承关系的类指针间的转换 //只能用于类对象的指针,而且还需要有虚函数的支持
用于有交叉关系的类指针间的转换
具有类型检查的功能 //不成功的话,这个会返回空指针
***在每一次的转换前,都要想一想这属于哪一类型的转换,需要用哪个关键词

void dynamic_cast_demo()
{
    int i = 0;
    int* pi = &i;
    char* pc = dynamic_cast<char*>(pi); //没有虚函数,指针间转换用reinterpret_cast<char*>(pi);
    //cannot dynamic_cast ‘pi’ (of type ‘int*’) to type ‘char*’ (target is not pointer or reference to class)
     char* pc = dynamic_cast<char*>(pi);

}

C语言方式的类型转换:

typedef void(PF)(int) ;
struct point 
{
	int x;
	int y;

}

int main()
{
	int v=0x12345;
	PF *pf =(PF *)v;
	char c= char(v);
	Point *p= (Point*)v;
	pf(5);
	printf("p->x =%d\n",p->x);
	printf("p->y =%d\n",p->y);
}

g++ 编译通过,但段错误
	

16)C++标准库

里面定义了多数常用的数据结构
-< bitset > -< set> < cstdio>
< deque> < stack> < cstring>
< list> < vector> < cstdlib>
< queue> < map> < cmap>

使用方式:
#include < cstring>
using namespace std;

在这里插入图片描述

17)C++标准库 提供string 类型:

c语言不支持真正意义的字符串(用字符数组和一组函数实现字符串操作)
c语言不支持自定义类型,因此无法获得字符串类型
c++原生没有字符串类型,但允许自定义类型,c++标准库提供了string类型

在这里插入图片描述17.1) 字符串类与数字的转换

#include <iostream>
#include <sstream>	//字符串流类相关文件
#include <string>

using namespace std;
//isstringstream --字符串输入流
string -> 数字:
	isstringstream iss("123.45");
	double num;
	iss >> num.
数字->string:
	ostringstream oss
	oss << 456.78
	string s =oss.str()
//字符串输出流

#define TO_NUMBER(s, n) (istringstream(s) >> n)
 	//这里简化是直接利用构造函数istringstream(s),生成一个临时对象,重载右移操作符号(c++标准库提供)
#define TO_STRING(n) (((ostringstream&)(ostringstream() << n)).str())

int main()
{

    double n = 0;
   
    if( TO_NUMBER("234.567", n) )
    {
        cout << n << endl;    
    }

    string s = TO_STRING(12345);

    cout << s << endl;     
    
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值