C语言(Head First C)-5_1:使用多个源文件:数据类型和使用头文件声明函数

2023-11-12

该系列文章系个人读书笔记及总结性内容,任何组织和个人不得转载进行商业活动!

 5_1 使用多个源文件:

 数据类型和头文件;


 大程序不等于大源文件:

 只有一个源文件的话,维护耗时且困难;如何把源文件分解为易于管理的小模块,然后合成一个大程序,正是本章的内容;

 同时还将了解数据类型的更多细节,并结识一个新朋友make;

 

 数据类型:

 C语言可以处理不同的数据类型:字符、整数、浮点数(分为普通浮点数和科学计算用的高精度浮点数);

 浮点型:float double;(包含小数点)

 整型:short int long char;(你没看错,char以字符编码保存,换句话说,他们也是数字)

 

 简明数据类型指南:

 char:

 字符在计算机的存储器中以字符编码的形式保存,字符编码就是一个数字,在计算机看来,A和65完全一样;(65是A的ASCII码)

 

 int:

 存储整数,不同计算机中的int大小不同,但至少要有16位,一般而言,int可以保存几万以内的数字;

 

 short:

 有时想节省一点空间,存储小一点的证书,可以用short,它一般只有int一半的大小;

 

 long:

 保存一个很大的数字,一些计算机中long是int的两倍,可以保存几十亿以内的数字;大部分计算机的long和int一样大,因为这些计算机中int本身就很大;

 

 float:

 保存浮点数的基本数据类型;

 

 double:

 表示很精确的浮点数;double比float多占一倍的空间,可以保存更大、更精确的数字;

 

 勿以杯小盛大物:

 赋值时要保证值的类型与保存他的变量类型相匹配;

 不同数据类型的大小不同,千万别让值的大小超过变量;(short < int < long)

 

 可以用int装short,反过来不行(编译器可能不会报错,实际会让值面目全非);

 可以使用%hi来格式化short类型值;

 

 使用类型转换把float值存进整数变量:

 两个整数相除,结果是一个四舍五入的整数;如果希望结果是浮点数,可以使用类型转换 临时转换数值的类型;

 float z = (float)x;//(float)会把int值转换为float值;

 编译器如果发现浮点数与整数的运算,会自动把整数进行类型转换,以减少代码中类型装换的次数;

 

 可以在数据类型之前加几个关键字,来改变数值的意义:

 unsigned:

 修饰非负数的值,由于无需记录负数,无符号数有更多的位可以使用,可以保存更大的数(0~max,max是int可以保存最大值的两倍左右);

 还有signed关键字,没见过对吧,因为所有数据类型默认都是有符号的;

 unsigned char c;//保存0-255的数;(2^8-1)

 

 long:

 没错,你可以在数据类型前加long,让他变长;long int;long long;还可以对浮点数用long,long double;

 只有C99和C11标准支持long long;

 

 示例:

 帮助餐厅服务员更好滴进行服务,代码自动计算总账,并为每笔消费收取消费税;

 (Code5_1)

/*
 * 数据类型
 */

#include <stdio.h>

float total = 0.0;
short count = 0;
short tax_persent = 6;

float add_with_tax(float f){
    float tax_rate = 1 + tax_persent/100.0;
    total = total + (f * tax_rate);
    count = count + 1;
    return total;
}

int main() {
    float val;
    printf("Price of items:");
    while (scanf("%f",&val) == 1) {
        printf("Total so far:%.2f\n",add_with_tax(val));
        printf("Price of items:");
    }
    
    printf("\nFinal total:%.2f\n",total);
    printf("Number of item:%hi\n",count);
    return 0;
}

 log:

 Price of items:5

 Total so far:5.30

 Price of items:6

 Total so far:11.66

 Price of items:112

 Total so far:130.38

 Price of items:7

 Total so far:137.80

 Price of items:423

 Total so far:586.18

 Price of items:4566

 Total so far:5426.14

 Price of items:23

 Total so far:5450.52

 Price of items:

 wer

 

 Final total:5450.52

 Number of item:7

 

 聚焦数据类型大小:

 不同平台数据类型的大小不同,那怎么知道int或double占多少字节?

 C标准库提供了这些细节:下面这段代码,告诉你int与float的大小;

 (Code5_2)

/*
 * int float的大小
 */

#include <stdio.h>
#include <limits.h>//含有表示整数大小的值(int 和 char)
#include <float.h>//含有表示浮点数大小的值(float 和 double)

int main() {
    
    printf("int:");
    printf("The value of INT_MAX is %i\n",INT_MAX);
    printf("The value of INT_MIN is %i\n",INT_MIN);
    printf("An int takes %lu bytes\n",sizeof(int));
    
    printf("char:");
    printf("The value of CHAR_MAX is %i\n",CHAR_MAX);
    printf("The value of CHAR_MIN is %i\n",CHAR_MIN);
    printf("An int takes %lu bytes\n",sizeof(char));
    
    printf("short:");
    printf("The value of SHRT_MAX is %i\n",SHRT_MAX);
    printf("The value of SHRT_MIN is %i\n",SHRT_MIN);
    printf("An int takes %lu bytes\n",sizeof(short));
    
    printf("long:");
    printf("The value of LONG_MAX is %li\n",LONG_MAX);
    printf("The value of LONG_MIN is %li\n",LONG_MIN);
    printf("An int takes %lu bytes\n",sizeof(long));
    
    printf("float:");
    printf("The value of FLT_MAX is %f\n",FLT_MAX);
    printf("The value of FLT_MIN is %f\n",FLT_MIN);
    
    printf("An int takes %lu bytes\n",sizeof(float));
    
    
    return 0;
}

 log:

 int:The value of INT_MAX is 2147483647

 The value of INT_MIN is -2147483648

 An int takes 4 bytes

 char:The value of CHAR_MAX is 127

 The value of CHAR_MIN is -128

 An int takes 1 bytes

 short:The value of SHRT_MAX is 32767

 The value of SHRT_MIN is -32768

 An int takes 2 bytes

 long:The value of LONG_MAX is 9223372036854775807

 The value of LONG_MIN is -9223372036854775808

 An int takes 8 bytes

 float:The value of FLT_MAX is 340282346638528859811704183484516925440.000000

 The value of FLT_MIN is 0.000000

 An int takes 4 bytes

 

 不同计算机上的结果可能不同;如果想知道double可以使用DBL替换INT即可;相关的常量值可以百度搜索相应的头文件:

 limits.h:含有表示整数大小的值(int和char)

 float.h:含有表示浮点数大小的值(float和double)

 

 为了适应硬件,C语言在不同操作系统与处理器上使用不同的数据类型的大小;(C语言并没有指定数据类型的大小)

 8位、64位中的位数既可以代表CPU指令的长度,也可以代表CPU依次从从存储器读取数据的大小;实际上,位数是计算机能够处理的数值长度;如果一台计算机能处理32位数值,就会把基本数据类型(如int)的大小设置成32位;

 

 函数声明:

 这里对调用的函数声明位置做一下调整:

 Code5_1代码中将函数声明移到main()函数之后,我们重新编译运行;

 log:

 5_1-numeric.c:18:38: warning: implicit declaration of function 'add_with_tax' is

 invalid in C99 [-Wimplicit-function-declaration]

 printf("Total so far:%.2f\n",add_with_tax(val));

 ^

 5_1-numeric.c:18:38: warning: format specifies type 'double' but the argument

 has type 'int' [-Wformat]

 printf("Total so far:%.2f\n",add_with_tax(val));

 ~~~~    ^~~~~~~~~~~~~~~~~

 %.2d

 5_1-numeric.c:26:7: error: conflicting types for 'add_with_tax'

 float add_with_tax(float f){

 ^

 5_1-numeric.c:18:38: note: previous implicit declaration is here

 printf("Total so far:%.2f\n",add_with_tax(val));

 ^

 2 warnings and 1 error generated.

 

 显然,编译器报错了;

 

 编译器做了什么?

 1)printf("Total so far:%.2f\n",add_with_tax(val));

 编译器看到了一个不认识的函数调用,先记下,随后在文件中查找该函数;

 2)编译器需要知道函数的返回类型,但现在还不知道,所以只好假设它返回int;

 3)等编译器看到实际的函数,返回了error: conflicting types for 'add_with_tax'错误;因为编译器认为有两个同名的函数,一个是文件中的函数,一个是编译器假设返回int的那个;

 

 如果把函数的位置放到main()函数调用ta之前定义,编译器就不用假设未知函数的返回类型了;但如果总是以特定的顺序定义函数,会有一些问题:

 1)调整函数的顺序很痛苦:如果新添加一个函数中调用了已有函数,那就需要把被调用函数的位置提前;

 最后又一个两全其美的方法,既不用交换代码顺序,又能让编译器高兴;

 2)在某些场景中,没有正确的顺序:比如两个相互递归调用的函数,func1中调用func2,func2中调用func1,这样总会有一个函数在定义前被调用;

 

 那么有什么办法呢?

 

 声明与定义分离:

 如果编译器一开始就知道函数的返回类型,就不用稍后再找了;为防止编译器假设函数的返回类型,我们可以显示的告诉他;

 告诉编译器函数会返回什么类型的语句就叫函数声明;

 如:float add_with_tax(float f);

 

 声明只是一个函数签名:一条包含函数名、形参类型与返回类型的记录;

 一旦声明函数,编译器就不需要假设了,可以先调用,再定义;

 

 如果代码中有很多函数,又不想管他们的顺序,就可以在代码开头列出函数声明;

 甚至,可以把这些声明拿到代码外,放到一个头文件中,你已经用头文件包含过C标准库中的代码;

 #include <stdio.h>包含一个叫stdio.h的头文件的内容;

 

 创建自己的头文件:

 两件事;

 1)创建一个扩展名为5_3-totaller.h的文件;并在其中写上函数声明;

 不用写main()函数,反正也没有函数调用他;

 (Code5_3)

float add_with_tax(float f);
    

 2)在主代码中包含头文件;使用include "5_3-totaller.h"

 我们创建一个主代码文件;

 (Code5_4)

/*
 * 数据类型
 */

#include <stdio.h>

#include "5_3-totaller.h"

float total = 0.0;
short count = 0;
short tax_persent = 6;

int main() {
    float val;
    printf("Price of items:");
    while (scanf("%f",&val) == 1) {
        printf("Total so far:%.2f\n",add_with_tax(val));
        printf("Price of items:");
    }
    
    printf("\nFinal total:%.2f\n",total);
    printf("Number of item:%hi\n",count);
    return 0;
}

float add_with_tax(float f){
    float tax_rate = 1 + tax_persent/100.0;
    total = total + (f * tax_rate);
    count = count + 1;
    return total;
}

 头文件的名字使用双引号括起来,注意不是尖括号;

 区别在于:

 当编译器看到尖括号,就会到标准库代码所在目录查找头文件,但现在你的头文件和.c文件在同一目录下,用引号把文件名括起来,编译器就会在本地查找文件;

 当编译器在代码中,读到#include,就会读取头文件中的内容,仿佛他们本来就在代码中;

 本地头文件也可以带有目录名,但通常会把它和c文件放在相同目录下;

 

 把声明放到一个独立的头文件中有两个有点:一个就是代码变短了,第二个稍后会看到;

 #include 是预处理命令;

 

 现在我们重新编译运行一下上边这段主代码:代码正常运行了;

 

 小结:

 1)函数定义在调用之后,需要提前声明;

 2)如果未声明的函数返回值是int行,可编译通过,但会收到一条警告;

 3)如果没有声明返回值不为int的函数,编译无法通过;

 4)只有使用gcc编译器的-std=c99选项编译后才会有该警告;

 

 预处理:

 预处理是把C源代码转化为可执行文件的第一个阶段;预处理会在正式编译开始之前修改代码,“创建”一个新的源文件;以我们的代码为例,预处理会读取头文件中的内容,插入主文件;这里的创建并不是真正的创建一个文件,为了提高编译效率,编译器通常会用到管道,在两个阶段之间发送数据;

 gcc会预处理和编译代码;

 

 区别用尖括号或引号括起来的头文件:

 这是由于编译器的工作方式决定的;通常,引号表示以相对路径查找头文件,如果不加目录名,只包含一个文件名,编译器就会在当前目录下查找头文件;如果使用尖括号,编译器就会以绝对路径查找头文件;gcc知道标准库的头文件保存的目录;

 我们也可以创建自己的库,后续会介绍;

 

 要点:

 -如果编译器发现你调用了一个他没见过的函数,就会假设这个函数返回int;

 -所以如果想在定义函数前就调用它,就可能出问题;

 -函数声明在定义函数前就告诉编译器函数长什么样子;

 -如果在源代码顶端声明了函数,编译器就知道函数返回什么类型;

 -函数声明通常放在头文件中;

 -可以用#include让编译器从头文件中读取内容;

 -编译器会把包含进来的代码看成源文件的一部分;



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言(Head First C)-5_1:使用多个源文件:数据类型和使用头文件声明函数 的相关文章

  • 自定义可视化 Web 部件属性 sharepoint

    我在 Visual Studio 2012 中创建可视 Web 部件属性时遇到问题 我被提及http msdn microsoft com en us library ee231551 aspx http msdn microsoft co
  • VBA 中的 VSTO:AddIn.Object 有时不返回任何内容 (null)

    Given VSTO 插件 An override object RequestComAddInAutomationService 它返回一个名为的类的实例Facade在我的场景中 Excel 2007 中的 VBA 宏可访问AddIn O
  • 多个源文件中包含包含“const”的头文件

    Why does not包含定义的头文件const并被多个源文件包含会产生编译错误multiple definition const in header file h const int num 5 int x Error Multiple
  • Reflection.Emit 中的短格式操作码错误

    我正在制作一种与以下非常相似的小语言hlsl但仅支持像素着色器 该语言使用reflection emit构建实现相同功能的 NET 程序集 我目前正在测试分支指令的实现if在我的一个单元测试中 一个大的if与内if elses 失败并显示以
  • 在 C 程序中追踪数组越界访问/写入的推荐方法

    考虑用 C 语言编写一些不太明显的算法的实现 例如 让它成为递归快速排序 我在 K N King 的 C 编程 现代方法 第二版 书中找到了它 可以从here http knking com books c2 programs qsort
  • c++11 中的 std::thread 问题

    我在尝试从标准模板库编译具有多线程的程序时遇到一些麻烦 当我尝试编译以下程序时 它返回一个晦涩的错误 include
  • 是否可以用 C# 为 Android 编写应用程序?

    我们都知道Android运行Dalvik VM程序 通常开发人员用 Java 编写程序并将其编译为 Dalvik 字节码 我想知道是否有可能创建一个可以接受 C 代码并将其编译为 Dalvik 字节码的编译器 嗯 这是一种选择 或者您可以在
  • 我应该使用函数还是无状态函子?

    这两段代码做同样的事情 如您所见 它将用于排序函数 哪个更好 我通常写后一种 但我看到一些程序员像以前那样做 struct val lessthan binary function
  • popen2()在c中如何工作?

    我尝试使用管道 叉子和 dup 在我的程序中执行 md5sume 命令 我发现总和代码运行成功 但我无法理解某些代码行 这是我的代码 int infp outfp char buf 128 if popen2 md5sum infp out
  • Xamarin 无法从异步获取实例

    我编写了一个通过蓝牙连接到 ESP32 的 Xamarin Forms 应用程序 现在我想从 MainPage xaml 页面的 CustomControl JoystickControl 获取值 我已经这样尝试过了 MainPage xa
  • 未定义条件编译符号

    我无法让 Visual Studio 按照我的预期运行 我创建了 2 个配置文件 一个定义了符号 FOO 另一个定义了符号 BAR 我有这个代码 static class MyClass if FOO public static strin
  • NHibernate 中具有不同类型答案的问题

    我正在尝试找到一个问卷问题的简洁解决方案 假设我有一个Questionnaire类有一个集合Answers e g public class Questionnaire public virtual ISet
  • 如何在 Xamarin.Mac 中执行终端命令并读入其输出

    我们正在编写一个 Xamarin Mac 应用程序 我们需要执行像 uptime 这样的命令 并将其输出读取到应用程序中进行解析 这可以做到吗 在 Swift 和 Objective C 中都有 NTask 但我似乎无法在 C 中找到任何示
  • 我可以在C中直接比较int和size_t吗?

    我可以比较一个int and a size t像这样的变量 int i 1 size t y 2 if i y Do something 或者我必须输入其中之一 只要满足以下条件 它就是安全的int为零或正数 如果它是负数 并且size t
  • gcc 中的“假设”子句

    gcc 最新版本 4 8 4 9 是否有类似于以下的 假设 子句 assume 内置icc支持吗 例如 assume n 8 0 从 gcc 4 8 2 开始 gcc 中没有 assume 的等效项 我不知道为什么 这会非常有用 马夫索建议
  • 在同一条线上铸造两次

    我在项目中看到了这段代码 b的类型是void void b int a int unsigned long b 这条线毫无意义吗 我的意思是 这与a int b在所有情况下 这可能会避免 64 位 Unix 系统上的编译器警告unsigne
  • 什么是多重重继承?

    我将以下称为 多重重新继承 直接继承一个类一次 并通过继承其一个或多个后代来间接继承一次或多次 通过继承一个类的两个或多个后代来间接继承一个类两次或多次 我想知道它是否存在以及如何明确访问嵌入的子对象 1 Professional C 2n
  • 在 WPF 树视图中获取 FullPath?

    如果我以编程方式创建 WPF TreeView 例如 TreeView treeView lt added in the designer TreeViewItem rootNode new TreeViewItem rootNode He
  • 使用 wmi 获取活动会话(Win32_LogonSession 还返回非活动/旧会话)

    有没有办法只显示 wmi 的活动会话 问题是 Win32 LogonSession 还显示不活动 断开连接的会话 ManagementScope scope new ManagementScope ManagementPath Defaul
  • C++20 范围太多 |运营商?

    我在这段代码中使用 g 10 2 有谁知道为什么我最后收到编译器错误std views reverse on results3 include

随机推荐

  • 不用==用equals引发的一系列思考

    毕业刚工作的时候 带我的小师傅给我说判断相等使用equals不要使用 第一次写java的我照办了 但是不清楚为什么 之后还是决定真正了解一下java本身 于是开始看jdk源码 这里以Integer为例 equal public boolea
  • 【Python】多分类算法—Random Forest

    Python 多分类算法 Random Forest 本文将主要就Random Forest 随机森林 的多分类应用进行描述 当然也可运用于二分类中 本文运用scikit learn框架 文章目录 Python 多分类算法 Random F
  • docker私有仓库

    一 docker私有仓库 1 拉取 docker pull registry 2 创建启动容器 docker run id name registry p 5000 5000 registry 3 浏览器输入地址 http ip 5000
  • 【Java】操作Sqlite数据库

    首先在https github com xerial sqlite jdbc下载jar包 import java sql Connection import java sql DriverManager import java sql Re
  • 01Linux常用指令

    所有的指令不要强制记忆 选项要多多查一下 多多练习使用即可 文章目录 一 Linux简单介绍 1 1 Linux的目录结构 1 2 常见的具体目录结构 bin sbin root lib etc usr boot tmp dev media
  • 从字符串中取出指定位置的字符

    首先肯定返回一个char类型
  • excel编写的测试用例转成xmind格式

    软件开发过程中 测试工程师必不可少的一项工作就是编写测试用例 进行测试评审 设计评审 用例评审 写用例常用的几种方法无非就是用excel写 或者xmind这种思维导图形式 而xmind具有条理清晰的特点 所以测试评审时为了提高效率 很多团队
  • 2023华为od机试 Python【不包含回文串】

    前言 本题使用python解答 如果需要Java版本代码 请参考 点我 题目 什么是回文串呢 就是将原字符串翻转过来 和原始字符串一样的字符串 我们现在有一个不包含回文串的字符串 并且 字符串的字符在英语字母的前N个 且字符串不包含任何长度
  • 【网络游戏同步技术】帧同步的一致性

    参考博文 GAD 网络游戏同步技术 引言 帧同步的形式很泛 根据不同游戏 使用的技术范围又不一样 所以大家都在讲方法论 要全面覆盖可能需要较大的篇幅 所以 我简单描述下 假定大家对帧同步和状态同步有一定的认识 理论上的问题 我就不作过多解释
  • 从pandas dataframe中随机删除n个某一列是某个值的元素

    从pandas dataframe中随机删除n个某一列是某个值的元素 import pandas as pd 创建示例 DataFrame data A 1 2 3 4 5 6 B 6 7 8 9 10 11 C X Y Z X Y X d
  • 基本排序算法(直接排序,选择排序,冒泡排序)

    一 直接排序 思路 首先需要两个嵌套的for循环 外层for循环控制轮数 内层for循环控制每轮比较的次数 这里来演示一下遍历的过程 第一轮 首先让i指向数组的首部 让j指向i的后一个元素 两者比较 2比1大 所以交换2跟1的位置 然后j后
  • git proxy

    git config global https proxy http 127 0 0 1 7890 git config global https proxy https 127 0 0 1 7890
  • tar命令的详细解释

    tar命令 root linux tar cxtzjvfpPN 文件与目录 参数 c 建立一个压缩文件的参数指令 create 的意思 x 解开一个压缩文件的参数指令 t 查看 tarfile 里面的文件 特别注意 在参数的下达中 c x
  • CSS 水平居中

    1 若元素内容为文字时 元素设置text align center text align属性指定元素文本的水平对齐方式 center 把文本排列到中间 p 这里是文本内容 p 2 父子元素宽度固定 父元素设置text align cente
  • SQL实用功能手册

    SQL实用功能手册 SQL基础复习 SQL结构化查询语言 是一种访问和处理数据库的计算机语言 对数据库操作 对表操作 对数据进行CRUD操作 操作视图 存储过程 索引 环境基础操作 安装mysql 启动mysql 配置环境变量 检查mysq
  • 搞懂java类加载机制和类加载器

    搞懂java类加载机制和类加载器 类加载概述 一个类从被加载到虚拟机内存中开始 到卸载出内存为止 它的整个生命周期将会经历加载 验证 准备 解析 初始化 使用和卸载七个阶段 其中验证 准备 解析三个部分统称为连接 如下图所示 其中加载 验证
  • Android学习——Adapter适配器

    AdapterView 容器控件 其整体效果由每一个子元素内容决定 子元素的形式由Adapter决定 AdapterView的子视图对象 ListView 以垂直滑动列表形式显示一组数据 GridView 以网格形式显示一组数据 Spinn
  • GPUView的使用

    本文翻译自GPUView的开发者Matt的blog https graphics stanford edu mdfisher GPUView html GPUview可以在 https docs microsoft com en us wi
  • 10个经典的C语言面试基础算法及代码

    本文是码农网原创整理 转载请看清文末的转载要求 谢谢合作 算法是一个程序和软件的灵魂 作为一名优秀的程序员 只有对一些基础的算法有着全面的掌握 才会在设计程序和编写代码的过程中显得得心应手 本文是近百个C语言算法系列的第二篇 包括了经典的F
  • C语言(Head First C)-5_1:使用多个源文件:数据类型和使用头文件声明函数

    该系列文章系个人读书笔记及总结性内容 任何组织和个人不得转载进行商业活动 5 1 使用多个源文件 数据类型和头文件 大程序不等于大源文件 只有一个源文件的话 维护耗时且困难 如何把源文件分解为易于管理的小模块 然后合成一个大程序 正是本章的