Error[8]: Undefined offset: 521, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

C/C++关键词必知必会(二) 1.const

const 是 constant 的缩写,本意是不变的,不易改变的意思。在 C++ 中是用来修饰普通类型变量,指针变量,函数参数、函数返回值。

1.1 const修饰普通变量
const int value = 10;
int b = value;//正确
value = 0;//错误

value变量此时被const修饰为常量,可以将value的数值赋值给其他变量,但不可以修改value的内容。若修改了,编译器会报错,因为对一个常量进行修改 *** 作是违法的。

1.2 const修饰指针变量

const 修饰指针变量有以下三种情况:

  1. cosnt修饰指针指向的内容
  2. const修饰指针
  3. 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修饰函数参数有三种:

  1. const修饰普通类型变量进行值传递
  2. const修饰指针进行参数传递
  3. 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 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的区别
  1. new/delete是C++中的 *** 作符,而malloc/free是标准库函数
  2. new在申请内存时会自动计算所需字节数,而malloc需要我们自己手动输入申请内存空间的字节数
  3. new/new[]完成了两件事,先是底层调用malloc申请分配内存空间,然后调用构造函数创建对象
  4. delete/delete[]完成了两件事,先是调用析构函数清理资源,然后底层调用free释放内存空间
3. strlen()和sizeof() 3.1 strlen()和sizeof的辨析
  1. strlen()是库函数,而sizeof是运算符
  2. 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和联合体union

struct和union都是C语言中两种不同的数据结构。

4.1 sturct和union的区别

union:

int Data
{
   ; ifloat
   ; fchar
   [  str20];}
; datastruct 

struct:

Person char{
	[ name64];int
	; age}
;
  • 结构体和联合体虽然都是由多个不同数据类型的成员组成,但不同之处在于联合体中所有的成员都共用同一块地址空间,而结构体中所有成员占用空间都是累加的,其所有成员都存放在不同的地址空间上。
  • 结构体和联合体的区别在于:

    4.2 联合体在通信有应用场景

    通信中的数据包会用到共用体:因为不知道对方会发一个什么包过来,用共用体的话就很简单了,定义几种格式的包,收到包之后就可以直接根据包的格式取出数据。

    比如下位机采集到的数据类型是float型,需要通过串口发送到上位机然后再进行计算得到结果。由于通信协议的中每个字节都是uint型。比较笨的办法是把float型通过计算转换成整型再传输,但是这样以来精度受到了影响,每次要传输的内容增加,也增加了运算。

    此时使用联合体可解决问题

    union uint8_t{
    	[ numChar4];float
    	; numFloat}
    ;FLoatChar4Union
  • 分离高低字节,测试机器大小端字节序
  • float类型占用4个字节,numChar[4]也是4个字节,他两占用同一段内存,在最底层计算机存储的内容是一样的,此时可以直接传输numChar[4]

    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[+++]
    

    所以,前置自增运算符会比后置自增的运算效率更高。

    )
    File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
    File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 165, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
    File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
    CC++关键词必知必会(二)_C_内存溢出

    CC++关键词必知必会(二)

    CC++关键词必知必会(二),第1张

    C/C++关键词必知必会(二) 1.const

    const 是 constant 的缩写,本意是不变的,不易改变的意思。在 C++ 中是用来修饰普通类型变量,指针变量,函数参数、函数返回值。

    1.1 const修饰普通变量
    const int value = 10;
    int b = value;//正确
    value = 0;//错误
    

    value变量此时被const修饰为常量,可以将value的数值赋值给其他变量,但不可以修改value的内容。若修改了,编译器会报错,因为对一个常量进行修改 *** 作是违法的。

    1.2 const修饰指针变量

    const 修饰指针变量有以下三种情况:

    1. cosnt修饰指针指向的内容
    2. const修饰指针
    3. 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修饰函数参数有三种:

    1. const修饰普通类型变量进行值传递
    2. const修饰指针进行参数传递
    3. 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的区别
    1. new/delete是C++中的 *** 作符,而malloc/free是标准库函数
    2. new在申请内存时会自动计算所需字节数,而malloc需要我们自己手动输入申请内存空间的字节数
    3. new/new[]完成了两件事,先是底层调用malloc申请分配内存空间,然后调用构造函数创建对象
    4. delete/delete[]完成了两件事,先是调用析构函数清理资源,然后底层调用free释放内存空间
    3. strlen()和sizeof() 3.1 strlen()和sizeof的辨析
    1. strlen()是库函数,而sizeof是运算符
    2. 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和联合体union

    struct和union都是C语言中两种不同的数据结构。

    4.1 sturct和union的区别

    union:

    int Data
    {
       ; ifloat
       ; fchar
       [  str20];}
    ; datastruct 
    

    struct:

    Person char{
    	[ name64];int
    	; age}
    ;
  • 结构体和联合体虽然都是由多个不同数据类型的成员组成,但不同之处在于联合体中所有的成员都共用同一块地址空间,而结构体中所有成员占用空间都是累加的,其所有成员都存放在不同的地址空间上。
  • 结构体和联合体的区别在于:

    • 对于联合体不同的成员赋值,将会对它的其他成员重写,原来的成员的值将不在,而对结构体的不同成员赋值是互不影响的。
    • 计算联合体变量的字节大小时,由于所有成员不能同时占用内存空间,所以一个联合型变量的长度等于其最长的成员的长度,而结构体型变量其空间总长度是所有成员长度之和,还要考虑内存对齐产生的偏移量。
    • 通信中数据包的发送与接收
    4.2 联合体在通信有应用场景
      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
    

    所以,前置自增运算符会比后置自增的运算效率更高。

    欢迎分享,转载请注明来源:内存溢出

    原文地址: http://outofmemory.cn/langs/707103.html

    (0)
    打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
    上一篇 2022-04-24
    下一篇 2022-04-24

    发表评论

    登录后才能评论

    评论列表(0条)

    保存