从零到一学习c++(基础篇--筑基期二-变量)

从零到一学习C++(基础篇) 作者:羡鱼肘子

 温馨提示1:本篇是记录我的学习经历,会有不少片面的认知,万分期待您的指正。

 温馨提示2:本篇会尽量避免一些术语,尽量用更加通俗的语言介绍c++的基础,但术语也是很重要的。

 温馨提示3:看本篇前可以先了解前篇的内容,知识体系会更加完整哦。

从零到一学习c++(基础篇--筑基期一)-CSDN博客

 初识变量

 变量就像“储物柜”

想象你有一个带标签的储物柜:

  • 柜子本身:能存放东西(比如数字、文字、真假值等)。

  • 标签名字:你给柜子贴个标签(比如叫 age),方便找到它。

  • 柜子类型:柜子专门放某类东西(比如“只能放整数”或“只能放小数”)。

在 C++ 中,变量就是程序里的“储物柜”,用来存数据,并且可以随时修改里面的内容。

(简单理解)变量就是给数据贴个名字、分个类,方便随时存、取、改的“储物柜” 🗄️! 

变量的关键特点

  1. 有名字:比如 scorename,方便你记住里面存了什么。

  2. 有类型:定义时就要告诉电脑这个柜子放什么类型的数据(比如整数、小数、文字)。

  3. 可以修改:存进去的数据之后能改(比如把 age 从 20 改成 21)。

举个栗子 :

// 声明一个叫 age 的“储物柜”,专门存整数(int)  
int age = 20;  

// 使用变量  
cout << "我今年 " << age << " 岁" << endl;  

// 修改柜子里的内容  
age = 21;  
cout << "明年我就 " << age << " 岁了!" << endl;  

输出:

我今年 20 岁  
明年我就 21 岁了!

变量的使用步骤 

  1. 声明:告诉电脑“我要一个放什么类型东西的柜子”

    • 格式:类型 名字;(比如 int score;)。

  2. 赋值:往柜子里放东西。

    • 可以直接声明时赋值:int score = 100;

    • 也可以后来再放:score = 100;

  3. 使用:用名字随时读取或修改数据。

// 存小数  
double price = 9.99;  
price = 8.5;  // 降价了!  

// 存一个字符  
char grade = 'A';  

// 存一句话(字符串)  
string message = "Hello World!";  

// 存真假值  
bool isRaining = true;  
isRaining = false;  // 雨停了!

要注意哦:

  1. 先声明,再使用:不能直接用没定义的柜子。

  2. 名字要合法:比如不能以数字开头(2name 是错的),区分大小写(Age 和 age 是不同变量)。

  3. 类型要对:不能往“整数柜”里塞小数(除非强制转换)。

变量的定义

 在 C++ 中,变量的定义是指为一个数据存储空间分配内存,并为其赋予名称和类型的过程。通俗来说,就是告诉计算机:

  1. 我要存什么类型的数据(比如整数、小数、字符等)。

  2. 这个数据叫什么名字(方便后续使用)。

  3. 初始值是什么(可选,但建议明确赋值)。

 变量定义的语法

数据类型 变量名;          // 只定义,不初始化(不推荐)
数据类型 变量名 = 初始值; // 定义并初始化(推荐)

 具体解释

  1. 数据类型
    决定变量能存储什么类型的数据(例如 intdoublechar)。

    int age;       // 定义一个整数类型的变量
    double price;  // 定义一个小数类型的变量
  2. 变量名
    为变量起一个有意义的名字(需符合命名规则)。

    int studentCount;   // 变量名通常用驼峰命名法或下划线分隔
    double total_price;
  3. 初始值(可选但推荐):
    在定义时直接赋值,避免未初始化的变量带来意外结果。

    int score = 100;          // 初始化为 100
    char grade = 'A';         // 初始化为字符 'A'
    bool isPassed = true;     // 初始化为布尔值 true

 举个栗子:

#include <iostream>
using namespace std;

int main() {
    // 定义变量并初始化
    int apples = 5;        // 存储苹果数量
    double weight = 3.75;  // 存储重量(千克)
    char symbol = '$';     // 存储货币符号

    // 使用变量
    cout << "我有 " << apples << " 个苹果,总重量 " << weight << "kg" << endl;
    cout << "单价:" << symbol << "10" << endl;

    // 修改变量的值
    apples = 10;          
    cout << "现在有 " << apples << " 个苹果!" << endl;

    return 0;
}

输出:

我有 5 个苹果,总重量 3.75kg  
单价:$10  
现在有 10 个苹果!

温馨小贴士: 

  1. 先定义,后使用

    int x = 10;  
    cout << x;   // ✅ 正确  
    cout << y;   // ❌ 错误:y 未定义  
  2. 避免未初始化

    int num;  
    cout << num; // ❌ 危险:num 的值不确定!
  3. 作用域限制

    {
        int temp = 42;  
        cout << temp; // ✅  
    }
    cout << temp; // ❌ temp 已超出作用域

小结

变量的定义是编程中的“声明存储空间”行为,相当于告诉计算机:

  • 类型:“我要存一个整数(int)。”

  • 名字:“这个空间叫 count。”

  • 初始值:“现在里面放数字 0。”

通过定义变量,程序才能安全、高效地操作数据。 🖥️

 变量定义和声明的关系

 变量的声明(Declaration) 

  • 作用:告诉编译器“存在这样一个变量”,包括它的类型和名字。

  • 语法:使用 extern 关键字(表示“这个变量在其他地方定义”)。

  • 特点

    • 不分配内存。

    • 可以多次声明同一个变量(但通常不建议)。

// 声明一个变量 age(告诉编译器它存在,但内存尚未分配)
extern int age;  

 变量的定义(Definition)

  • 作用:实际创建变量,分配内存,并可初始化。

  • 特点

    • 分配内存。

    • 只能定义一次(否则会引发“重复定义”错误)。

int age = 20;    // 定义变量 age(分配内存并初始化)

关键区别

声明(Declaration)定义(Definition)
内存分配❌ 不分配内存✅ 分配内存
允许次数可多次声明(需用 extern只能定义一次
初始化不能初始化(因为没内存)可以初始化
代码示例extern int age;int age = 20;

 实际应用场景(现在不理解没关系,后边我会讲到,目前有个印象就行)

场景 1:多文件编程
  • 头文件(.h:通过 extern 声明变量(告诉其他文件变量存在)。

  • 源文件(.cpp:定义变量(分配内存)。

// 在 header.h 中声明变量
extern int globalCount;  // 声明(不分配内存)

// 在 source.cpp 中定义变量
int globalCount = 0;     // 定义(分配内存并初始化)
场景 2:避免重复定义

如果多个文件直接定义同一个全局变量,会导致链接错误:

// file1.cpp
int globalVar = 42;  // ❌ 定义(分配内存)

// file2.cpp
int globalVar = 42;  // ❌ 重复定义!

正确做法:

// file1.cpp
int globalVar = 42;  // ✅ 定义(分配内存)

// file2.cpp
extern int globalVar; // ✅ 声明(告诉编译器变量在其他文件定义)

小结

  • 声明是“预告”,定义是“实现”。

  • 定义会分配内存,声明不会。

  • 在大型项目中,通过 extern 声明共享变量,通过定义确保唯一性。

一句话:声明是“我知道它存在”,定义是“我创造了它”。 🛠️

标识符 

是为变量、函数、类、结构体、命名空间等程序元素自定义的名称。它相当于代码中的“名字标签”,用于唯一标识某个实体 

标识符的核心规则

  1. 允许的字符

    • 字母(A-Z, a-z)、数字(0-9)、下划线(_)。

    • 首字符不能是数字(如 2name ❌,name2 ✅)。

    • 区分大小写(Age 和 age 是两个不同的标识符)。

  2. 禁止使用关键字

    • 不能用 C++ 的保留关键字(如 intclassreturn ❌)。

  3. 命名规范(非强制,但建议遵守):

    • 有意义:studentAge 比 sa 更清晰。

    • 驼峰命名法:calculateTotalPrice(函数名、变量名)。

    • 下划线分隔:MAX_SIZE(常用于常量)。

合法 vs 非法标识符的栗子

合法标识符非法标识符原因
age3rd_place以数字开头
_tempValueclass使用关键字 class
calculateAveragetotal-price包含非法字符 -
MAX_LENGTHuser name包含空格

温馨小贴士:

  1. 长度限制
    C++ 标准不限制标识符长度,但编译器可能有内部限制(一般足够长)。

  2. 作用域冲突
    同一作用域内不能重复定义同名标识符。

    int value = 10;
    int value = 20; // ❌ 错误:重复定义
  3. Unicode 支持(C++11 起):
    允许使用非英文字符(如中文、日文),但需谨慎(真的很不推荐)。

    int 年龄 = 20;  // ✅ 合法,但不推荐

小结

标识符就像代码里的“身份证”——名字要合法、清晰、唯一(在作用域内)! 🏷️ 

变量的作用域 

通俗来说,作用域就像一个个“房间”,房间内的东西只能在这个房间内被看到和使用,超出房间就无效了。

 全局作用域(Global Scope)

  • 范围:整个程序(所有文件)。

  • 定义位置:在所有函数和代码块之外。

  • 特点

    • 生命周期:从程序启动到结束。

    • 默认初始化为 0(未显式初始化时)。

    • 容易被滥用(可能导致命名冲突)。

#include <iostream>
int globalVar = 10;  // 全局变量(全局作用域)

int main() {
    std::cout << globalVar;  // ✅ 可以访问
    return 0;
}

局部作用域(Local Scope) 

  • 范围:函数内部或代码块(如 {} 内部)。

  • 定义位置:函数体内或代码块内。

  • 特点

    • 生命周期:进入作用域时创建,离开时销毁。

    • 未初始化时值为随机(垃圾值)。

void myFunction() {
    int localVar = 20;  // 局部变量(局部作用域)
    std::cout << localVar;  // ✅
}

int main() {
    myFunction();
    std::cout << localVar;  // ❌ 错误:localVar 在此不可见
    return 0;
}

 块作用域(Block Scope)

  • 范围:由 {} 包裹的代码块(如 ifforwhile)。

  • 定义位置:代码块内部。

  • 特点

    • 常用于临时变量(如循环计数器)。

    • 变量在代码块结束时销毁。

int main() {
    int a = 5;
    {
        int b = 10;  // 块作用域变量
        std::cout << a;  // ✅ 可以访问外层变量
        std::cout << b;  // ✅
    }
    std::cout << b;  // ❌ 错误:b 已销毁
    return 0;
}

 看看结果

命名空间作用域(Namespace Scope)

  • 范围:命名空间内部。

  • 定义位置:在 namespace 关键字定义的区域内。

  • 特点

    • 解决全局作用域的命名冲突。

    • 通过 命名空间::标识符 访问。

namespace MyLib {
    int value = 30;  // 命名空间作用域
}

int main() {
    std::cout << MyLib::value;  // ✅ 通过命名空间访问
    return 0;
}

类作用域(Class Scope)

  • 范围:类的内部(成员变量、成员函数)。

  • 定义位置:类定义的大括号 {} 内。

  • 特点

    • 成员变量和函数需要通过对象或类名访问。

    • 静态成员属于类作用域,但生命周期可能更长。

class MyClass {
public:
    static int staticVar;  // 类作用域(静态成员)
    int memberVar;         // 类作用域(成员变量)
};

int MyClass::staticVar = 40;

int main() {
    MyClass obj;
    obj.memberVar = 50;
    std::cout << MyClass::staticVar;  // ✅ 通过类名访问
    return 0;
}

作用域的嵌套与遮蔽(Shadowing)

  • 规则:内层作用域可以定义与外层同名的标识符,此时外层标识符被暂时“遮蔽”。

  • 小贴士:遮蔽可能导致代码可读性下降,一定要谨慎使用哦。

int x = 100;  // 全局变量

int main() {
    int x = 200;  // 局部变量遮蔽全局变量
    std::cout << x;      // 输出 200(访问局部变量)
    std::cout << ::x;    // 输出 100(通过 :: 访问全局变量)
    return 0;
}

小结

  • 作用域是变量的“有效区域”,超出区域则无法访问。

  • 合理使用作用域能写出更安全、清晰的代码。

  • 全局变量少用,局部变量多用,块变量按需使用! 🚪

复合类型 

是由基本类型(如 intdouble)或其他复合类型组合而成的数据类型,用于表示更复杂的数据结构。通俗来说,它们像是“容器”或“盒子”,可以将多个数据打包在一起,方便统一操作。目前先介绍两种引用和指针,其他的会在后边介绍。

 引用和指针

引用(Reference) 和 指针(Pointer) 都是用于间接操作数据的工具,但它们的设计目标、语法和用途有显著区别。

我认为对比学习一种比较好的学习方法,所以我把引用和指针这两个c++中比较难理解的概念对比学习

1. 基本概念

指针(Pointer)
  • 是什么:指针是一个变量,存储另一个变量的 内存地址

  • 特点

    • 需要显式解引用(用 *)访问数据。

    • 可以指向 nullptr(空指针)。

    • 支持重新指向其他地址。

  • 语法

    int num = 10;
    int* ptr = &num;  // ptr 存储 num 的地址
    *ptr = 20;        // 通过指针修改 num 的值为 20
引用(Reference)
  • 是什么:引用是变量的 别名,与原始变量共享同一内存地址。

  • 特点

    • 必须在定义时初始化,且不能重新绑定到其他变量。

    • 操作引用就像操作原变量(无需解引用)。

    • 不存在空引用。

  • 语法

    int num = 10;
    int& ref = num;   // ref 是 num 的别名
    ref = 20;         // 直接修改 num 的值为 20

2. 核心区别

特性指针(Pointer)引用(Reference)
初始化可以不初始化(但危险)必须初始化
空值支持 nullptr不能为空
重绑定可以指向其他地址绑定后不可更改
内存占用占用内存(存储地址)不额外占用内存(别名)
操作语法用 * 解引用,-> 访问成员直接操作,像普通变量
多级间接访问支持多级指针(如 int**不支持多级引用

3. 使用场景

(看不懂没关系的,目前可以跳过,将所有基础篇学完之后回来再看就会很清楚啦)

适合用指针的场景
  • 动态内存管理:如使用 new 和 delete 分配堆内存。

    int* arr = new int[10];
    delete[] arr;
  • 可选参数:允许函数参数为空。

    void print(int* ptr) {
        if (ptr != nullptr) cout << *ptr;
    }
  • 数据结构:如链表、树等需要灵活指向的场合。

    struct Node {
        int data;
        Node* next;  // 指向下一个节点
    };
适合用引用的场景
  • 函数参数传递:避免拷贝大型对象。

    void modifyValue(int& ref) {
        ref *= 2;  // 直接修改原变量
    }
  • 返回值优化:返回容器或大型对象的引用(如 std::vector<T>&)。

    std::string& getLongName() {
        static std::string name = "Alice";
        return name;
    }
  • 操作符重载:如 <<>> 的实现。

    ostream& operator<<(ostream& os, const MyClass& obj) {
        os << obj.data;
        return os;
    }

4. 代码示例对比

修改原变量
// 使用指针
void changeViaPointer(int* ptr) {
    *ptr = 100;
}

// 使用引用
void changeViaReference(int& ref) {
    ref = 200;
}

int main() {
    int a = 10;
    changeViaPointer(&a);   // 需传递地址
    changeViaReference(a);  // 直接传递变量
    cout << a;  // 输出 200
    return 0;
}
空值安全性(这是个重点呢)
int* ptr = nullptr;  // 合法,但解引用会崩溃
int& ref;            // ❌ 错误:引用必须初始化

一句话记忆

  • 指针是“可以指向任何地址的遥控器”。

  • 引用是“固定绑定到某个对象的快捷方式”。

合理选择引用和指针,能写出既高效又安全的代码! 🛠️

下一篇会学习有关const 限定符相关的内容 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愚戏师

多谢道友

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值