首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

当我在int main()之前声明函数,而不是在int main()之后声明它时,为什么函数可以工作?

当在int main()之前声明函数时,函数可以工作的原因是因为C/C++编译器在编译源代码时会进行两个阶段的处理:编译和链接。

在编译阶段,编译器会对源代码进行词法分析、语法分析和语义分析等操作,生成目标代码。在这个阶段,编译器会扫描整个源代码文件,识别函数的声明,并生成相应的函数原型。

函数原型包含了函数的名称、参数列表和返回类型等信息,它告诉编译器函数的存在和函数的接口。当编译器在int main()之前遇到函数声明时,它会将函数原型记录下来,以便在后续的编译过程中使用。

在链接阶段,编译器会将所有的目标代码文件合并成一个可执行文件。在这个阶段,编译器会根据函数的原型来检查函数的调用是否正确,并将函数的定义与函数的调用进行关联。

因此,即使函数的定义在int main()之后,编译器仍然可以通过函数的原型来识别函数的存在,并在链接阶段将函数的定义与函数的调用进行关联。这样,当程序执行到函数调用的位置时,编译器就可以根据函数的定义执行相应的代码。

总结起来,函数可以在int main()之前声明而工作,是因为编译器在编译和链接阶段对函数进行了处理,通过函数的原型将函数的定义与函数的调用进行关联,从而实现函数的正常工作。

腾讯云相关产品和产品介绍链接地址:

  • 云函数(Serverless):https://cloud.tencent.com/product/scf
  • 云服务器(CVM):https://cloud.tencent.com/product/cvm
  • 云数据库 MySQL 版(CDB):https://cloud.tencent.com/product/cdb
  • 云原生容器服务(TKE):https://cloud.tencent.com/product/tke
  • 云网络(VPC):https://cloud.tencent.com/product/vpc
  • 云安全中心(SSP):https://cloud.tencent.com/product/ssp
  • 腾讯云音视频处理(MPS):https://cloud.tencent.com/product/mps
  • 人工智能(AI):https://cloud.tencent.com/product/ai
  • 物联网(IoT):https://cloud.tencent.com/product/iotexplorer
  • 移动开发(移动推送、移动分析):https://cloud.tencent.com/product/mobile
  • 云存储(COS):https://cloud.tencent.com/product/cos
  • 区块链(BCS):https://cloud.tencent.com/product/bcs
  • 腾讯云元宇宙(Tencent Cloud Metaverse):https://cloud.tencent.com/solution/metaverse
页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

程序员C语言快速上手——基础篇(五)

函数的好处是可以让编程结构化,不是像早期的程序那样写成一坨。另外函数可以复用代码,这使得程序员可以少写大量的重复代码,还使得大型程序可以模块化,多人同时开发。...大家会发现,这是因为我们自定义的函数写在main函数之后,编译器通常从上往下扫描代码,当扫到printError(),会发现并不认识,有些人会想当然的认为报错是因为编译器不认识该函数产生的,实际上是报的重定义了不同的类型错误...以上示例就是将函数声明与定义分开,实际开发,这些函数声明也并不是像这样直接写到main函数之前的源码中,而是写到头文件中,由于我们还没有讲到头文件,具体内容在后面的部分再说。...这里声明是指显式声明。意即,当自定义的函数被定义main函数之前同时包含了声明与定义。...什么是指针 回答什么是指针之前,我认为应当先提问为什么需要指针?如果没有明确的应当重视的理由,大家何必花大力气学习呢?

90020

为什么C语言会有头文件

前段时间一个刚转到C语言的同事问我,为什么C会多一个头文件,不是像Java和Python那样所有的代码都在源文件中。...然后main文件中定义这个宏,表示main中不包含的实现,但是不管怎么样都需要在add.cpp中加上add函数的定义,否则在调用add函数时会报add函数未定义的变量或者函数 上述写法的窘境 上面只引入一个文件...其实针对所有的变量、类、函数可以都在统一的头文件中声明,但是这么做又带来一个问题,如果我要看的实现怎么办,那么多个文件我不可能一个个的找吧。...编译完成之后进行链接,首先扫描所有的obj文件,先查找main函数,然后根据main函数中代码的执行流程来一一组织代码结构,当碰到之前保留的符号,去所有的obj中的符号表中根据变量符号查找对应的地址,...它们这点区别就解释了为什么C/C++需要声明Python不用。 ----

2.1K50

【C语言】卍字通晓→函数+递归

函数可以提高软件的开发效率,main函数当中调用其它函数这些函数执行完毕被调用函数执行完毕之后又回到main函数当中。通常把这些被调用的函数称为下层函数。...函数调用发生时候,立即执行被调用函数调用者则进入等待状态,直到被调用函数执行完毕。函数可以又参数(void&int)和返回值。 ...隐含数据结构:可以把数据结构的实现细节隐含起来。 隐含指针操作:指针操作可读性很差,而且很容易引发错误。通过把它们独立函数中,可以把注意力集中到操作意图不是集中到的指针操作本身。...形式参数当函数调用完成之后就会自动销毁了。因此形式参数只是函数当中有效!声明周期范围有限。...通过引用传递方式,形参为指向实参地址的指针,当对形参的指向操作,就相当于对实参本身进行的操作。 传递指针可以让多个函数访问指针所引用的对象,不用把对象声明为全局可访问。

72810

【C++修炼之路】1. 初窥门径

因为C++兼容C语言的用法,这些又用得不是很多,因此,当我们需要控制格式以及精度,仍然可以利用printf得形式输出。...先来看看第一行和下面的注释,当给已经const的a定义别名,ra是int类型不是const类型,这代表着权限的放大,因为a的值不能被修改,但这么定义的话就可以通过ra来修改,因此这种定义错误的,正确的方式是不让他的权限放大...debug环境下进行一系列设置: 环境配置完之后看看内联之后汇编的变化: 我们发现,这里直接就是mov和add,并没有Add函数地址的声明,因此,内联函数就是将内部之间展开使用,不是创建栈帧后生成符号表...对于这个问题,上述已经提到过内联之后函数由于不建立栈帧因此不会产生地址,链接的时候声明与此函数定义的符号表中的地址也不会合并,通过之前的文章:程序的编译和链接,我们知道最后链接的过程中,函数定义的地址和函数声明的地址符号表中最后会合并成定义的地址...(因为声明函数的地址不是函数的真正地址),由于内联不产生地址,这最后符号表合并之后的地址也就不是函数定义的地址,因此这样会引发错误。

99300

程序员C语言快速上手——进阶篇(七)

之前我们一直使用别人的头文件,现在自己也来做一份头文件,创建calculate.h文件,并将函数声明都挪到头文件中 1 /* 加法函数 */ 2 int add(int a, int b); 3 4 /...1 gcc t1.c t2.c main.c -o main 这里有几点需要注意 头文件和.c源文件放到一个文件夹下 我们自己本地的头文件,包含应当写英文双引号,不是尖括号 有了头文件以后,我们的声明可以放到头文件中...包含头文件,和""到底有什么区别? 首先回答第一个问题,头文件实际上并不是什么特殊的东西,仅是一个普通的文本文件,它也可以是任意后缀名的文本文件。...当我们的头文件和源文件同一级目录,这时候的头文件路径是以源文件(.c文件)路径为参照物的,因此当使用双引号来包含;当我们的头文件和源文件不在同一级目录下,且使用上述两种方式之一指定了头文件路径,那么使用...之前,我们已经了解过了#include、#define这两个指令,实际上预处理指令并不是C语言词法的一部分,仅仅是写给编译器看的,让编译器正式编译之前,先帮我们做点小事情。

1.2K60

程序员C语言快速上手——高级篇(十)

当我们调用一个函数,被称为函数入栈,指的就是为这个函数栈区中分配内存。 堆(heap)堆内存由程序员手动分配、手动释放,如果不释放,只有当程序运行结束后,操作系统才会去回收这片内存。...区别:所有函数都能访问全局变量,静态变量作用域则只局限于定义函数内部 自动内存 函数声明函数调用时创建(分配在栈中),作用域局限于该函数内部,函数执行完则释放。...可以看到malloc的函数原型 void *malloc(size_t _Size); 返回一个void *类型指针,这是一个无类型或者说是通用类型指针,它可以指向任意类型,因此我们使用它的返回值...可以看到,该函数之所以如此复杂,其目的就是为了保证申请的空间都是一片连续的内存空间,不是碎片化的内存。...当我们不确定指针所指向的具体数据类型,就可以使用void*类型来声明当我们后续确定了具体类型之后,就可以使用强制类型转换来将void*类型转换为我们需要的具体类型。

1.4K30

go基础之--函数和map

整理函数之前先整理一下关于指针 指针 普通类型变量存的就是值,也叫值类型。指针类型存的是地址,即指针的值是一个变量的地址。 一个指针指示值所保存的位置,不是所有的值都有地址,但是所有的变量都有。...当我们理解指针的之后,就可以通过指针的的方法来解决上面的这个问题,将代码更改为: 1 package main 2 3 import ( 4 "fmt" 5 ) 6 7 func...关于函数的可变参数 变长函数被调用的时候可以有可变的参数个数 参数列表最后的类型名称前使用省略号...可以声明一个变长的函数, 例如: 0个或多个参数 func add(arg...int) int{...defer用途: 当函数返回,执行defer语句,因此可以用来做资源清理 多个defer语句,按先进后出的方式执行 defer语句中的变量,defer声明时就决定了 先通过一个小例子理解defer:...() 这样函数结束可以帮我们自动关闭一个打开的文件 Map类型 key-value的数据结构,又叫字典 声明 var map1 map[keytype]valuetype 例子: var a map

52870

哦!这该死的 C 语言!

当我键盘上输入了 ./hello 这几个字符,shell 程序将字符逐一读入寄存器,再把放到内存中,如下图所示 当我键盘上敲击回车键的时候,shell 程序就知道我们已经结束了命令的输入。...但事实却不是这样, main() 方法确实是世界的中心。 C 语言程序一定从 main() 函数开始执行,除了 main() 函数外,你可以随意命名其他函数。...变量声明 我们入门级的代码中,我们声明了一个名为 number 的变量,的类型是 int,这行代码叫做 声明声明是 C 语言最重要的特性之一。...执行 int number ,编译器会在计算机内存中为变量 number 预留空间,然后执行这行赋值表达式语句,把值存储之前预留的位置。...C 语言的特性,C 语言为什么这么火,C 语言的重要性,之后我们以一道 C 语言的入门程序讲起,我们讲了 C 语言的基本构成要素,C 语言硬件上是如何运行的,C 语言的编译过程和执行过程等,在这之后我们又加深讲解了一下入门例子程序的组成特征

48320

仙人指路,引而不发,Go lang1.18入门精炼教程,由白丁入鸿儒,Golang中New和Make函数的使用背景和区别EP16

因为值类型的零值是一个具体的值,不是nil,比如整形的零值是0,字符串的零值是空,空不是nil,所以就算是空,也可以赋值。     那引用类型就没法赋值了?...a) fmt.Println(&a) }     系统返回: ok 0x14000126018     这里我们使用了new函数正是用于分配内存,第一个参数接收一个类型不是一个值,函数返回一个指向该类型内存地址的指针...换句话说,new函数可以帮我们做之前系统自动为值类型数据类型做的事。    ...这显然有些矛盾了,既然已经有了new函数,并且new函数可以为引用数据类型分配内存,切片、字典和通道不也是引用类型吗?     大家既然都是引用类型,为什么不直接使用new函数呢?...事实上,这是一个分配内存的时机问题,声明之后,没有任何规定必须要立刻赋值,赋值后的变量会消耗系统的内存资源,所以声明以后并不分配内存,而是适当的时候再分配,这也是new和make的意义所在,所谓千石之弓

24220

实例说明

开头的#,这个符号表明这一行是在编译器接手之前先由C预处理器处理的语句。以后我们将碰到更多的预处理指令。 二、主函数 int main() 接下来的代码声明了一个main函数。...每个C程序中必须有! 那么圆括号的功能呢?表明main()是一个函数。以后,我们将遇到更多的函数。现在,请记住这个函数就是C程序的基本模块。 int指明main()函数的返回类型。...然而,首次使用变量之前仍然必须先声明。...当程序运行到这一行,控制权将转给该函数(在这个例子中是 printf())。当函数完成了它所要做的工作,将控制权返回给原来的函数(调用函数),在这个例子中是 main()。...当键入 printf()这个参数为什么不直接按回车键呢?因为那将看作是直接针对编辑器的命令,不是作为存在源代码中的指令。也就是说,当你按回车键,编辑器退出你正在输入的当前行,并开始新的一行。

1.4K80

C语言的灵魂——指针

解释: main()是主调函数,Increment()是被调函数当我主调函数中调用其他函数,这个参数叫做实参,这个被调函数的参数叫做形参,实参会被映射到形参,当这个函数被调用的时候,主函数中的实参...这里我们不是拷贝变量的值,仅仅拷贝了一个变量的地址,所以这里是传(址)引用,这个很有意义,因为数组可以很大, 每次拷贝整个数组没有意义,他会消耗大量的内存,一次对于数组来说不使用传值引用,而是传(址)...(c);//sizeof的结果就是5,刚好同来存放ABCDstrlen(c);//长度是4,不包括null 如果我们这么声明,代码如下,这时我们需要显式的声明的结束。...void *p = (int *)calloc(3,sizeof(int)) 还有一个区别是,calloc分配完内存之后会对其进行初始化,malloc不会。...之后为Print函数分配栈空间,覆盖之前的空间。

87010

C语言函数指针基础

当我main函数中调用它的时候,向屏幕输出出”hello world“。非常简单。接下来,我们改写一下main函数之前直接调用的sayHello函数,现在改用函数指针来调用它。...关于函数名的更多细节我们会在下文中讨论,现在暂时可以将其看作一个标签,代表函数的地址,并且可以赋值给函数指针。...接着看第三行,我们用代码’(*sayHelloPtr)();·‘解引用并调用了函数指针。 第二行被声明之后,sayHelloPtr作为函数指针的名称,跟其他任何指针没有差别,能够储值和赋值。...我之前提到过,函数指针被要求当作输入的地方,就能够使用函数名。 第16行,解引用符作用于add之前,即*add,返回在这个地址的函数之后函数名一样,它被隐式的转换为一个函数指针。...第17行,取地址符作用于add之前,即&add,返回这个函数的地址,之后又得到一个函数指针。 18到19行,add不断地解引用自身,不断返回函数名,并被转换为函数指针。

1.5K100

记64位地址截断引发的挂死问题

32位和64位代码区别 分享之前,需要了解一下32位和64位程序代码有何区别,的主要区别体现在某些数据类型的占用字节大小的不同: 数据类型 32位 64位 long 4字节 8字节 unsigned...但是跟踪到动态库接口内部,发现返回的结果是正常的8字节地址值,排除定时器接口的问题 最终可以确定,调用动态库接口,虽然返回的是8字节地址,但是赋给外部变量,就被截断了 换项目中的另外一个进程调试demo...发现,编译出现错误,提示函数没有声明 于是加上声明之后编译通过,但并没有出现挂死的问题 随即继续跟踪原项目出问题的进程,发现同样这些接口都没有外部声明,再加上另外一个进程的警告信息,提示有int往指针强转...尝试从整形转换成指针 第一个警告很容易理解,虽然定义了testFun函数,但是main函数中并没有声明。...本文总结如下: 不要忽略任何一个警告,除非你非常清楚地知道自己在做什么 头文件中声明函数,并提供给调用者 函数使用前进行声明 问题长期定位不出来时,休息一下 尽量编写通用性代码 非必要不强转 使用void

84220

C语言编程语法—语法风格

main函数了,说到底它就是一个函数而已,仅仅因为地位特殊拥有第一执行权力,换句话说,难道因为一个人是省长它就不是人类了?...'关键字,也能被编译器默认为内联函数,但之后带来的某些并发问题就不是编译器考虑的了。...,自己编写函数的时候请注意在开头(main函数之前)写上你的函数的原型,并且末尾(main函数之后)写上你的函数定义,这是一个很好的习惯以及规范。...能发明这种写法的人,必定是了解了,C语言中,如果一个函数不显式声明自己的返回值,那么会被缺省认为是int,但这一步是由编译器掌控,然而C语言设计之初便是让我们对一切尽可能的掌握,一切不确定因子我们都不应该让存在...还有许许多多C于C++不相同的地方,兴许有人说C++是C的超集,但我并不这么认为,一门语言的出现便有的意义所在,关键在于我们如何发挥的最大优势,不是通过混淆概念来增强实用性。

14930

Scala学习笔记(一)

一、变量 获取变量的值是一个耗时的工作可以考虑使用lazy var. lazy val forLater = someTimeConsumingOperation() 二、函数定义 ?...迭代Array的操作是非常简单的,我们只需要使用它的foreach方法,同时传递一个函数字面量即可。下面典型的例子是迭代main函数的args参数列表: ? 五、FOR循环 ? ?...这意味着初始化之后,你将无法再修改的值。...声明的那个函数!...对于重载构造函数的第一个语句必须是调用另外一个重载的构造函数或者是主构造函数!当然除了主构造函数以外!这个表述如果再深入地一想,那么我们就可以想到:所有的构造函数一开始就会首先调用主函数!!

60710

EasyC++70,构造函数的一些坑

这意味着我们声明当中没有为字符串本身分配空间,而是构造函数当中使用new来完成的,避免了预先定义字符串的长度。...因为声明只是描述了如何分配内存,但并不真的分配内存。 所以对于静态类成员,我们可以声明之外使用单独的语句进行初始化。因为静态成员变量是单独存储的,并不是对象的一部分。...初始化要在方法文件也就是cpp文件当中,不是头文件中。因为头文件可能会被引入多次,如果在头文件中初始化将会引起错误。当然也有一种例外,就是加上了const关键字。...: 从屏幕可以看到我们的析构函数执行了两次,一次很好理解应该是main函数退出的时候自动执行的,还有一次呢?...等价于: StringBad st = StringBad(sb); 对应的构造函数原型是: StringBad(const StringBad&); 当我们用一个对象来初始化另外一个对象的时候,

15520

Kotlin —— 这次入门就不用放弃了

再也不用findViewById 做过Android开发的人都知道,布局文件写的多了,findViewById也是一个很大的工作量,而且还要先声明变量,findViewById然后再强转成我们的控件,...,可能有些人说现在不是有一些注解的库,如butterknife,当我们使用注解可以不用findViewById了,使用方式如下 @BindView(R.id.user) TextView username...: Int): Int = (value * resources.displayMetrics.density).toInt() 就如我们之前说的toast、text也是拓展函数一样 inline fun...没有命名空间 Kotlin 允许你文件中定义顶级的函数和属性,但是这会带来困扰——所有从 Kotlin 引用的顶级声明无法区分。这让我们有时候在读代码很难快速确定用的是哪一个函数。...重写期间没有发生大的架构更改,因此重写之前之后测试编译时间应该很好地了解Java和Kotlin之间的构建时间的差异。我写了一个shell来重复执行gradle。所有测试连续进行10次。

1.6K30

【Modern C++】深入理解移动语义

其实就是左值引用,比如: int a = 1; int &b = a; C++11之前,我们通过会说b是对a的一个引用(当然,C++11及以后也可以这么说,大家潜移默化的认识就是引用==左值引用)...移动语义 移动语义是Howard Hinnant2002年向C++标准委员会提议的,引用其移动语义提案上的一句话: 移动语义不是试图取代复制语义,也不是以任何方式破坏。...当进行值传递,编译器会隐式调用拷贝构造函数;自C++11起,通过右值引用来避免由于拷贝调用导致的性能损失。...但是,移动构造函数可以避免内存重新分配,这是因为移动构造函数的参数是一个右值引用,也可以说是一个临时对象,临时对象调用之后就被销毁不再被使用,因此,移动构造函数中对参数进行移动不是拷贝。...同样的,如果你声明拷贝赋值运算符但是没有拷贝构造函数,代码用到拷贝构造函数编译器就会生成。上述规则在C++98和C++11中都成立。 两个移动操作不是相互独立的。

76710

类A的成员函数做类B的友元函数

不是类或命名空间: 因为 ManagerPoint 类是 Point 类之后声明定义的,所以他找不到,这样我们需要将 ManagerPoint 类的声明和定义移动到 Point 类之前。...类中使用了Point类,很明显,ManagerPoint之前并没有声明或定义Point类,那么我们可以使用一种手段叫做“前向声明”的方式,将 Point 类声明 ManagerPoint 类之前。...不,还没那么简单,这样编译后,又出现了新问题,如下图: 编译器提示,Point是一个未定义的类,因为我们直接操作了Point类中的_x和_y成员,而我们之前只给出了Point类的一个前向声明,并没有告诉...换位思考一下,当我们将前 Point 类做了前向声明后,ManagerPoint类中的 distance() 函数已经可以识别 Point 这个类型了,也就是说,如果我们这里只做 distance()...函数声明,而把distance() 函数的实现放到 Point 类的后面,是不是可以避免以上所有遇到的问题了呢?

13740

C++基础闯关100题,你能闯多少?【2021超硬核大厂高频面试题】

2、main执行之前之后执行的代码可能是什么?...等传递给main函数,然后才真正运行main函数 main函数执行之后: 全局对象的析构函数会在main函数之后执行 可以用 atexit 注册一个函数,它会在main 之后执行 3、C...,但不是同一个变量,函数中改变这个变量的指向不影响实参,引用却可以。...模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问; 模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制声明的模块内; 类中的...将析构函数声明为虚函数实现多态,当用基类操作派生类,析构防止只析构基类不析构派生类的状况发生,要将基类的析构函数声明为虚函数

1.9K20
领券