02-Extern、内联函数和Const

《C++文章汇总》
上一篇文章介绍了《01-Cin、Cout、函数重载和默认参数》,本文介绍Extern、内联函数Inline和Const。

1.被extern "C"修饰的代码会按照C语言的方式去编译

两种写法,一种是加载头部,另一种是用大括号包住
I.加在头部extern "C"

#include 
using namespace std;
extern "C" void func(){
    
}
extern "C" void func(int v1){
    
}
int main(int argc, const char * argv[]) {
    getchar();
    return 0;
}
//报错,不允许重载,C语言不支持重载

II.大括号包住

extern "C" {
    void func(){
    
    }
    void func(int v1){
    
    }
}

2.如果函数同时有声明和实现,要让函数声明被extern "C"修饰,函数实现可以不修饰,声明和实现同时修饰也不会报错

extern "C" void func();
extern "C" void func0(int v1);
int main(int argc, const char * argv[]) {
    getchar();
    return 0;
}
extern "C" void func(){

}
extern "C" void func0(int v1){
 
}

extern "C"也可以用来包住声明头部

extern "C" {
    void func();
    void func0(int v1);
}
int main(int argc, const char * argv[]) {
    getchar();
    return 0;
}
extern "C" void func(){

}
extern "C" void func0(int v1){
 
}

由于C、C++编译规则的不同,在C、C++混合开发时,可能会经常出现以下操作
C++在调用C语言API时,需要使用extern "C"修饰C语言的函数声明

#include "main.h"

int sum(int v1,int v2){
    return v1 + v2;
}
int sub(int v1,int v2){
    return v1 - v2;
}

extern "C" {
int sum(int v1,int v2);
int sub(int v1,int v2);
}
int main(int argc, const char * argv[]) {
    
    cout << sum(10, 20) << endl;
    cout << sub(30, 20) << endl;
    
    getchar();
    return 0;
}

新建头文件如main.h存放函数的声明,在要调用的类中引用

extern "C" {
     #include "main.h"
}
int main(int argc, const char * argv[]) {
    
    cout << sum(10, 20) << endl;
    cout << sub(30, 20) << endl;
    cout << divide(30, 2) << endl;
    
    getchar();
    return 0;
}

也可直接在头文件中指定,C++文件中默认定义了宏#define __plusplus在文件的第一句,在C文件中通过宏定义环境判断

#ifdef __cplusplus
extern "C" {
#endif
int sum(int v1,int v2);
int sub(int v1,int v2);
int divide(int v1,int v2);
#ifdef __cplusplus
}
#include "main.h"
int main(void) {
    cout << sum(10, 20) << endl;
    cout << sub(30, 20) << endl;
    cout << divide(30, 2) << endl;
    getchar();
    return 0;
}

C文件调用C文件,C文件中也可以用宏定义判断

#include "main.h"
extern "C" {
#include "other.h"
}
int main(void) {
    
    other();
    
    cout << sum(10, 20) << endl;
    cout << sub(30, 20) << endl;
    cout << divide(30, 2) << endl;
    
    getchar();
    return 0;
}
//输出
other - 30
30
10
15

3.防止重复引入头文件,重复引入也没用了,后面的代码也不会执行了,因为#ifndef other_h表示没有定义,才会定义往下执行,第二次头文件引入时,已经定义了宏other_h,不会再往下执行,也可以大写,前面加两个__,比如__OTHER_H,保证宏名不一样,这样多个头文件引入的时候不会相互影响

#ifndef other_h
#define other_h

#include 
void other();
#endif /* other_h */

#ifndef main_h
#define main_h
#include 
#ifdef __cplusplus
extern "C" {
#endif
int sum(int v1,int v2);
int sub(int v1,int v2);
int divide(int v1,int v2);
#ifdef __cplusplus
}
#endif
#endif

在新文件中能同事包含
#include "main.h"
#include "other.h"

4.我们经常使用#ifndef、#define、#endif来防止头文件的内容被重复包含,#pragma once可以防止整个文件的内容被重复包含

pragram once也可以防止重复包含,引入了很多次头文件保证只引入了一次

区别

I.ifndef、#define、#endif受C\C++标准的支持,不受编译器的任何限制
II.有些编译器不支持#pragma once(较老编译器不支持,如GCC 3.4版本之前),兼容性不够好
III.#ifndef、#define、#endif可以针对一个文件中的部分代码,而#pragma once只能针对整个文件

5.内联函数

  • 使用inline修饰函数的声明或者实现,可以使其变成内联函数
  • 建议声明和实现都增加inline修饰

特点:编译器会将函数调用直接展开为函数体代码,可以减少函数调用的开销,会增大代码体积

  • 什么时候使用内联函数?
    1.函数代码体积不大
    2.频繁调用的函数
  • 递归的调用添加Inline意义不大,编译器会识别,内联不起作用,还是存在函数调用
  • 注意
    尽量不要内联超过10行代码的函数 有些函数即使声明为inline,也不一定会被编译器内联,比如递归函数
#include 
using namespace std;
//开辟栈空间
void func(){
    cout << "void func1()" << endl;
    cout << "void func2()" << endl;
    cout << "void func3()" << endl;
}//回收栈空间
int sum(int v1,int v2){
    return v1+v2;
}
inline void func0(){
    cout << "void func1()" << endl;
    cout << "void func2()" << endl;
    cout << "void func3()" << endl;
}
int sum0(int v1,int v2){
    return v1+v2;
}
int main(int argc, const char * argv[]) {
    func0();
    cout << sum0(10, 20) << endl;
    func();
    cout << sum(10, 20) << endl;
    return 0;
}
//输出
void func1()
void func2()
void func3()
30
void func1()
void func2()
void func3()
30

6.内联函数与宏

内联函数和宏,都可以减少函数调用的开销
对比宏,内联函数多了语法检测和函数特性

  • 思考以下代码的区别
  • define sum(x) (x + x)

  • inline int sum(int x) { return x + x; }
  • int a = 10; sum(a++);

7.C++的有些表达式是可以被赋值的

int a = 1;
int b = 2;
//赋值给了a
(a = b) = 3;
//赋值给了b
(a < b ? a : b) = 4;

8.Const

◼ const是常量的意思,被其修饰的变量不可修改 如果修饰的是类、结构体(的指针),其成员也不可以更改

#include 
using namespace std;

struct Date {
    int year;
    int month;
    int day;
};

int main(int argc, const char * argv[]) {
    /*
    const Date d = {2011,2,5};
    Date d2 = {2013,4,8};
    d = d2;
    d.year = 2018;
     */
    Date d1 = {2011,2,5};
    Date d2 = {2013,4,8};
    const Date *p = &d1;
    p->year = 2019;
    (*p).month = 5;
    *p = d2;
    cout << d1.year << endl;
    return 0;
}
//会报错,通过指针也无法修改了

◼ 以下5个指针分别是什么含义?


image
    int age = 10;
    int height = 30;
    //p1不是常量,*p1是常量
    const int *p1 = &age;
    //p2不是常量,*p2是常量
    int const *p2 = &age;
    //p3是常量,*p3不是常量
    int * const p3 = &age;
    //p4是常量,*p4也是常量
    const int * const p4 = &age;
    //p5是常量,*p5也是常量
    int const * const p5 = &age;
    
    *p4 = 20;//age = 20;
    p4 = &height;
    *p4 = 40;//height = 40;

◼ 上面的指针问题可以用以下结论来解决:
const修饰的是其右边的内容


image

pStu1->age = 30也是想修改指针变量pStu1指向的内容,但指针变量*pStu1为常量,不能被修改,会报错

你可能感兴趣的:(02-Extern、内联函数和Const)