V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
fantastM
V2EX  ›  C

对 C 语言中变量的声明和定义,可以这样用类比 Java 的方式来理解吗?

  •  
  •   fantastM · 2021-03-03 23:29:57 +08:00 · 1470 次点击
    这是一个创建于 1364 天前的主题,其中的信息可能已经有所发展或是发生改变。

    平时工作过程中主要写 Java,因为对底层比较感兴趣,所以最近在学 C 语言和 Unix 。看 K & R 的过程中总体觉得都满顺畅的,不过因为自身对操作系统和编译原理不怎么了解,也没有 C 项目的实践经验……所以对书中提及的一些细节概念还不确定,想向各位请教一下。

    1. 把函数原型写到后缀名为 .h 的头文件,这种行为是不是类似于 Java 中的定义 interface,可以告诉调用方这些函数的调用方式,然后头文件中声明的函数原型类似于 Java 中的 interface 的方法。
    2. 在 C 语言中,对于外部变量的 static 关键字,可以类比为 Java 中的 private 关键字吧,用于将变量的作用域限制在当前的源文件(在 Java 中是对象)中,避免对全局环境造成影响。
    3. K & R 书中提及「将外部变量的 声明定义 严格区分开来很重要」,还有一个初始化的概念。变量的声明仅是说明变量的类型,不会引起存储器的分配,例如使用 extern 关键字仅是表示它声明的变量是来自于外部源文件中。变量的定义是在变量声明的基础上,还会引起存储器的分配存储单元。然后,变量的初始化和赋值是需要在存储器分配存储单元(也意味着是需要在变量定义)之后才能进行的吧,如果仅是变量声明的话,则无法进行变量的初始化和赋值。在 Java 中 new 对象时候,类的成员字段在没有显式初始化的情况下,会被赋予一个默认值,这样的行为是不是可以理解为 C 语言中变量定义 + 初始化的这两个概念?另外,Java 类的方法中的变量不会被初始化,(下面是一些假设)如果 JVM 是在 int a; 这一步中为变量分配存储单元的话,那么这就应该理解为 C 语言中的变量定义,如果 JVM 是在 a=10; 这一步中为变量分配存储单元的话,而不是 int a; 的话,那么 int a; 就应该理解为 C 语言中的变量声明。对变量声明、定义这两个概念可以这么理解么。
    9 条回复    2021-03-08 14:24:46 +08:00
    ipwx
        1
    ipwx  
       2021-03-03 23:45:34 +08:00   ❤️ 1
    1. 完全错误
    2. 有点不太一样,建议不要用 java 去类比。
    3. 完全不对
    ----

    学新语言大忌:类比。建议好好学习 C 语言。关注重点:C 语言编译过程、.c => .o 是怎么回事,link 这个步骤是什么。然后看看编译出来的 .o 文件和可执行文件的符号表
    Kasumi20
        2
    Kasumi20  
       2021-03-04 01:15:02 +08:00
    写函数原型其实只是因为编译器垃圾,没办法向后查找

    不过头文件还用来定义结构体和类
    yolee599
        3
    yolee599  
       2021-03-04 08:31:41 +08:00 via Android
    C 语言是一个开放性很高的语言,什么写法都有,用 java 无法类比。指针和宏这两个东西很玄学
    BingoXuan
        4
    BingoXuan  
       2021-03-04 09:43:24 +08:00   ❤️ 1
    请配合汇编一起学习,毕竟 C 是好看一点的汇编
    Shazoo
        5
    Shazoo  
       2021-03-04 09:51:20 +08:00
    C 语言的写法其实是直接面向编译器和链接器的。

    像是.h 文件也好,.x 文件也好,include 后,都是等同于预编译阶段将目标文件内容递归拷贝到当前位置。(是的,你可以直接 include .c 文件,很多嵌入式领域直接利用宏来 include 不同 c 文件,用来配置 rom 某处的 payload 。没错,你都可以指定某个巨型数组 link 到绝对地址内。

    所以,C 不存在什么 interface 之类的玄学概念。每次编译,其实就是编译一个完完整整的庞大无比的标准 C 文件。( so,你改动一个很多文件都 include 的头文件,会造成所有 C 文件重编译)

    同理,static 在编译器内的规则很简单,限制不能被其他文件调用即可;在 linker 里面,实际上是根据不同 linker 自己去定义。大致理解为 link 到全局堆内分配。

    C 很有趣,各种魔幻写法,lz 类比 java 有点不合理,java 在 C 程序员眼里才是玄学,各种限制。学 C,类比汇编可能好些。

    可以复习下编译原理和体系结构再学 C 。
    huang119412
        6
    huang119412  
       2021-03-04 10:48:27 +08:00
    K & R 也是 Java 之父高斯林的老师。所以 Java 的风格真的和 C 很像,大括号开头不换行,驼峰,String hash 等等都是来源 K & R 。而现 C 的风格是 ANSI C,和 K & R 并不一样。
    fantastM
        7
    fantastM  
    OP
       2021-03-04 11:26:56 +08:00
    #2 @Kasumi20 原来如此……其实 C 的语法层面容易理解,不过我会多想为什么要这样设计,就容易想偏。

    #5 @Shazoo 感谢指点!学习操作系统 /编译原理之类的底层知识正是我这次学习 C 语言的目的,另外想再请教一下理解「将外部变量的 **声明** 与 **定义** 严格区分开来很重要。变量声明用于说明变量的属性(主要是变量的类型),而变量定义除此以外还将引起存储器的分配」这句话,需要哪些储备知识呢?

    感谢各位回复。
    Shazoo
        8
    Shazoo  
       2021-03-08 09:07:51 +08:00
    @fantastM

    「将外部变量的 **声明** 与 **定义** 严格区分开来很重要。变量声明用于说明变量的属性(主要是变量的类型),而变量定义除此以外还将引起存储器的分配」

    这句话是新手向的教条。实际上,你只需要知道以下:
    某个 var 被定义也好,extern 引用也好,在编译阶段都是无差别的。只是告知编译器,有这么个变量,你知道这货是啥玩意后,tm 别报错。
    在链接阶段,extern 无用,应该有且仅有一个定义。这是因为,链接就需要确定这个变量真实地址位置了。多个的话,自然是不现实的。

    工程上多人配合,一般应用范围广的全局变量都是有个专门的 extern 用的 h,和定义用的 C 。适合 5w 量级的项目。你可以试试,傻瓜化,也好用,不过量级上去了,改动就重编译,也很烦。


    储备知识的话,还是多用多看吧。回想起来,C 也没有特别适合学习的开源代码。要不学习下嵌入式?这领域里面很多不错的 C Project 。
    fantastM
        9
    fantastM  
    OP
       2021-03-08 14:24:46 +08:00
    #8 @Shazoo 感谢回复。我是准备学完 C 之后先看 Redis 的实现。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3594 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 10:51 · PVG 18:51 · LAX 02:51 · JFK 05:51
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.