02_C++和C

02_C++和C

1.c和c++关系

在这里插入图片描述

2. 区别

2.1 小特性

整体来说C++兼容了C语言的语法,并且做了很多扩展

  1. C++支持在使用变量的时候再定义。
  2. C++支持访问register 地址,实际会优化为无效。
  3. C++不允许定义多个同名的全局变量
#include <stdio.h>

int g_v;
//int g_v;  //3

int main(int argc, char *argv[])
{
    printf("Begin...\n");
    
    int c = 0;//1
    
    for(int i=1; i<=3; i++)
    {
        for(int j=1; j<=3; j++)
        {
            c += i * j;
        }
    }
    
    printf("c = %d\n", c);
    
    register int a = 0;//2
    
    printf("&a = %p\n", &a);
    
    printf("End...\n");
    
    return 0;
}

2.2 struct 关键字 & 函数声明

struct关键字的加强方面

  1. C语言中的struct定义了 一组变量的集合其中的变量标识符并不是一种新的类型;
  2. C++ 中的struct 可以用于定义一个全新的类型不需要使用类似typedef的机制来说明这个新的类型;

函数声明

  1. C++中所有的表示符都必须显示声明类型;
  2. C语言中的默认类型在C++都是不合法的(缺省的intvoid…);
typedef struct  _student student;	              
struct _student															
{														
	const char *  name;									
	int age;									
};							
 struct student
{
	const char *  name;	
	int age;	
};
#include <stdio.h>
//C++认为是一个类型是可以实例化的,C不支持
struct Student
{
    const char* name;
    int age;
};

f(i)
{
    printf("i = %d\n", i);
}

g()//C中可以接受任何参数,返回值为默认的`int`类型,C++不可以
{
    return 5;
}


int main(int argc, char *argv[])
{
    Student s1 = {"Delphi", 30};
    Student s2 = {"Tang", 30};
    
    f(10);
    
    printf("g() = %d\n", g(1,2,3,4,5));
    
    return 0;
}

int f1() & int f2(void)区别?
使用不同的编译器(C不同。C++相同)

2.3 const

C语言中const

  1. const 修饰的变量是只读,本质上还是一个变量
  2. const 修饰的局部变量在栈上分配空间
  3. const 修饰的全局变量在只读存储区分配空间
  4. const 只在编译期间有效,运行期间无效
    总结:
    只是该变量具有只读属性,无法作为左值修改(但是可以指针修改),所以并不是真正的常量,并且具有存储空间。
    C定义常量:#define enum

C++ const
在C++中遇到const 时,在符号表中生成常量
编译器期间遇到使用const修饰的常量,直接使用之前符号表中的常量替换
编译器还是有可能为const修饰的常量分配空间的,例如使用extern 关键字,或者使用&符号。
但是不会使用期存储空间的值(不论修改与否,该常量还是保持之前的不变)目的是兼容C。

#include <stdio.h>
//GCC 
int main()
{
    const int c = 0;
    int* p = (int*)&c; //c++ 还是会分配空间
    
    printf("Begin...\n");
    
    *p = 5;   //c++ 此处和C一样都是5
    
    printf("c = %d\n", c);  //gcc 中 c = 5  g++中 c=0
    
    printf("End...\n");
    
    return 0;
}

类似效果和C中的define 差不多
但是又比宏高级 具备类型检查作用域检查,而宏是预编译期间文本替换

#include <stdio.h>
void f()
{
    #define a 3
    const int b = 4;
}

void g()    
{
    printf("a = %d\n", a);//编译器会预编译中展开为3  
    //printf("b = %d\n", b);  // 由于在f()中作用域,执行会报错
}

int main()
{
    const int A = 1;
    const int B = 2;
    int array[A + B] = {0};
    int i = 0;
    
    for(i=0; i<(A + B); i++)
    {
        printf("array[%d] = %d\n", i, array[i]);
    }
    
    f();
    g();
    
    return 0;
}

2.4 BOOL类型

C++中 bool类型只有true (编译器-1)和false (编译器-0)
占用 1byte空间,能够支持数学运算 ,运算结果只有truefalse,输出结果只有1和0
C中没有该类型,用int类型替代。

g++
#include <stdio.h>

int main(int argc, char *argv[])
{
    bool b = false;
    int a = b;
    
    printf("sizeof(b) = %d\n", sizeof(b));//1
    printf("b = %d, a = %d\n", b, a);/0,0
    
    b = 3;
    a = b;
    
    printf("b = %d, a = %d\n", b, a);//1,1
    
    b = -5;
    a = b;
    
    printf("b = %d, a = %d\n", b, a);//1,1
    
    a = 10;
    b = a;
    
    printf("a = %d, b = %d\n", a, b);//10,1
    
    a = 0;
    b = a;
    
    printf("a = %d, b = %d\n", a, b);//0,0
    
    return 0;
}

2.5 三目运算和 refrence &

C++相对于C进行了升级

int a = 1;
int b = 2;
(a<b?a:b) = 3;  c编译器不通过,c++中 a=3;b=2;但是必须是变量返回,不可以变量常量混搭、

引用 &
为一个已知的变量重新取一个别名

type namea =value;
type& nameb = namea; 

注意:类型名字必须一致。并且必须在声明的时候初始化,绑定一个类型一致的变量
根据内存模型,一个变量实际是物理内存上一个的sizeof(type)的空间 在程序中可见的名称我们进行了一个独一无二的区分ID 叫做namea ,同样我们还可以取另外一个ID->nameb来绑定这段内存,操作nameb其实和操作namea同样效果

#include <stdio.h>

int main(int argc, char *argv[])
{
    int a = 4;
    int& b = a;
    
    b = 5;
    
    printf("a = %d\n", a);  //5
    printf("b = %d\n", b); //5
    printf("&a = %p\n", &a); //0xbfe54aec
    printf("&b = %p\n", &b);//0xbfe54aec
    
    return 0;
}

C++对于三目运算的返回 有两种情况:

  1. 全部是变量,返回变量的引用
  2. 含有或全部为常量,都返回常量值。所以不可以再接赋值操作。

2.6 引用本质

数值交换

//引用版本
void swap(int& a,int& b)
{
	int t = a;
	a = b;
	b = t;
}
//指针版本
void swap(int *a,int*b)
{
	int t = *a;
	*a = *b;
	*b = t;
}

引用在传参的时候进行初始化。
const 引用:
作用:让变量具有只读属性

#include <stdio.h>

void Example()
{
    printf("Example:\n");
    
    int a = 4;
    const int& b = a;
    int* p = (int*)&b;  //p= &a
    
    //b = 5;//error b是只读变量 
    
    *p = 5;//ok 
    
    printf("a = %d\n", a);
    printf("b = %d\n", b);
}
//c++ 使用常量对const 引用初始化时候,c++ 会为常量值分配存储空间,并量引用名称作为这段空间别名。
void Demo()
{
    printf("Demo:\n");
    
    const int& c = 1;  //ok
    int* p = (int*)&c;
    
    //c = 5;//error
    
    *p = 5;//ok
    
    printf("c = %d\n", c);
}

int main(int argc, char *argv[])
{
    Example();
    
    printf("\n");
    
    Demo();

    return 0;
}

引用是否有自己的存储空间?

#include <stdio.h>

struct TRef
{
    char& r;
};

int main(int argc, char *argv[])
{ 
    char c = 'c';
    char& rc = c;
    TRef ref = { c };
    
    printf("sizeof(char&) = %d\n", sizeof(char&));  //1
    printf("sizeof(rc) = %d\n", sizeof(rc)); //1
    
    printf("sizeof(TRef) = %d\n", sizeof(TRef));//4
    printf("sizeof(ref.r) = %d\n", sizeof(ref.r));//1

    return 0;
}

从上可知,引用本质上占用4byte 和指针一致。

  1. C++ 编译器在编译过程中,用指针常量作为引用内部的实现方式,因此占用相同空间;
  2. 从使用的角度来说,引用只是一个别名,隐藏了指针操作的具体细节;
#include <stdio.h>

struct TRef
{
   char* before;
   char& ref;
   char* after;
};

int main(int argc, char* argv[])
{
   char a = 'a';
   char& b = a;
   char c = 'c';

   TRef r = {&a, b, &c};

   printf("sizeof(r) = %d\n", sizeof(r));//12
   printf("sizeof(r.before) = %d\n", sizeof(r.before));//4
   printf("sizeof(r.after) = %d\n", sizeof(r.after));//4
   printf("&r.before = %p\n", &r.before);//0xbf8a300c+4 = 0xbf8a3010
   printf("&r.after = %p\n", &r.after);//0xbf8a3014

   return 0;
}

使用引用替代指针操作根据安全性和易用性。注意:不能返回局部变量的引用

#include <stdio.h>

int& demo()
{
    int d = 0;
    
    printf("demo: d = %d\n", d);
    
    return d;   //warnning
}

int& func()
{
    static int s = 0;
    
    printf("func: s = %d\n", s);
    
    return s;
}

int main(int argc, char* argv[])
{
    int& rd = demo();
    int& rs = func();
    
    printf("\n");
    printf("main: rd = %d\n", rd); //wrong
    printf("main: rs = %d\n", rs);
    printf("\n");
    
    rd = 10;  //wrong 不能修改
    rs = 11;
    
    demo();
    func();
    
    printf("\n");
    printf("main: rd = %d\n", rd);
    printf("main: rs = %d\n", rs);
    printf("\n");
    
    return 0;
}

2.7 inline

C语言中常使用#define来定义常量数字,或者宏代码块,在预编译期间会替换相对应的文本#define a 5
C++ 中对于常量的使用除了兼容#define以外还有const ,例如 const int a = 5
相比于#define没有类型检查,仅仅是替换文本,使用的时候必须谨慎,而const 常量可以替换宏常数;
C++ 对于宏代码块,使用内联函数:

inline int function(int a,int b)
{
	return a>b? a:b;
}
  1. C++ 编译器将把用inline声明的函数体插入到函数调用的地方;
  2. 内联函数没有普通变量调用时的额外开销,并且具有函数的检查特性(编译阶段的参数检查以及返回检查)
  3. C++ 编译器不一定满足内联函数的请求,编译器可以拒绝请求(inline 只是声明我们请求把整个函数作为内联,编译器不一定实现);
  4. 效率基本和`#define 差不多并且更加安全
#include <stdio.h>

#define FUNC(a, b) ((a) < (b) ? (a) : (b))

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

int main(int argc, char *argv[])
{
    int a = 1;
    int b = 3;
    int c = FUNC(++a, b);//int c = ((++a) < (b) ? (++a) : (b))  ----> 2<3? 3 目的是2? 
    int c = func(++a, b); 
   
    printf("a = %d\n", a);//define  3  inline2 
    printf("b = %d\n", b);//define  3  inline3
    printf("c = %d\n", c);//define  3  inline2 
    
    return 0;
}

以上可知使用#define 定义红代码块的时候存在着副作用。然使用inline 不会。
一般根据编译器的优化选项来确认是否使用inline 具体可以反汇编查看编译后的结果,现在编译器 也有可能会因为优化抉择把函数优化为内联函数 。当然也可以使用编译选项,强制声明为内联函数:
//__forceinline //MSVC
//__attribute__((always_inline)) //G++

#include <stdio.h>

//__forceinline 	//MSVC
//__attribute__((always_inline))  //G++
inline 
int add_inline(int n);

int main(int argc, char *argv[])
{
    int r = add_inline(10);

    printf(" r = %d\n", r);

    return 0;
}

inline int add_inline(int n)
{
    int ret = 0;

    for(int i=0; i<n; i++)
    {
        ret += i;
    }

    return ret;
}

inline函数被编译器内联限制:

  1. 不能存在任何循环语句
  2. 不能存在过多的条件判断
  3. 函数体不能过于庞大
  4. 不能对函数进行 & 操作
  5. 必须在调用前声明
    以上也不是一定,根据编译器的决定,具体汇编代码分析

2.8 C++ 函数参数默认值

如果函数在调用的的时候,没有传入新的参数的,会默认使用函数声明时候的默认值(默认形参构造),若有新的参数 会优先使用新的参数;

#include <stdio.h>

int mul(int x = 0);

int main(int argc, char *argv[])
{
    printf("%d\n", mul());  //0
    printf("%d\n", mul(-1));//1
    printf("%d\n", mul(2));//4
    
    return 0;
}

int mul(int x)
{
    return x * x;
}

对于多个默认参数的函数。必须使用从右到左依次声明默认参数初始化;
例如int add(int x, int y = 1, int z = 2);是正确的;
int add(int x, int y = 1, int z );此时不对;

#include <stdio.h>

int add(int x, int y = 0, int z = 0);

int main(int argc, char *argv[])
{
    printf("%d\n", add(1));     // x = 0;y = 0;z= 0;
    printf("%d\n", add(1, 2)); // x = 1;y = 2;z= 0;
    printf("%d\n", add(1, 2, 3)); // x = 1;y = 2;z= 3;
    
    return 0;
}

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

如上,传入参数的若有新的参数,更新参数,若无的话,使用默认参数。
可以使用默认参数结合占位操作结合,兼容c语言的一些语法

#include <stdio.h>

int func(int x, int = 0);

int main(int argc, char *argv[])
{
    printf("%d\n", func(1));
    printf("%d\n", func(2, 3));
    
    return 0;
}

int func(int x, int)
{
    return x;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值