C++入门自学Day1-- C++基础

 一、C++简介

  • C++ 是在 C 语言的基础上发展起来的一种 支持面向对象编程 的语言。

  • 面向过程(Procedural Programming)

    1. 定义

    面向过程是一种以过程(Process)或函数(Function)为中心的编程方法。程序被看作是由一系列顺序执行的步骤(命令、语句)构成的,强调从上到下的控制流程

    2. 特点

  • 以函数为单位组织代码,关注的是“做什么、怎么做”;

  • 数据和函数是分开的;

  • 常见操作:调用函数、传递数据;

  • 比较适合小规模程序控制流程明确的问题

  • 常用结构:顺序、选择(if)、循环(for/while);

  • C++ 兼容 C 语言,同时引入了类、对象、继承、多态、模板等特性。

  • 应用广泛:系统软件、游戏引擎、图形渲染、嵌入式系统等。

以下是 C++入门 的基础知识总结,适合初学者快速掌握 C++ 的基本语法与概念:


 二、基本结构

一个最简单的 C++ 程序:

#include   // 头文件
using namespace std; // 使用标准命名空间

int main() {
    cout << "Hello, C++!" << endl;
    return 0; // 主函数返回值
}

三、变量与数据类型

基本数据类型:

类型

描述

示例

int

整型

int a = 10;

float

单精度浮点型

float f = 3.14;

double

双精度浮点型

double d = 3.14159;

char

字符

char c = ‘A’;

bool

布尔值

bool flag = true;

常量定义:

const int SIZE = 100;    // 常量
#define PI 3.14          // 宏常量

 变量的引用:

引用就是某个变量的别名,是对一个已存在变量的“另一个名字”。引用必须初始化,不能像指针那样先声明再赋值:

  1. 引用一旦绑定,就不能更换绑定对象

  2. 引用本身不占用内存(它不像指针存一个地址,它是变量的另一个名字)

int a  =0;
int& ra = a; //ra是a的引用,也就是别名,a再取了一个名称

int main(){
    int a = 10;
    int& ref = a;  // ref 是 a 的引用,即别名

    ref = 20;      // 实际修改的是 a
    cout << a;     // 输出 20
}

四、输入输出

int a = 10;
cin >> a;               // 从标准输入读取
cout << "a = " << a;    // 输出变量

输出为:

a   =   10;


五、运算符

算术运算符: 

        + - * / %

关系运算符: 

        ==  !=  >  <  >=  <= 

逻辑运算符: 

        &&  ||  !

赋值运算符: 

        =, += ,-=, *=, /=,


六、控制结构

条件语句:

if (a > 0) {
    cout << "Positive";
} else if (a < 0) {
    cout << "Negative";
} else {
    cout << "Zero";
}

switch 语句:

switch (a) {
    case 1:
        cout << "One";
        break;
    default:
        cout << "Other";
}

循环语句:

for (int i = 0; i < 10; i++) {
    cout << i;
}

while (a < 10) {
    a++;
}

do {
    a--;
} while (a > 0);

 七、函数定义与调用

        1、简单的函数定义调用

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

int main() {
    int result = add(3, 4);
}

       2、缺省参数 --》备胎参数

        当函数定义了参数,但是在调用函数时并没有参入实参,则函数会默认使用缺省参数

#include

//using namespace std //c++库中的所用东西都是放在std命名空间中的
// 展开常用库里面的一下对象。
using std::cout;
using std::endl;
void Print_Hello(int num = 1){
    for(int i = 0;i

输出结果如下:

hello world!
如果传入了参数如 num = 5

输出结果为:

hello world!

hello world!

hello world!

hello world!

hello world!

        1️⃣ 全缺省参数

          函数的参数全部为缺省参数:

//全缺省
void Func1(int a = 10,int b = 20,int c = 30){
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
}

int main(){
    Func1(); 
    Func1(1);
    Func1(1,2);
    Func1(1,2,3);
}

输出结果:

a = 10
b = 20
c = 30
a = 1
b = 20
c = 30
a = 1
b = 2
c = 30
a = 1
b = 2
c = 3

  2️⃣ 半缺省参数

        函数的参数部分为缺省参数必须从右往左缺省,而且必须连续缺省

//半缺省(缺省部分参数)
void Func2(int a,int b, int c = 30){
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
    cout << "c = " << c << endl;
}

int main(){

    Func2(1,2);
    Func2(1,2,3);
}

输出结果为:

a = 1
b = 2
c = 30
a = 1
b = 2
c = 3

3、 函数重载

        c++ 允许在同一作用域中声明几个功能相似的同名函数,要求函数名相同,参数不同(类型/个数/顺序不同);返回值没有要求。但是如果两个函数只有返回值不同,无法构成函数重载

int Add(int left,int right){
    return left+right;
}

double Add(double left,double right){
    return left+right;
}

long Add(long left,long right){
    return left+right;
}
int Add(int left,int mid,int right){
    return left+mid+right;
}


int main(){
    printf("%d\n",Add(10,20));
    printf("%d\n",Add(10,20));
    printf("%ld\n",Add(10L,20L));
    printf("%d\n",Add(10,30,20));
    return 0;
}

输出结果:

30
30
30
60

面试问题:有关于函数重载的问题

1、什么是函数重载?

2、c++如何支持函数重载,为什么c语言不支持呢?

因为 C++ 是“名字改编”(name mangling)支持语言

也就是说,编译器会根据函数名 + 参数类型,生成唯一的函数符号名(符号重整)

C++入门自学Day1-- C++基础_第1张图片

c语言和c++编译文件的区别:

项目

C

C++

文件后缀

.c

.cpp, .cc, .cxx

编译器调用

clang 或 gcc

clang++ 或 g++

是否支持函数重载

❌ 不支持

✅ 支持

生成符号名称

函数名保持原样

函数名被“修饰”(Mangled)

是否支持类、模板

❌ 不支持

✅ 支持

比如现在我有这样两个add函数分别用c++和c语言来实现:

c语言的add.c文件代码如下:

int add(int a, int b) {
    return a + b;
}
int main(){
    return 0;
}

然后你对add.c文件进行编译产生add.o的可执行文件

clang add.c -o add.o

然后查看编译后的函数符号(在命令行窗口使用nm进行查看.o):

nm add.o

输出如下
0000000100000000 T __mh_execute_header
0000000100000328 T _add 
0000000100000348 T _main

你会发现 编译后的文件并没有定义的add函数名做过的的修饰

c++的func.cpp文件 代码如下

int add(int a, int b) {
    return a + b;
}
int main(){
    return 0;
}

而你对fun.cpp文件进行编译产生fun.c的可执行文件

nm fun.o

输出如下:

0000000100000360 T __Z3addii
0000000100000000 T __mh_execute_header
0000000100000380 T _main

编译后的文件中,定义的add函数名被修饰了

如果此时你再定一个add的函数重载,在进行编译。

int add(int a, int b) {
    return a + b;
}
//函数重载
double add(double a, double b) {
    return a + b;
}
int main(){
    return 0;

再进行编译后;利用

nm fun.o

输出如下:

0000000100000380 T __Z3adddd
0000000100000360 T __Z3addii

0000000100000000 T __mh_execute_header
00000001000003a0 T _main

此时编译后你可以看到两个被修饰的add函数名

c++编译器如何实现?

通过 名称修饰(Name Mangling) 来区分函数:

函数声明

编译后符号名(伪示例)

int add(int, int)

__Z3addii

float add(float, float)

__Z3addff

而c语言不支持函数名称修饰,多个同名函数会导致链接冲突。

关键字 “Extern”的作用

当我需要在c语言项目中调用使用c++的静态或者动态库,如何实现呢?

例如我有一个函数声明如下:

void* tcmalloc(size_t n);

当我们在c++的头文件申明中:

利用 extern “c”  告诉 C++ 编译器不要对声明的函数做 name mangling(名字修饰),以便生成兼容 C 的符号名称。
// wrapper.hpp 或 tcmalloc.h(供 C 使用的头文件)
#ifdef __cplusplus
extern "C" {
#endif

void* tcmalloc(size_t n);  // 声明导出的 C 接口

#ifdef __cplusplus
}
#endif

extern "C"

告诉 C++ 编译器不要对函数名做 name mangling,生成 C 风格接口


八、数组和字符串

int arr[5] = {1, 2, 3, 4, 5};
string str = "Hello";
char name[] = "Tom";

九、引用和指针

int a = 10;
int* p = &a;     // 指针
int& ref = a;    // 引用

1. 变量的引用:

引用就是某个变量的别名,是对一个已存在变量的“另一个名字”。引用必须初始化,不能像指针那样先声明再赋值:

  1. 引用一旦绑定,就不能更换绑定对象

  2. 引用本身不占用内存(它不像指针存一个地址,它是变量的另一个名字)

int a  =0;
int& ra = a; //ra是a的引用,也就是别名,a再取了一个名称

int main(){
    int a = 10;
    int& ref = a;  // ref 是 a 的引用,即别名

    ref = 20;      // 实际修改的是 a
    cout << a;     // 输出 20
}

引用 vs 指针(重要对比)

        引用只是一个地址的别名,没有独立空间,和引用的实体共用同一快空间。

项目

引用(Reference)

指针(Pointer)

是否可以为 NULL

❌ 不可以

✅ 可以为 nullptr

是否可重新指向

❌ 不可以(固定)

✅ 可以指向别的变量

是否需要解引用

❌ 不需要,直接使用

✅ 需要用 *ptr 解引用

是否必须初始化

✅ 必须立即初始化

❌ 可以先定义再赋值

语法简洁性

✅ 更简洁

❌ 稍复杂

2、引用的用途

1. 用作函数参数(传引用)
避免值拷贝,提高效率,还能在函数中修改原始变量!
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}
 2. 用作函数返回值(返回引用)

可以让函数直接返回原变量的引用(⚠️但不能返回局部变量引用)

int& getElement(int arr[], int index) {
    return arr[index];
}

int main() {
    int nums[3] = {1, 2, 3};
    getElement(nums, 1) = 10;  // 修改数组第二个元素
}
3. 引用的隐式类型转换

        引用可以支持隐式类型转换,但有一些关键限制,尤其在函数参数传递中表现明显。

void foo(double& x) {
    x = x + 1;
}

int main() {
    int a = 5;
    // foo(a);  ❌ 错误:int 无法隐式转换为 double&,因为引用要求“精确匹配类型”
}

解决方案

void foo(const double& x) {
    cout << x << endl;
}

int main() {
    int a = 5;
    foo(a);   // ✅ OK:int 可隐式转换为 const double&
}
✅ 解释:
  • const T& 引用可以绑定到一个临时变量(由隐式转换生成的)。

  • 普通的 T& 引用不可以绑定到临时变量。

  • 理解:因为非 const 引用意味着你可以修改它指向的对象,而临时变量的生命周期很短,可能在语句结束就销毁,所以为了安全性,C++ 禁止非 const 引用绑定临时变量。

  • int main(){
        //权限的放大,不能把const给到非const
        const int a = 10;
        // int& b  = a; 错误,权限不能放大
        int b = 10;
        const int& c  = b; //权限可以缩小
        cout << "c = " << c;
    }

引用类型

是否允许绑定临时变量

是否支持隐式转换

T&

❌ 不允许

❌ 不支持

const T&

✅ 允许

✅ 支持

4、实用例子:重载函数与引用转换
void func(int& x) {
    cout << "int& version\n";
}

void func(const double& x) {
    cout << "const double& version\n";
}

int main() {
    int a = 10;
    func(a);     // 匹配 int&
    func(3.14);  // 匹配 const double&,从 double 到引用
}

3、函数返回“引用”

        1.什么是函数返回引用
函数返回的是某个变量的别名(引用),而不是它的值(拷贝)。
int a = 10;
int& getA() {
    return a;
}

getA() = 20;  // 直接修改了变量 a

✅ 这样做可以提高效率,也允许我们修改函数外部的对象。

        2. 为什么返回引用可能不安全?

         因为你可能返回了一个已经被销毁的对象引用!

危险场景 1:返回局部变量的引用
int& foo() {
    int x = 10;   // 局部变量,函数结束后内存释放
    return x;     // ❌ 返回无效引用
}

int main() {
    int& ref = foo();  // 未定义行为!
    cout << ref;       // 很可能是垃圾值或崩溃
}

原因:

  • x 是栈上的局部变量,函数执行完就被销毁。

  • 返回它的引用是 悬垂引用(Dangling Reference)

危险场景 2:返回临时变量的引用
int& bar() {
    return 10;    // ❌ 返回的是临时变量的引用
}

原因:

  • 字面量 10 会生成一个临时值,只在语句中有效。

  • 尝试返回它的引用是不合法的。

安全的用法:返回外部变量或静态变量的引用
int arr[5] = {1, 2, 3, 4, 5};

int& get(int index) {
    return arr[index];  // 合法:数组元素在全局或 main 中有效
}

get(2) = 10;   // 修改 arr[2] 的值

原因:

        arr是定义的全局变量,在全局域,main函数中都是有效的

返回静态变量引用
int& foo() {
    static int x = 10;
    return x;   // 安全:x 的生命周期是整个程序期间
}

原因:

        static修饰的变量,变量的生命周期变为整个程序运行期间。不会因为栈的销毁,变量被销毁,但是静态局部变量只在函数第一次调用时初始化一次,以后不会重新初始化。

int& Add2(int a,int b){
    static int c = a+b;
    return c;
}

int main(){
    Add2(3,4);
    int& ret = Add2(1,2);
    cout << "Add2(1,2) is :" <

这段代码输出结果是:


Add2(1,2) is :7

原因分析:

  • 第一次调用函数,static int c = a + b; 被执行,c = 7

  • 返回 c 的引用(没有使用结果)

  • 函数再次被调用,但注意:static int c = a + b; 不会执行,c 保持为第一次设定的值:7

  • 所以返回的是 c(值为 7)的引用,ret 绑定到这个值

        输出就是:Add2(1,2) is :7

如何修正呢?:

int& Add2(int a,int b){
    static int c;
    c = a+b;
    return c;
}

即可,这样c不会被初始化,但是c会一直执行 c = a + b;

3. 函数返回引用的安全性规则

        引用返回需要变量出了函数的作用域中仍然有效才能使用引用返回。引用可以减少临时变量的出现,提高效率。

场景

是否安全

原因

返回局部变量的引用

❌ 不安全

函数结束后变量消失

返回临时值的引用

❌ 不安全

临时变量生命周期太短

返回全局变量的引用

✅ 安全

全局变量生命周期贯穿程序始终

返回静态变量的引用

✅ 安全

静态变量不会在函数结束时销毁

返回容器/对象的成员引用(非局部)

✅ 安全

只要容器还存在就是安全的


十、命名空间(namespace)

命名空间中可以定义变量,也可以定义函数

 1. 使用作用域运算符 

namespace myns {
    int a = 5;
    int Add(int a,int b){
        return a+b;
    }
}


#include 
using namespace std; //c++库的所用东西都是放到std命名空间中

namespace MySpace {
    int a = 10;

    void sayHello() {
        cout << "Hello from MySpace!" << endl;
    }
}

int main() {
    MySpace::sayHello();    // 使用作用域限定符 ::
    cout << MySpace::a << endl;
    return 0;
}

2. 使用 using namespace

        这种方式会把命名空间里的所有成员导入到当前作用域。

using namespace MySpace;

int main() {
    sayHello();  // 不用写 MySpace::
}

3. 使用 using 声明单个成员

using MySpace::sayHello;

int main() {
    sayHello();       // ok
    // cout << a;     // 错误,因为没有导入 a
}


 十一、头文件与编译结构

  • .cpp 是源代码文件

  • .h 是头文件,通常包含函数声明、结构体定义等

  • 使用 #include 引入头文件


十二、常用标准库头文件

头文件

作用

输入输出

数学函数

随机数、内存操作等

时间处理

字符串类

你可能感兴趣的:(c++,开发语言,c,系统)