C语言重新开始:编译通过了,为什么还会链接失败?

pandas使用指南完整版 创建DataFrame对象 创建DataFrame对象的数据可以为列表,数组和字典 import pandas as pd import numpy as np a = [[1, 2, 3],[4, 5, 6]] #二维矩阵 df2 = pd.DataFrame(a, columns=[‘col1’,‘col2’,‘col3’], index=[‘a’,‘b’]) print df2 结果: co... 阅读详情

gcc -c main.c 没有报错,不代表已经能得到一个可执行程序。如果 main.c 调用了一个只有声明、没有提供定义的函数,问题可能直到链接时才出现。

这篇从一个小程序开始,亲手把每个中间文件生成出来。原文曾以“C语言重拾”为开头,却残留了不相关的默认模板;这里重新整理为独立的 C 入门实验,不把模板当作学习经历。

实验使用 Linux 下的 GCC,已在 WSL 的 Arch Linux、GCC 16.1.1 上验证。Windows 可以在已安装 GCC 的 WSL 中运行;下面的文件都放在一个单独的实验目录,不需要管理员权限,也不改系统配置。

1. 先准备三个文件

建立并进入一个空目录,例如 mkdir c-build-demo 后执行 cd c-build-demo。将以下代码分别保存为对应文件。

greet.h:告诉调用者函数怎么调用。

#ifndef GREET_H
#define GREET_H

#define APP_NAME "C build demo"
void greet(const char *name);

#endif

main.c:使用头文件中的宏和函数声明。

#include "greet.h"

int main(void)
{
    greet(APP_NAME);
    return 7;
}

greet.c:提供函数真正执行的代码,也就是定义。

#include <stdio.h>
#include "greet.h"

void greet(const char *name)
{
    printf("Hello, %s\n", name);
}

先把完整程序跑通:

gcc -std=c11 -Wall -Wextra -Werror main.c greet.c -o demo
./demo
status=$?
printf 'exit status: %s\n' "$status"

屏幕会出现:

Hello, C build demo
exit status: 7

Hello 是程序打印的文本,7 是 main 返回给运行环境的退出状态。它们不是一回事。本例故意返回7用于观察:程序执行到了最后,但在 shell 的约定里,非零退出状态仍表示非成功状态,不应把它用于“成功”的自动化判定。

2. 一条 gcc 命令背后有四个阶段

可以把过程画成这条链:

main.c + greet.h
        |
        | 预处理:处理 #include、宏和条件编译
        v
      main.i
        |
        | 编译:产生汇编代码
        v
      main.s
        |
        | 汇编:产生目标文件
        v
      main.o ----- greet.o
           \       /
            \     / 链接:结合目标文件与所需库
              demo

这里“编译”有两种常见说法:日常说“编译程序”,往往泛指整条构建链;讨论具体阶段时,则指从预处理后的 C 代码到汇编代码的转换。先分清语境,才不会把“编译没错”和“整个构建成功”混在一起。

GCC 的 -E、-S、-c 分别控制停在哪个阶段。注意:-S main.c 仍会先预处理,-c main.c 仍会先预处理、编译、汇编,不是跳过前面的工作。GCC 阶段控制选项

命令停止位置产物还不能说明什么
gcc -E main.c -o main.i预处理后展开后的文本C 语义正确、能链接
gcc -S main.c -o main.s编译后汇编文本所需函数都能找到定义
gcc -c main.c -o main.o汇编后目标文件已经是可直接运行的程序
gcc main.o greet.o -o demo链接后可执行文件运行结果符合业务预期

目标文件不是“装着 C 源码的文本”。它包含机器代码以及链接所需的符号、重定位等信息;通常不能像 ./demo 那样直接运行。

3. 分阶段生成,亲眼看中间结果

gcc -std=c11 -E main.c -o main.i
gcc -std=c11 -S main.c -o main.s
gcc -std=c11 -Wall -Wextra -Werror -c main.c -o main.o
gcc -std=c11 -Wall -Wextra -Werror -c greet.c -o greet.o

打开 main.i,能看到函数声明和展开后的调用:

greet("C build demo");

APP_NAME 已替换成字符串,但 greet 的函数体并不会因为包含 greet.h 自动出现:头文件里只有声明。

main.s 的具体指令会因 CPU 架构、编译器版本及优化选项不同而变化。不需要背这段汇编,先确认它是编译器生成的汇编文本,而不是最终可执行文件即可。

还可以用 nm 看目标文件里的符号:

nm main.o
nm greet.o

在本例的普通目标文件中,main.o 会把 greet 标为 U,表示它在这个文件中尚未定义;greet.o 则提供该函数。具体地址不必相同。U 不等于程序一定有错:它可能由其他目标文件或库在后续步骤中提供。GNU nm 符号说明

4. 故意漏掉一个目标文件,复现链接失败

仅链接 main.o:

gcc main.o -o missing-demo
status=$?
printf 'link status: %s\n' "$status"

这一步会失败,错误信息中包含对 greet 的未定义引用,常见措辞是 undefined reference to 'greet'。不同工具链的标点、路径、诊断格式可能不同,因此自动验证不能要求整段错误文本逐字相等。

为什么之前 gcc -c main.c 能通过?编译器看到了 void greet(const char *name);,知道函数接收什么参数、返回什么类型,可以生成调用代码。链接时才需要把调用和实际实现接起来,而当前命令没有提供 greet.o。

修复不是删除函数声明,也不是关闭警告,而是把定义所在的目标文件交给链接器:

gcc main.o greet.o -o demo
./demo
status=$?
printf 'exit status: %s\n' "$status"

如果出现类似问题,可以先问三件事:函数有没有定义?定义所在的文件有没有参与构建?声明与定义的名称、签名是否一致?不要一看到错误就去改 #include;“找不到头文件”和“链接找不到函数”不是同一个阶段。

5. 验证时不能只看有没有生成文件

这个实验应检查的是一组事实,而不是“命令似乎运行过”:

检查点期望
分阶段命令全部返回0,生成非空 .i、.s、.o
预处理结果宏展开为字符串
仅链接 main.o非零退出,诊断提及 greet
链接两个目标文件返回0
运行 demo输出准确,同时退出状态为7

其中“故意失败”的链接命令返回非零,才算这个测试点成立;不能给整段脚本简单加上 set -e 后就不管它,否则会在期望的失败处提前结束。运行返回7的程序也一样,需要单独捕获状态。

再做一步变化:把 APP_NAME 换成另一段文本,重新构建后观察输出。头文件没有独立变成可执行文件,却通过预处理影响了使用它的源码。这也说明,真实项目里改了头文件,为什么相关源文件常常需要重新编译。

这次真正要记住的不是三个扩展名,而是:声明让调用可以被编译,定义让调用在链接时有着落;链接成功之后,还要验证运行结果。 排查错误先定位阶段,会比盲目修改源码更有方向。

gcc编译C语言全链路拆解:从预处理到链接的4个关键阶段 C语言编译本质是将高级代码转换为机器可执行指令的过程,其核心在于理解编译器工作原理与工具链协同机制。gcc并非单一程序,而是调度预处理、编译、汇编和链接四个阶段的前端工具,每个阶段对应不同中间文件(.i/.s/.o)和系统级依赖。掌握这些阶段不仅有助于排查‘stdio.h找不到’‘undefined reference’等高频报错,更能支撑跨平台开发(如Windows下w64devkit的UTF-8兼容)、工程化构建(Makefile与头文件路径管理)及调试优化(gdb/valgrind/AddressSa 阅读详情

相关推荐

C语言中动态链接库加载错误的排查与修复

统一编译选项确保库文件与程序使用相同的编译器版本、标准库版本和优化选项(如-O2显式错误处理在dlopendlsym等调用后检查返回值,并通过dlerror输出详细错误信息。依赖管理工具使用pkg-config(Linux)或vcpkg(Windows)管理依赖库路径和编译选项。版本控制为库文件添加版本号(如),并通过符号链接指向当前版本。日志与监控在生产环境中记录动态库加载失败事件,并设置告警阈值。C语言动态链接库加载错误的核心在于理解库的加载机制与依赖关系。通过工具链(lddstracenm。

shejizuopin的博客 1818

一文详解:Vue3中使用Vue Router

详细介绍了Vue3中使用Vue Router,从Vue Router的安装,基础知识到路由嵌套、路由守卫及路由懒加载等内容详细探讨了Vue Router的使用方法

九仞山的专栏 3万+

aspose官网介绍

Aspose是一套强大的文档处理工具,被超过80%的财富100强公司信赖,用于在应用程序中创建、编辑、导出和转换100多种文件格式。无论是PDF、Word文档、Excel表格还是PowerPoint演示文稿,Aspose都能轻松胜任。

lgbisha的博客 1689

vue3中路由的使用(详细讲解)

在前端或者vue3项目中路由主要用于构建单页面应用程序(SPA),其中所有的页面都在同一个HTML文件中加载,通过JavaScript动态地切换显示不同的内容。路由器(Router):路由器是负责管理路由的组件或模块。它定义了路由的规则、路径和对应的处理函数或组件。请着重注意,路由器(Router)与路由(Route)的区别。路由器是用来管理路由的,你也可以理解为路由的集合。如果将一个一个的路由比作学生,那么路由器就是老师,负责管理所有的路由。

2301_78646673的博客 1万+

Python文件保存问题解决方法(涉及VTK)

当无法保存Python文件时,特别是在使用VTK库进行可视化操作时,我们应该检查文件路径、权限、文件格式以及代码逻辑等方面的问题。通过仔细检查这些因素,我们可以解决大多数保存文件的问题,并顺利生成所需的输出文件。如有其他问题,请随时提问。在使用Python编程时,有时候会遇到无法保存文件的问题,特别是在使用VTK(Visualization Toolkit)库进行可视化操作时。如果你使用的是错误的文件格式,可能会导致文件损坏或无法打开。请仔细检查你的代码逻辑,确保在保存文件之前已经完成了必要的操作。

VipeNvml的博客 1219

配置VS Code使用Linux服务器上的Matlab

0、需求 Linux服务器上安装有Matlab 无图形界面,只能使用命令行工具,编辑文件不方便 1、VS Code插件安装 在扩展商店中搜索matlab,会出现Matlab Extension Pack,里面有整理好的matlab扩展 这里只安装了第一排的三个扩展 2、设置(Windows、Mac的设置类似,只是不同系统的文件路径不同) 安装好扩展,重启VS Code后,进行设置 打开:文件→首选项→设置(或快捷键ctrl+,),搜索matlab,结果如下图所示;直接打开完整json配置文件(图

赵彬的博客 5008

linux怎么重新编译c,Linux:重新编译后,C ++库无法链接(示例代码)

我在远程Linux服务器上安装了https://github.com/simongog/sdsl-lite库。我所做的是在我的主目录中创建lib,include目录,然后像./install.sh /my/home/dir一样运行脚本,如上页所述。我能够通过使用CMakeLists.txt文件将其成功链接到我的其他程序,类似于以下内容:cmake_minimum_required(VERSION ...

weixin_31523667的博客 264

在VS2019里把JSBSim飞行模型集成到你的C++项目里(附完整配置流程和常见错误解决)

本文详细介绍了如何在Visual Studio 2019中将JSBSim飞行模型集成到C++项目中,包括环境准备、源码编译、项目配置和常见错误解决方案。通过实战技巧和优化建议,帮助开发者高效实现高精度飞行仿真功能,提升开发效率。

weixin_42620202的博客 413

Rust 项目编译故障排查:从 ‘onnxruntime‘ 链接失败到 ‘#![feature]‘ 工具链不兼容错误

工具链,它为了保证稳定性,禁止使用任何未稳定的功能,因此直接报错。这清晰地表明,项目中的某个 crate 是为 Nightly Rust 环境而设计的。解决此问题的唯一正确方法是,将项目的 Rust 编译环境切换到 Nightly 版本,以满足代码的编译要求。在解决了链接问题后,出现了新的编译错误,直接指向项目内某个 crate 的源码。命令时,先后遇到了两个不同性质的编译错误,导致编译流程中断。为项目指定正确的工具链,是解决此类问题的标准最佳实践。错误时,说明项目依赖于 Rust 的实验性功能,因此。

u014288878的博客 1105

神联动:LSTM+卡尔曼滤波!一区大门轻松开!

近来热度飙升,成为时间序列预测和状态估计等领域的香饽饽,非常好发高区!主要在于:这两者结合,不仅能提高模型预测准确性;还能拓展应用场景,为我们的论文创新提供发挥空间,且当下还在蓝海阶段,还不算卷。具体点说:卡尔曼滤波擅长处理线性状态估计和噪声,能使LSTM更好地适应动态环境的变化,减少噪声干扰,降低预测误差。而LSTM通过捕捉长期依赖关系,擅长处理非线性时间序列,能够弥补卡尔曼滤波在这方面的不足,从而把应用场景从传统的状态估计,延伸到金融预测、工业检测、自动驾驶等任务。

2401_82426425的博客 2208

【电力电子控制】基于强化学习的单相逆变器并网电流控制

本文将尝试以 DQN(Deep Q-Network)为例,设计并训练一个面向单相逆变器的智能控制器,实现对并网电流的精准调节。

weixin_62596197的博客 463

C语言——程序的编译和链接相关知识点总结

1.编译的过程:将.c文件转成二进制文件。 C语言中,每个.c文件都视为一个“编译单元”,针对每个编译单元,编译器先把.c以及所包含的.h转换成.o(.o是一个“目标文件”,也是一个二进制的文件,但是还不能执行) 2链接的过程:把.o文件最后合并到一起 实际开发中,经常会把一个函数的定义和调用分散在两个不同的.c中,就会被编译到不同的.o里面,所以最后需要合并这些.o文件。 链接过程除了要链接用户自己写的文件编译的.o之外,还需要链接一些库文件(标准库,第三方库),此处的库可以是动态库的方式提

weixin_43939602的博客 348

移动通信基础(13)TDL、CDL、信道相关性

参考《U型槽无线信道多径传播特性测量与建模方法的研究》、《5G信号源中的信道模拟与仿真研究》衰落衰落理解为信号幅度在时间和频率上的不断波动,是无线信道一个重要的恶化特性。信道幅度恶化的两个主要来源分别是加性噪声和信号衰落,高斯白噪声信道模型就是典型的加性噪声,信号衰落引起的是乘性的信号扰动,与加性噪声从原理上完全不同。信道的频率选择性衰落组为无线信道的重要特征之一,是人们进行无线系统设计考虑的重要因素,决定了导频隔间的大小。...

tybAly的博客 2万+

zlib编译错误解决:从缺少头文件到链接失败的解决方案

zlib作为广泛使用的压缩库(Compression Library),在开发中常因环境配置、依赖管理或编译选项问题导致各类错误。本文系统梳理从**缺少头文件**到**链接失败**的12类典型错误,提供基于zlib 1.3.1.1版本的专业解决方案,涵盖Linux/macOS/Windows全平台,包含50+代码示例与流程图解。 ## 问题诊断框架:zlib编译错误分类与排查流程 zlib编译...

gitblog_01007的博客 1782

【WSL2+Ubuntu+Docker Desktop】迁移到D盘

该篇文章包含将WSL2+ubuntu+Docker Desktop从C盘迁移到D盘

哈茶的博客 3929

【云原生进阶之容器】第六章容器网络6.6.2--Cilium部署

部署Cilium非常简单,可以通过单独的yaml文件部署全部组件(目前我使用了这个方式部署了1.7.1版本),也可以通过Helm Chart一键完成。重要的是部署环境和时机:官方建议所有部署节点都使用Linux最新稳定内核版本,这样所有的功能都能启用。作为一个Kubernetes网络组件,它应该在部署Kubernetes其他基础组件之后,才进行部署。

junbaozi的专栏 789

【c语言】编译与执行的区别

本文深入浅出地解析了编程中编译与执行的核心区别。编译是将源代码转换为机器码的翻译过程,主要检查语法正确性;执行则是实际运行程序,验证逻辑正确性。文章通过生活类比、技术解析和多种语言对比,帮助读者理解:编译型语言先完全编译后执行,解释型语言边解释边执行,混合型语言则先编译为中间码再由虚拟机执行。文中还提供了实际开发工作流、错误类型对比和完整示例,强调编译检查"代码写得对不对",执行验证"代码做得对不对"这一核心差异。理解这两个概念是编程入门的重要一步。

Kratzdisteln的博客 1231

【技能】OpenClaw Skill 操作钉钉(原理+20个实例)

OpenClaw Skill是遵循AgentSkills规范的自动化逻辑单元,用于指导AI助理调用钉钉工具。每个Skill包含SKILL.md文件,定义触发条件、执行逻辑和接口调用方式。Skill通过钉钉Connector插件实现API鉴权和请求转发,需配置ClientID/Secret等凭证。Skill加载路径包括工作目录、本地目录和内置目录,优先级由高到低。使用前需安装必备插件并配置钉钉权限,通过openclaw.json管理Skill开关和环境变量。ClawHub作为官方技能仓库支持Skill的安装和

海兰 1212

C语言常见编译报错

原因分析:调用的静态函数在调用之后声明的。解决方法:把静态函数声明放在调用前面。

weixin_45117176的博客 3138

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

用TCC实现C语言编译器自举的全过程解析

深入探索Tiny C Compiler(TCC)——仅100K却功能强大的开源C编译器,支持C99、跨平台,并实现在Windows下用自己的源码完成自编译,展现编译器自举的核心原理。

weixin_42509888的博客 929

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Linux下的GCC编译器

GCC是GNU公社的一个项目,它是GNU Compiler Collection的缩写,起初,GCC只是一个C语言编译器(GNU C Collection),随着开发者的加入,GCC已扩展为可以编译C、C++、Java、Objective-C等多种编程语言的编译器集合了。如今的GCC也具有了交叉编译的功能,即在一个平台上编译另一个平台的代码。所谓编译器,可以简单地将其理解为“翻译器”。

asddrsq的博客 3206

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

(C语言之路-----p13 程序的编译与链接)

(C语言之路-----p13 程序的编译与链接) (一)我们写的代码是如何变成可执行程序然后执行的? 我们以我们写的test.c文件为例 其中这个程序会经历两个环境,第一个是翻译环境,第二个是执行环境 翻译环境又分为两个阶段,就是编译和链接 编译器将我们写的test.c文件进行编译以后,会产生目标文件,其文件的后缀一般是**.obj**;当然,我们平时写程序的时候,可能不止写一个.c文件,此时如果是多个.c文件,都会经过编译器的编译,变成目标文件,目标文件会和预存好的库由链接器进行链接,变成可执行程序,其文

CK1513710764的博客 780

用TCC实现C语言编译器自举的全过程

Tiny C Compiler(TCC)仅约100KB,却支持C99标准和多平台,具备直接运行C代码的独特能力。通过下载源码并使用TCC自身编译,可完成编译器自举实验,验证C语言“自己编译自己”的经典特性,整个过程简洁高效。

weixin_36328210的博客 1101

c语言打断语录,重新认识C语言

1.缘起接触C语言有三四年时间了,工作中也一直使用C语言。但对于一些C语言的特性和定义还存在一些疑问,这里总结一下,作为以后参考。2.C语言的链接属性工作中无意发现了C语言一个有趣的问题,在两个源文件中定义了同一个未初始化的变量,编译器竟然不报错,但是如果在其中一个文件中定义并初始化,那就会报错。我测试使用的代码如下(测试环境window7(32位)gcc 4.5.0):main.c:glob...

weixin_36450447的博客 201

C语言开发工具推荐及安装指南

在本篇文章中,讲述了市面上大多数人使用的C语言的开发工具,主要推荐了3款C软件,都会对其安装步骤进行讲解。

weixin_74407520的博客 5354

C语言inline关键字:链接语义、编译器行为与性能优化实践

函数调用虽然在源代码层面看似简单,但在底层包含参数传递、栈帧建立与恢复等固定开销。为了降低高频小函数的调用成本,C语言引入了内联(inline)这一优化手段,允许编译器在调用点展开函数体,从而减少跳转和现场保存的指令。然而,inline并不仅仅是一个性能提示,它在C标准中还有复杂的链接语义,尤其是在头文件中使用时,稍不注意就会引发“undefined reference”或“multiple definition”等链接错误。同时,与宏相比,内联函数具备类型检查和单次求值的优势,能避免经典宏定义中的副作用陷

weixin_33404102的博客 262
上一篇: Java String 为什么不可变?final 数组、引用重绑和返回原对象的区别
下一篇: 两两交换链表节点:相信递归之前,先看清这两次指针重连
咔_
博客等级 码龄4年 948粉丝 258原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值