当前位置: 首页 > article >正文

嵌入式学习笔记-C语言知识点:栈的作用,C语言函数参数的入栈顺序,C++ 拷贝构造函数,数组名和指针的区别与联系,指针运算,指针和引用

文章目录

    • 栈的作用
      • **1. 存储临时变量**
      • **2. 多线程编程中的作用**
    • C语言函数参数的入栈顺序
      • **为什么采用自右向左的入栈顺序?**
    • **C++ 拷贝构造函数**
      • **1. 什么是拷贝构造函数?**
      • **2. 默认拷贝构造函数**
      • **3. 拷贝构造函数的错误示例**
        • **错误原因**
        • **正确做法**
    • **C++ 中拷贝赋值函数是否可以使用值传递?**
      • **正确写法(使用 `const &`)**
    • **数组名和指针的区别与联系**
      • **1. 数据存储方式**
      • **2. 数据访问方式**
      • **3. 使用场景**
    • **指针运算**
      • **1. `p + n`(指针加法)**
      • **2. `p - n`(指针减法)**
      • **3. `p1 - p2`(指针相减)**
    • **指针和引用的异同**
      • **相同点**
      • **不同点**
      • **指针和引用的转换**

栈的作用

1. 存储临时变量

在C语言中,栈用于存储临时变量,包括:

  • 函数参数
  • 函数内部定义的局部变量
  • 函数返回地址
  • 寄存器的备份(用于函数调用时的上下文切换)

当一个函数被调用时,函数的返回地址、参数和局部变量都会被压入栈中,函数返回时,这些数据会被从栈中弹出,从而恢复函数调用前的状态。

2. 多线程编程中的作用

栈是多线程编程的基础,每个线程都有自己的专属栈,用于存储该线程执行过程中各个函数的局部变量,同时维护函数调用关系。

操作系统的多线程管理依赖栈,包括:

  • 每个线程有独立的栈
  • 中断和异常处理机制也使用栈

栈的独立性保证了线程间的局部变量不会相互干扰。


C语言函数参数的入栈顺序

在C语言中,函数参数的入栈顺序通常是自右向左

示例:

void f(int a, int b, int c);
f(1, 2, 3);

入栈顺序: 3 → 2 → 1

为什么采用自右向左的入栈顺序?

  1. 支持可变参数函数(如 printf

    • 变长参数位于参数列表的最右侧,固定参数在栈底部。
    • 这样可变参数的起始位置可以动态确定。
  2. 提高寄存器参数优化的灵活性

    • 现代编译器优化时,可以优先将前面的参数放入寄存器,提高效率。

C++ 拷贝构造函数

1. 什么是拷贝构造函数?

在 C++ 中,拷贝构造函数是用于创建新对象并初始化为已有对象的副本的特殊构造函数。

语法:

class Example {
public:
    Example(const Example &obj); // 拷贝构造函数
};

2. 默认拷贝构造函数

如果未显式定义拷贝构造函数,C++ 会自动生成一个默认拷贝构造函数。

示例:

class Example {
public:
    int value;
    Example(int val) : value(val) {} // 普通构造函数
    Example(const Example &ex) = default; // 默认拷贝构造
};

3. 拷贝构造函数的错误示例

错误代码:

class Example {
public:
    Example(int a) : value(a) {}
    Example(Example &ex) { value = ex.value; } // 错误:缺少 `const`

private:
    int value;
};
错误原因
  • Example(Example &ex) 缺少 const,导致递归调用
  • Example e2 = e1; 触发拷贝构造,但 e1 需要传递给 ex,这又会调用拷贝构造,形成无限递归,最终导致栈溢出
正确做法
class Example {
public:
    Example(int a) : value(a) {}
    Example(const Example &ex) { value = ex.value; } // 正确

private:
    int value;
};

C++ 中拷贝赋值函数是否可以使用值传递?

不可以!

原因:

  • 如果 operator= 的参数是 值传递,那么在调用时会触发拷贝构造函数
  • 这会导致无限递归,最终导致栈溢出

示例(错误代码):

class Example {
public:
    Example &operator=(Example obj) { // ❌ 错误:值传递
        value = obj.value;
        return *this;
    }
private:
    int value;
};

正确写法(使用 const &

class Example {
public:
    Example &operator=(const Example &obj) { // ✅ 正确
        value = obj.value;
        return *this;
    }
private:
    int value;
};

数组名和指针的区别与联系

1. 数据存储方式

  • 指针: 存储的是地址,本身占 4/8 字节(具体取决于系统)。
  • 数组: 存储的是具体的数据,数组名表示第一个元素的地址。

2. 数据访问方式

  • 指针: 需要使用 * 进行解引用,如 *ptr
  • 数组: 直接使用下标访问,如 arr[0]

3. 使用场景

  • 指针: 适用于动态内存管理(如 malloc/free)。
  • 数组: 适用于存储固定大小的数据集合。

指针运算

1. p + n(指针加法)

指针向前移动 n 个元素大小。

struct Data { int a; double b; };
Data arr[10];
Data *ptr = arr;
ptr = ptr + 1; // 移动 sizeof(Data) 个字节

2. p - n(指针减法)

指针向后移动 n 个元素大小。

3. p1 - p2(指针相减)

返回两个指针之间相差的元素个数

int arr[5];
int *p1 = &arr[4];
int *p2 = &arr[0];
int diff = p1 - p2; // diff = 4

指针和引用的异同

相同点

  1. 都表示某个变量的地址
  2. 都可以用于参数传递,避免拷贝开销

不同点

指针引用
是否可更改指向可更改不可更改
是否允许 NULL可以不允许
语法int *p = &a;int &ref = a;

指针和引用的转换

指针转引用:

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

引用转指针:

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


http://www.kler.cn/a/589708.html

相关文章:

  • Apache Spark_解决生产环境数据倾斜问题方案及思路
  • Tomcat新手入门指南:从零开始安装与基本配置
  • Python驱动CATIA自动化建模:科赫雪花算法实现与工程应用
  • 《解锁华为黑科技:MindSpore+鸿蒙深度集成奥秘》
  • 咪咕MG101_晨星MSO9380芯片_安卓5.1.1_免拆卡刷固件包
  • Bash语言的手动测试
  • Keepalived 多主模型与 LVS 高可用
  • 基于System V的共享内存函数使用指南
  • Flume详解——介绍、部署与使用
  • 2025深圳国际数字能源展全球招商启动,9月18日盛大开启
  • XGPT x DeepSeek:微步AI安全助手满血升级
  • 蓝桥杯备考-----》差分数组+二分答案 借教室
  • python局部变量和全局变量
  • nodejs 使用 puppeteer 打印PDF 有元素没有打印出来
  • 什么是广播系统语言传输指数 STIPA
  • 【云原生技术】容器技术的发展史
  • 市场监管总局升级12315平台 专项整治四大市场顽疾保障消费安全
  • Leetcode——151.反转字符串中的单词
  • 【Linux】Ext系列文件系统(上)
  • 机器人触觉的意义