返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C/C++开发中extern的一些使用注意事项
  • 897
分享到

C/C++开发中extern的一些使用注意事项

C/C++开发extern使用事项C C++ extern 2023-01-04 12:01:50 897人浏览 泡泡鱼
摘要

目录前言数组与指针的区别具体分析extern "C"前言 前些日子,有友友问了我这样的一道问题: 数组通过外部声明为指针时,数组和指针是不能互换使用的;那么请思

前言

前些日子,有友友问了我这样的一道问题:

数组通过外部声明为指针时,数组和指针是不能互换使用的;那么请思考一下,在 A 文件中定义数组 char a[100];在 B 文件中声明为指针:extern char *a;此时访问 a[i],会发生什么;

先说结果,会引起 segmentation fault 报错;

那接下来由博主来分析一番;

数组与指针的区别

在介绍 extern 之前,我们需要了解一下数组与指针有什么区别?

数组变量代表了存放该数组的那块内存,它是这块内存的首地址。这就说明了数组变量是一个地址,而且,还是一个不可修改的常量,具体来说,就是一个地址常量。

数组变量跟枚举常量一样,都属于符号常量。数组变量这个符号,就代表了那块内存的首地址。注意,不是数组变量这个符号的值是那块内存的首地址,而是数组变量这个符号本身代表了首地址,它就是这个地址值。这就是数组变量属于符号常量的意义所在。

由于数组变量是一种符号常量,它是一个右值,而指针,作为变量,却是一个左值,一个右值永远都不是左值,那么,数组名永远都不会是指针!

举个例子,char a[] 中的 a 是常量,是一个地址,char *a 中 a 是一个变量,一个可以存放地址的变量。

具体分析

了解了数组与指针的区别之后,让我们来看看 extern 声明全局变量的内部实现;

externC/C++ 语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用。

TIP :被 extern 修饰的全局变量不被分配空间,而是在链接的时候到别的文件中通过查找索引定位该全局变量的地址。

extern char a[];

这是一个外部变量的声明,它声明了一个名为 a 的字符数组,编译器看到这个声明就知道不必为这个变量分配空间,这个 .cpp 文件中所有对数组 a 的引用都化为一个不包含类型的标号,具体地址的定位留给链接器完成。编译完成之后也得到一个中间文件,链接器遍历这个文件,发现有未经定位的标号,于是它搜索其他中间文件,试图寻找到一个匹配的空间地址,在此例中无疑链接器将成功地寻找到这个地址并将此中间文件中所有的这个标号替换为链接器所寻找到的地址,最终生成的可执行文件中,所有曾经的标号都应当已经被替换为地址。这是一个正常工作过程,链接出来的可执行文件至少在对于该数组的引用部分将工作得很好。

extern char * a; 

这是一个外部变量的声明,它声明了一个名为 a 的字符指针,中间过程与上同,经过一番搜索,找到了一个分配过空间的名为 a 的地方(也就是我们先定义的那个字符数组),链接器并不知道它们的类型,仅仅是发现它们的名字一样,就认为应该把 extern 声明的标号链接到数组 a 的首地址上,因此链接器把指针 a 对应的标号替换为数组 a 的首地址。这里问题就出现了:由于在这个文件中声明的 a 是一个指针变量而不是数组,链接器的行为实际上是把指针 a 自身的地址定位到了另一个 .c 文件中定义的数组首地址上,而不是我们所希望的把数组的首地址赋予指针 a(这很容易理解:指针变量也需要占用空间,如果说把数组的首地址赋给了指针 a,那么指针 a 本身在哪里存放呢?)。这就是症结所在了。所以此例中指针 a 的内容实际上变成了数组 a 首地址开始的 4 字节表示的地址(如果在 16 位机上,就是 2 字节)。

上述加粗部分的可以理解为,链接器认为 a 变量本身的内存位置是数组的首地址,但其实 a 的位置是其他位置,其内容才是数组首地址。

举个例子,定义 char a[] = "abcd",则外部变量 extern char a[] 的地址是 0x12345678 (数组的起始地址),而 extern char *a 是重新定义了一个指针变量 a,其地址可能是 0x87654321,因此直接使用 extern char *a 是错误的。

通过上述分析,我们得到的最重要的结论是:使用 extern 修饰的变量在链接的时候只找寻同名的标号,不检查类型,所以才会导致编译通过,运行时出错。

extern "C"

extern "C" 包含双重含义,从字面上即可得到:

  • 首先,被它修饰的目标是 extern 的;
  • 其次,被它修饰的目标是 C 的。

1、 被 extern "C" 限定的函数或变量是 extern 类型的;

extern int a;

仅仅是一个变量的声明,其并不是在定义变量 a,并未为 a 分配内存空间。变量 a 在所有模块中作为一种全局变量只能被定义一次,否则会出现连接错误。

通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字 extern 声明。例如,如果模块 B 欲引用该模块 A 中定义的全局变量和函数时只需包含模块 A 的头文件即可。这样,模块 B 中调用模块 A 中的函数时,在编译阶段,模块 B 虽然找不到该函数,但是并不会报错,它会在连接阶段中从模块 A 编译生成的目标代码中找到此函数。

extern 对应的关键字是 static,被它修饰的全局变量和函数只能在本模块中使用。因此,一个函数或变量只可能被本模块使用时,其不可能被 extern "C" 修饰。

2、被 extern "C" 修饰的变量和函数是按照 C 语言方式编译和连接的;

未加 extern "C" 声明时的编译方式

作为一种面向对象的语言,c++ 支持函数重载,而过程式语言 C 则不支持。函数被 C++ 编译后在符号库中的名字与 C 语言的不同。例如,假设某个函数的原型为:

void foo( int x, int y );

该函数被 C 编译器编译后在符号库中的名字为 _foo,而 C++ 编译器则会产生像 _foo_int_int 之类的名字(不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为 “mangled name”)。

_foo_int_int 这样的名字包含了函数名、函数参数数量及类型信息,C++ 就是靠这种机制来实现函数重载的。例如,在 C++ 中,函数 void foo(int x, int y)void foo(int x, float y) 编译生成的符号是不相同的,后者为 _foo_int_float

同样地,C++ 中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程序的类成员变量可能与全局变量同名,我们以 . 来区分。而本质上,编译器在进行编译时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与用户程序中同名的全局变量名字不同。

未加 extern "C" 声明时的连接方式

假设在 C++ 中,模块 A 的头文件如下:

// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
int foo(int x, int y);
#endif

在模块 B 中引用该函数:

// 模块B实现文件 moduleB.cpp
#include "moduleA.h"
foo(2, 3);

实际上,在连接阶段,连接器会从模块 A 生成的目标文件 moduleA.obj 中寻找 _foo_int_int 这样的符号;

extern "C" 声明后的编译和连接方式

extern "C" 声明后,模块 A 的头文件变为:

// 模块A头文件 moduleA.h
#ifndef MODULE_A_H
#define MODULE_A_H
extern "C" int foo(int x, int y);
#endif

在模块 B 的实现文件中仍然调用 foo(2, 3),其结果是:

  • 模块 A 编译生成 foo 的目标代码时,没有对其名字进行特殊处理,采用了 C 语言的方式;
  • 连接器在为模块 B 的目标代码寻找 foo(2, 3) 调用时,寻找的是未经修改的符号名 _foo

如果在模块 A 中函数声明了 fooextern "C" 类型,而模块 B 中包含的是 extern int foo(int x, int y),则模块 B 找不到模块 A 中的函数;反之亦然。

所以,可以用一句话概括 extern "C" 这个声明的真实目的:实现 C++ 与 C 及其它语言的混合编程

以上就是C/C++开发中extern的一些使用注意事项的详细内容,更多关于C/C++开发extern使用事项的资料请关注编程网其它相关文章!

--结束END--

本文标题: C/C++开发中extern的一些使用注意事项

本文链接: https://lsjlt.com/news/176647.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C/C++开发中extern的一些使用注意事项
    目录前言数组与指针的区别具体分析extern "C"前言 前些日子,有友友问了我这样的一道问题: 数组通过外部声明为指针时,数组和指针是不能互换使用的;那么请思...
    99+
    2023-01-04
    C/C++开发extern使用事项 C C++ extern
  • C#中HttpClient使用注意事项有哪些
    小编给大家分享一下C#中HttpClient使用注意事项有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!最近在测试一个第三方API,准备集成在我们的网站应用中...
    99+
    2023-06-29
  • C++开发注意事项:避免常见的C++开发错误
    C++作为一种高级编程语言,在软件开发中应用非常广泛。然而,由于C++具有一定的复杂性和繁琐性,开发人员在进行C++开发时往往会遇到一些常见的错误。这些错误在程序的性能、可靠性和可维护性方面都会产生深远的影响。因此,本文将介绍一些C++开发...
    99+
    2023-11-23
    开发注意事项 C++错误避免 常见错误避免
  • C++开发注意事项:避免C++代码中的异常不一致性
    C++开发注意事项:避免C++代码中的异常不一致性引言:C++是一种功能强大且灵活的编程语言,但在开发过程中,不一致的异常处理可能会导致程序的不可预知的行为和错误。本文将探讨一些重要的注意事项,以帮助开发人员避免C++代码中的异常不一致性。...
    99+
    2023-11-22
    异常处理 一致性 C++开发
  • C++开发注意事项:避免C++多线程开发的陷阱
    C++开发注意事项:避免C++多线程开发的陷阱在当今的软件开发领域中,多线程编程已经变得异常重要。无论是为了提高程序性能还是为了避免阻塞,利用多线程来进行并行处理已经成为了一个普遍的趋势。而对于C++开发者而言,多线程编程更是一个十分重要的...
    99+
    2023-11-22
    C++开发 多线程开发 陷阱避免
  • C++开发注意事项:避免C++安全漏洞的发生
    C++开发是一项广泛应用于软件开发领域的技术,其灵活性和高效性使其成为许多项目的首选语言。然而,随之而来的是需要特别注意C++代码中的安全漏洞。本文将介绍一些C++开发注意事项,以帮助开发人员避免常见的安全漏洞的发生。数组越界访问:C++中...
    99+
    2023-11-22
    安全 开发 漏洞
  • C++开发注意事项:避免C++代码中的内存溢出
    C++开发注意事项:避免C++代码中的内存溢出C++语言作为一种强大的编程语言,被广泛应用于系统软件、游戏开发、嵌入式系统和高性能应用程序等领域。然而,在C++开发过程中,内存溢出是一个常见的问题,它可能导致程序崩溃、安全漏洞和性能问题。因...
    99+
    2023-11-22
    内存分配 智能指针 内存释放
  • C++开发注意事项:避免C++代码中的死锁问题
    C++开发注意事项:避免C++代码中的死锁问题引言:在C++开发中,死锁(Deadlock)是一个很常见的问题,它会导致程序出现无响应、崩溃等严重后果。因此,我们在编写C++代码时,要特别注意避免死锁的发生。本文将介绍一些常见的死锁问题以及...
    99+
    2023-11-22
    C++开发 (C++ development) 死锁 (deadlock) 注意事项 (precautions)
  • C++开发注意事项:避免C++代码中的魔法数字
    C++开发注意事项:避免C++代码中的魔法数字在C++开发中,魔法数字指的是代码中直接出现的未经解释的硬编码数字。这些数字通常会在代码中直接出现多次,给代码的维护和理解带来了困难。因此,在C++开发中,避免使用魔法数字是一个很重要的注意事项...
    99+
    2023-11-22
    C++ 开发注意事项 魔法数字
  • C++开发注意事项:避免C++代码中的资源泄漏
    C++作为一种强大的编程语言,广泛应用于软件开发领域。然而,在开发过程中,很容易遇到资源泄漏的问题,导致程序运行不稳定或者出现错误。本文将介绍一些C++开发中避免资源泄漏的注意事项。资源泄漏是指在程序中分配了一定的资源(如内存、文件句柄、数...
    99+
    2023-11-22
    内存管理 错误处理 资源管理
  • C语言中使用break要注意哪些事项
    在C语言中,使用break语句时需要注意以下几点事项:1. break只能用于循环语句和switch语句中,用来跳出当前循环或swi...
    99+
    2023-10-12
    C语言
  • C++开发注意事项:避免C++代码中的循环引用问题
    C++是一种广泛使用的编程语言,被广泛应用于游戏开发、嵌入式系统开发等各个领域。在C++开发过程中,有一种常见的问题被称为“循环引用”问题。循环引用指的是两个或多个类之间互相引用对方,形成一个循环的引用关系。这种情况会导致编译错误或运行时错...
    99+
    2023-11-22
    C++ 循环引用 开发注意事项
  • 使用CDN的一些注意事项
    使用CDN的注意事项有: 选择CDN的时候,为了保障网站的安全性,应该选择安全可靠的提供商。不要为搜索引擎设置指定IP,不然CDN发挥不了作用,会使CDN加速系统就无法给出最佳的加速线路。做域名CNAME解析时,地址后面要加点,不然CDN网...
    99+
    2024-04-02
  • C++开发注意事项:避免C++代码中的空指针异常
    C++开发中,空指针异常是一种常见的错误,经常出现在指针没有被初始化或被释放后继续使用等情况下。空指针异常不仅会导致程序崩溃,还可能造成安全漏洞,因此需要特别注意。本文将介绍如何避免C++代码中的空指针异常。初始化指针变量C++中的指针必须...
    99+
    2023-11-22
    注意事项 C++编程 空指针异常
  • C++开发注意事项:避免C++性能优化的误区
    C++开发注意事项:避免C++性能优化的误区在C++开发中,性能优化是一个非常重要的因素。优化代码的性能可以提高程序的执行效率和响应速度,对于大型项目和要求高性能的应用尤为重要。然而,在进行C++性能优化时,我们也需要注意一些误区,以避免引...
    99+
    2023-11-22
    注意事项 C++性能优化 误区
  • C/C++中的静态变量注意事项
    前言 C/C++中的静态变量,相信大多数人都用过,但你很可能用错了,包括你现在所在的项目中都可能埋着这个坑,不信我们往下看! 正文 我们先来看一段大家常写的代码,很简单,这段代码没啥...
    99+
    2024-04-02
  • C++构造函数的一些注意事项总结
    目录1、匿名对象2、拷贝构造函数的调用时机3、深拷贝和浅拷贝总结1、匿名对象 首先应该明确匿名对象,匿名对象是之没有对象名,调用完构造函数后即析构的对象。下面通过代码捕捉类的构造函数...
    99+
    2024-04-02
  • C++开发注意事项:避免C++代码中的编码规范问题
    在进行C++开发时,除了关注功能实现和性能优化等方面的问题外,开发人员还需要注意代码的编码规范。良好的编码规范不仅可以提高代码的可读性和可维护性,还有助于减少错误和增加代码的一致性。本文将介绍一些常见的C++开发注意事项,帮助开发人员避免编...
    99+
    2023-11-22
    注意事项 编码规范 C++开发 关键词提取:
  • C++开发注意事项:避免C++多态性的潜在问题
    C++作为一种面向对象的编程语言,多态性是其的一大特点。多态性可以帮助我们在编写程序时更加灵活,有效地复用代码。但是,当我们不小心使用不当的多态性方法时,就会出现潜在的问题。本文将介绍一些C++开发注意事项,以避免多态性带来的潜在问题。避免...
    99+
    2023-11-22
    多态性 C++开发 潜在问题
  • 使用c语言常量要注意哪些事项
    使用C语言常量时需要注意以下事项: 常量的值不能被修改。一旦常量被定义后,它的值就不能被修改。 常量的命名应该符合命名规范,...
    99+
    2023-10-23
    c语言
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作