const 是 constant 的缩写,本意是不变的,不易改变的意思。在 C++ 中是用来修饰普通类型变量,指针变量,函数参数、函数返回值。
1.1 const修饰普通变量const int value = 10;
int b = value;//正确
value = 0;//错误
value变量此时被const修饰为常量,可以将value的数值赋值给其他变量,但不可以修改value的内容。若修改了,编译器会报错,因为对一个常量进行修改 *** 作是违法的。
1.2 const修饰指针变量const 修饰指针变量有以下三种情况:
- cosnt修饰指针指向的内容
- const修饰指针
- const同时修饰指针和指针指向的内容
cosnt修饰指针指向的内容
int value = 456;
const int *p = &value;
int a = 10;
*p = 124;//错误
p = &a;//正确
此时const修饰的只是指针指向的内容,故指针自身的指向是可变的。由于这种情况下const是在*
的左边,且修饰的是指针指向的内容,因此也叫作“左定值”
const修饰指针
int value = 456;
int* const p = &value;
int a = 10;
*p = 123;//正确
p = &a;//错误
此时const 修饰的只是指针,其指针指向的内容是可以改变的。由于这种情况下const是在*
的右边,且修饰的是指针,因此也叫作“右定向”
const同时修饰指针和指针指向的内容
int value = 110;
const int* const p = &value;
int a = 200;
*p = 10;//错误
p = &a;//错误
此时const同时修饰了指针和其指向的内容,故两个都是不可变的。
1.3 const修饰函数参数const修饰函数参数有三种:
- const修饰普通类型变量进行值传递
- const修饰指针进行参数传递
- const修饰自定义类型对象进行参数传递
const修饰普通类型变量进行值传递
void fun(const int a)
{
int b = a;//正确
a++;//是错的
}
一般这种情况不需要 const 修饰,因为函数会自动产生临时变量复制实参值。
const修饰指针进行参数传递
void func1(const char *p)
{
//....
}
void func2(char* const p)
{
//....
}
使用const修饰过函数的指针形参,可以有效防止函数内部对指针的指向或者其指向的内容被意外篡改。
const修饰自定义类型对象进行参数传递
class Test
{
public:
Test(){}
Test(int _m):_cm(_m){}
int get_cm()const
{
return _cm;
}
private:
int _cm;
};
void Cmf(const Test& _tt)
{
cout<<_tt.get_cm();
}
自定义类型的参数传递,需要临时对象复制参数,对于临时对象的构造,需要调用构造函数,比较浪费时间。采取 const 外加引用传递的方法,可以不需要调用构造函数,又可以防止函数对对象内容意外篡改。
1.4 const修饰函数返回值const修饰函数f返回值有三种:
- const 修饰内置类型的返回值,修饰与不修饰返回值作用一样
- const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改
- const 修饰返回的指针或者引用
const int myFun();//修饰与不修饰的效果一样
const MyClass getMyClassObj();//此时返回的类型对象不能作为左值使用,既不能被赋值,也不能被修改
int* const MyClass getMyClassObj();//返回一个对象指针,该指针的指向不可以改变。
1.5 const修饰类中的成员函数
const 修饰类成员函数,其目的是防止成员函数意外修改被调用对象的值,如果我们不想修改一个调用对象的值,所有的成员函数都应当声明为 const 成员函数。
class Test
{
public:
Test(){}
Test(int _m):_cm(_m){}
int get_cm()const //const修饰成员函数 get_cm()
{
return _cm;
}
private:
int _cm;
};
void Cmf(const Test& _tt)
{
cout<<_tt.get_cm();
}
如果 get_cm() 去掉 const 修饰,则 Cmf 传递的 const _tt 即使没有改变对象的值,编译器也认为函数会改变对象的值。所以我们尽量按照要求将所有的不需要改变对象内容的函数都作为 const 成员函数。
若我们需要在const 修饰过的类成员函数中,加入一些能修改被调用对象的成员的 *** 作,可使用关键词mutable。其意为可变的。被该关键词修饰过的成员变量内容是可以不断改变的。
class Test
{
public:
Test(){}
Test(int _m):_cm(_m){}
int get_cm()const //const修饰成员函数 get_cm()
{
cnm++;//可以正确编译运行
return _cm;
}
private:
int _cm;
mutable int _cnm;
};
2.new/delete和malloc/free
2.1 malloc/free
用于分配和释放内存
申请内存,并确认是否申请成功
char *str = (char*) malloc(100);
assert(str != nullptr);
释放内存,指针置空避免野指针的出现
free(p);
p = nullptr;
2.2 new/delete与malloc/free的区别
- new/delete是C++中的 *** 作符,而malloc/free是标准库函数
- new在申请内存时会自动计算所需字节数,而malloc需要我们自己手动输入申请内存空间的字节数
- new/new[]完成了两件事,先是底层调用malloc申请分配内存空间,然后调用构造函数创建对象
- delete/delete[]完成了两件事,先是调用析构函数清理资源,然后底层调用free释放内存空间
- strlen()是库函数,而sizeof是运算符
- sizeof是求数据类型所占的空间大小,而strlen是求字符串的长度,遇到/0就会结束。
char *c="abcdef;"printf
("%d %d/n",sizeof()c,strlen()c);char
输出结果为 4 6。c的类型是char *,里面放的都是地址,而地址的长度当前是由地址总线的位数决定的,若编译器是以32位作为计算机的地址总线数,也就占4个字节。6是表示字符串长度,字符串以’\0’为结尾,strlen不统计’\0’字符
[ d]="abcdef";printf
("%d %d/n",sizeof()d,strlen()d);char d[]="abcdef"
输出结果为7 6。char,初始化后,字符数组的内容实际为{‘a’,‘b’,‘c’,‘d’,‘e’,‘f’’,’\0’},故d数组的所占空间字节大小为7
[ e]='a'{,'b','c','d','e','f'};printf
("%d %d/n",sizeof()e,strlen()e);#
输出结果6 9。sizeof(e)的结果是对的,strlen(e)的输出结果是错的。原因在于e不是字符串,没有’\0’作为结尾符,只是一个长度为6的字符数组。strlen函数没有找到字符串结尾符’\0’,所以输出的结果是不确定,错误的。
3.2 不使用sizeof,求变量所占的字节个数include#
defineCountNumberBytes ()value( char*)(&+value 1 )- ( char* )(&)valueint
main ()/* Write C code in this online editor and run it. */
{
int
= i 10 ;float
= f 0.0 ;double
= d 0.1 ;printf
("%d\n",CountNumberBytes()d);return
0 ;}
union
(char*)(&value + 1)获取(&value + 1)地址的首字节地址,(char *)(&value)获取value地址的首字节地址
4. 结构体struct和联合体unionstruct和union都是C语言中两种不同的数据结构。
4.1 sturct和union的区别union:
int Data
{
; ifloat
; fchar
[ str20];}
; datastruct
struct:
Person char{
[ name64];int
; age}
;结构体和联合体虽然都是由多个不同数据类型的成员组成,但不同之处在于联合体中所有的成员都共用同一块地址空间,而结构体中所有成员占用空间都是累加的,其所有成员都存放在不同的地址空间上。
结构体和联合体的区别在于:
- 对于联合体不同的成员赋值,将会对它的其他成员重写,原来的成员的值将不在,而对结构体的不同成员赋值是互不影响的。
- 计算联合体变量的字节大小时,由于所有成员不能同时占用内存空间,所以一个联合型变量的长度等于其最长的成员的长度,而结构体型变量其空间总长度是所有成员长度之和,还要考虑内存对齐产生的偏移量。
- 通信中数据包的发送与接收
- typedef
通信中的数据包会用到共用体:因为不知道对方会发一个什么包过来,用共用体的话就很简单了,定义几种格式的包,收到包之后就可以直接根据包的格式取出数据。
比如下位机采集到的数据类型是float型,需要通过串口发送到上位机然后再进行计算得到结果。由于通信协议的中每个字节都是uint型。比较笨的办法是把float型通过计算转换成整型再传输,但是这样以来精度受到了影响,每次要传输的内容增加,也增加了运算。
此时使用联合体可解决问题
union uint8_t{
[ numChar4];float
; numFloat}
;FLoatChar4Union分离高低字节,测试机器大小端字节序
float类型占用4个字节,numChar[4]也是4个字节,他两占用同一段内存,在最底层计算机存储的内容是一样的,此时可以直接传输numChar[4]
- union
char{
; chint
; i}
;unint
main ().
{
un=i 0x12345678 ;if
(.un==ch 0x12 )printf
("大端字节序\n");else
if (.un==ch 0x12 )printf
("小端端字节序\n");return
0 ;}
int
5. 左值、右值及短路计算
左值:指可以出现在等号左边的变量或者表达式,其最重要的特点是可写(可寻址)。也就是说它的值可以被修改,如果一个变量或者表达式的值不能被修改,那么它就不能作为左值。
右值:指可以出现在等号右边的变量或者表达式,其最重要的特点是可读。一般的使用场景都是把一个右值赋值给左值。
短路计算:
6. 前自增与后自增后置自增运算符会把原来的变量的值复制到一个临时的存储空间,等运算结束后才会返回这个临时变量的值。
a++的具体运算过程:
= temp ; a=
a + temp 1 ;return
; temp=
前置自增不需要用到临时的存储空间,其具体运算过程如下:
a + a 1 ;return
; a
所以,前置自增运算符会比后置自增的运算效率更高。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)