c++学习笔记(1)-符号重定义

news/2024/11/30 20:29:57/

1、引言

符号重定义(Symbol Redefinition)指的是在同一个作用域内多次定义同名标识符(包括变量、函数、类等)。符号重定义错误通常包括预处理期符号重定义,编译期符号重定义,链接期符号重定义,以及运行期符号重定义。

2、符号重定义场景

2.1、同名宏

如果在多个地方重复定义同一个宏,则会在预处理阶段导致符号重定义错误。

//file1.h
#define MAX 100//file2.h
#define MAX 50//main.c
#include "file1.h"
#include "file2.h"
int main() {return 0;
}

在这个例子中,当编译器编译 main.c 时,会发现 MAX 宏在两个头文件中都被定义了,从而导致符号重定义错误。

2.2、名称冲突

同一作用域或不同作用域内出现了相同的符号名定义,会造成编译期错误。下面是一个C++示例代码,演示了编译期符号重定义错误:

#include <iostream>
int value = 3;
int value = 5;// 错误:value重定义int main()
{std::cout << value << std::endl; return 0;
}

在上述代码中,我们在同一作用域中定义了两个名为value的变量,这会导致编译器无法确定应该取哪个变量的值,所以会在编译期间报错,提示符号重定义的问题。

2.3、头文件多重包含

头文件多重包含的问题是这样产生的:当一个源文件包含多个头文件时,某个头文件可能会包含一个已经包含过的头文件,导致同一个函数或变量的定义被重复包含。下面是一个示例,展示了多重包含相同的头文件引起重定义的错误:

//add.h文件
int add(int a,int b){return a+b;
}//printAdd.h文件
#include<iostream>
#include"add.h>
void printAdd(int a,int b){std::cout<<add(a,b)<<std::endl;
}//main.cpp文件
#include"printAdd.h"
#include"add.h"  //符号重定义
int main(){printAdd(1,2);
}

上述代码中,add.h文件在main.cpp内被2次包含,这就导致add()函数发生两次定义,所以会在编译期间报错,提示符号重定义的问题。

2.4、链接错误

链接重定义错误是一种常见的链接错误,通常是由于多个编译单元中出现了相同的符号定义而导致的。这种错误会在链接时被检测到,表示无法解析符号引用,因为有多个定义存在。

//a.h
int getValue();
//a.cpp
int getValue(){return 0;};//b.h
int helgetValuelo();
//b.cpp
int getValue(){return 1;}

我们将a.cpp和 b.cpp 分别编译为目标文件a.o和b.o,并尝试将其链接到一起,会导致符号重定义错误。

2.5、运行期符号重定义

符号重定义错误通常是在编译期间被检测到的,而不是在运行时。然而,有些情况下可能会在运行时发生符号重定义错误。例如,在动态链接库或共享对象中,函数或变量可以在运行时加载和卸载。如果在两个动态链接库中定义了相同名称的函数或变量,它们可能会导致符号重定义错误。

3、符号重定义机制

c++ 采用单文件编译,在编译期间将这些文件单独编译成目标文件,然后通过链接器将它们组合成一个可执行文件。编译器在把单个源码文件编译成目标文件过程中,会检测源码中的同名符号,从而发现预处理期符号重定义,编译期符号重定义的错误。目标文件格式如下:
目标文件头ELF Header内存放有符号信息,把多个目标文件链接到一起的时候,若发现文件头内有同名符号,就会报链接期符号重定义错误。接下来看一下符号表结构:

4、解决符号重定义的方法

这里列举了6种解决符号重定义问题的方法,其中static,inline,extern,const只能用于解决链接期重定义问题;
预处理指令只用于解决编译期重定义问题;
命名空间可解决预处理期和编译期重定义问题


http://www.ppmy.cn/news/1282707.html

相关文章

第十四章 集合(List)

一、集合框架体系 集合&#xff1a; &#xff08;1&#xff09;可以动态保存任意多个对象。 &#xff08;2&#xff09;提供了一系列方便的操作对象的方法&#xff1a;add、remove、set、get等。 二、Collection 1. Collection 接口常用方法 &#xff08;1&#xff09;add&a…

【已解决】c++ 如何给qt的lineEdit传入中文且不乱码

本博文源于笔者正在写的一个模块&#xff0c;需要给qt的linEdit传入中文&#xff0c;并且不会乱码&#xff0c;原本想着在初始化&#xff0c;也就是构造函数里写入ui.lineEdit->setText(“你好”);结果发现&#xff0c;显示出来是乱码&#xff0c;那怎么办啊。下面就以一个小…

k8s集群通过helm部署skywalking

1、安装helm 下载脚本安装 ~# curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 ~# chmod 700 get_helm.sh ~# ./get_helm.sh或者下载包进行安装 ~# wget https://get.helm.sh/helm-canary-linux-amd64.tar.gz ~# mv helm …

5.Redis管道(pipeline)

5.Redis管道&#xff08;pipeline&#xff09; 如何优化频繁命令往返造成的性能瓶颈&#xff1f; 引出管道这个概念 管道&#xff08;pipeline&#xff09;可以一次性发送多条命令给服务端&#xff0c;服务端依次处理完毕后&#xff0c;通过一条响应一次性结果返回&#xff0…

Java学习:Comparator和Comparable

一.Comparator的使用 1.Arrays.sort 给基本类型的数组进行排序的方法&#xff0c;默认是实现升序&#xff0c;也可以通过方法写匿名类自定义实现升降序排序。 代码&#xff1a; Arrays.sort(arr,new Comparator<Integer>(){Overridepublic int compare(Integer o1, In…

操作系统面试题目

1、进程和线程的区别&#xff1a; 调度&#xff08;进程是资源的最小单位&#xff0c;线程是程序执行的基本单位&#xff09;&#xff1b;切换&#xff08;线程切换快&#xff09;&#xff1b;拥有资源&#xff08;线程不拥有资源&#xff09;&#xff1b;系统开销2、并发和并行…

记录为 uni-app的扩展组件(uni-ui)和 微信小程序标签 添加行内样式的正确做法

如题&#xff0c;首先&#xff0c;正确为微信小程序标签添加行内样式&#xff0c;其做法是&#xff1a;&#xff08;以view为例&#xff09; <view style"width: 400rpx; height: 400rpx; background-color: green;">goods_list</view>也就是说&#xf…

顺序表的实现(头插、尾插、头删、尾删、查找、删除、插入)

目录 一. 数据结构相关概念​ 二、线性表 三、顺序表概念及结构 3.1顺序表一般可以分为&#xff1a; 3.2 接口实现&#xff1a; 四、基本操作实现 4.1顺序表初始化 4.2检查空间&#xff0c;如果满了&#xff0c;进行增容​编辑 4.3顺序表打印 4.4顺序表销毁 4.5顺…