9.25
1.头文件
把函数原型放头文件里,在需要调用这个函数的源代码文件(.c)文件里#include这个头文件,就能编译时知道它是原型,来决定函数定义是否符合该头文件,如:(要用就include max.h)
- #include 预处理指令 和宏一样 编译前被处理
- 把文件全部文本内容原封不动插入所在地 so也不一定要在.c文件最前面#include
- include 就是把头文件所在的里头的文本插入include所在的那行
#include两种形式指出要插入的文件
“”:要求编译器首先在当前.c文件所在目录找,若没有则到指定编译器目录找
<>:让编译器只在指定目录找
编译器自己懂自己的标准库的头文件在哪
环境变量和编译器命令行参数也可以指定寻找头文件目录
在使用 定义这个函数的地方都应该#include这个头文件
一般就是任何.c都有对应的同名.h 把所有对外公开的函数原型和全局变量(可在多个.c文件中共享 但要用恰当方式告诉别人该全局变量原型是什么 )的声明都放进去
误区:
#include 不是用来引入库的 stdio.h里只有printf的原型 printf的代码在另外地方
现在C编译器默认会引入所有标准库
不对外公开的函数
在函数前加static就使得它成为只能在所在的编译单元中被使用的函数
在全局变量前加上static就使得它成为只能在所在的编译单元(.c文件)中被使用的全局变量
2.声明
int i;是变量的定义
exturn int i;是变量的声明
exturn=继承(相当于)
声明和定义:
声明可以多次 定义只能一次
- 声明不产生代码 定义产生代码
头文件
- 只有声明可被放在头文件中,是规则不是法律 否则会造成一个项目中多个编译单元里有重名的实体
- *某些编译器允许几个编译单元中存在同名函数 / 用week修饰符强调这种存在(week修饰符:指定日期属于一年中的哪一周)
- 同一编译单元里同名结构不可被重复声明,如果头文件里有结构声明,该头文件容易在一个编译单元里被#include多次,so需要“标准头文件结构”
标准头文件结构:
- 用条件编译和宏,保证该文件在一个编译单元中只会被#include一次 #pragma once 也能起到相同作用,但也不是所有编译器都支持
3.格式化输入和输出:
printf(“%9d\n”, 123); 输出: 123(六个空格123) 9意为输出要占据9个字符的空间
printf(“%-9d\n”, 123); 输出:123(前无空格) -意为左对齐
printf(“%09d\n”,123); 输出:000000123(填充6个0) 0意为用0填充123前面的空位
scanf中 :%d只能读十进制的整数;%i据输入的整数的情况决定到底以哪种形式读进
0–>八进制; x–>十六进制
scanf返回值告诉我们该次读了几个item、变量;
printf返回值告诉我们这次输出了几个字符
类型修饰:
1️⃣整数:hh --> 单字节;
2️⃣h --> short;
3️⃣l --> long;
4️⃣II --> longlong;
5️⃣L --> logdouble.
4.文件的输入和输出
- 用>和<做重定向
打开文件的标准代码:
FILE* fp = fopen("file", "r");
if (fp)
{
fcanf(fp...);
fclose(fp);
}
else
{
...
}
fopen:
r --> 打开只读;
r+ --> 打开读写,从文件头开始读写,一般来修改;
w --> 打开只读,如不存在则新建,如存在则清空;
w+ --> 打开读写,如不存在则新建,如存在则清空;
a --> 打开追加,如不存在则新建,如存在则从文件尾开始;
··x --> 只新建,如文件已存在则不能打开。
5.二进制文件
其实所有文件最终都是二进制
文本文件无非是用最简单的方式可以读写的文件
- more,tail
- cat
- vi
二进制文件需要专门程序读写,文本文件的输入输出是格式化 可能经过转码
6.类型定义
自定义数据类型(typedef)
typedef int length(成为int类型的别名 代替int出现在变量定义和参数声明的地方)
1.Length a,b,len; =int { … }
Length numbers[ 10 ]; =int{ … }
Typedef(声明新类型名)
- 新名字是某类型别名
- 改善程序可读性
Typedef +原类型 新名字
如:
typedef struct node aNode
{
int data;
}aNode;
或
typedef struct aNode(用aNode 代替struct node)
7.联合:
大小端储存, x86(小端机器);
低位在前(低位放在低地址前),反之。
全局变量:
- 定义在函数外部的变量,有全局生存期、作用域;
- 与任何函数无关,在任何函数内部都可以使用它们
本地变量:
- 进该函数时才出现,离开则不存在(但都在函数内部)
全变初始化(发生在main前)
- 没做初始化会得到0值(全变)
- 而指针会得到NULL值
- 只能用编译时刻已知值来初始化全局变量
被隐藏的全局变量:
函数内部存在一个与全局变量同名的变量,则全变会被隐藏
静态本地变量(有全局的生存期 函数内的局部作用 / 本地作用域):特殊全局变量 位于相同内存区域
- 本地变量定义时+static 演示符 就成静态本地变量(函数离开–> 静态本地变量会继续存在并保持其值)
- 初始化:第一次进入函数是做,以后进都保持上次离开的值
static在这意为局部作用域(本地可访问)
全局变量小贴士
*返回指针的函数
- 返回本地变量地址危险,返回全局变量 / 静态本地变量安全
- 返回在函数内malloc的内存安全,但容易造成问题
- best:返回传入的指针
tips:
1.不用全局变量在函数间传递参数和结果
2.尽量避免全局变量
3.*使用全局变量和静态本地变量的函数是多线程不安全的
8.宏
一.定义
#:编译预处理指令(不是C语言的成分,但是C语言离不开它们)
#define 来定义一个宏 <名字> <值>
因为不是C的语句,so结尾没有分号
C在开始编译前,预处理程序(把程序中的程序换成值)
一个宏有其他的名,也会被替换
if一个宏的值超过一行,则最后之前的行末要加\(反斜杠)
宏的值后面出现的注释不会被当作是宏的值的一部分
多行定义宏最好放在一起
二.没有值的宏(如:#define _DEBUG)
- 用于条件编译,后面还有其他编译预处理指令检查该宏是否被定义过
预定义宏:(前后带两下划线)
__ LINE __(代码文件所在行号)
__ FILE __(文件名)
__ DATE __(编译时的日期)
__ TIME __(编译时的时间)
__ STDC __
带参数的宏;
;
像函数的宏:
-
#define cube(x)((x)* (x)**(x)***) 【都只有一个 *】
-
宏可带参数
(一切都要括号!整个值要括号 参数出现的每个地方都要括号)
#define RADTODEG((x)(x)*57.29578)【不加分号】
可带多个参数,也可以组合嵌套使用别的宏 -
大程序中普遍 可以复杂,比如:“产生函数”; 在#和##下,,存在中西文化差异 部分会被inline函数代替,无检查类型不好检查bug
-
main()中代码太长,适合分成几个函数
-
一个源代码文件太长可分为几个文件
-
两独立源代码文件不能编译成可执行文件
项目
- 新建项,把几个源代码文件加入,编译会把一个项目的所有源代码文件都编译后链接起来
9.字符数组
char
字符串:
1️⃣以0结尾的一串字符,0/ '\0’是一样的;
2️⃣0是表示字符串结束,但不是字符串的一部分;
3️⃣计算字符串长度时不包含0;
4️⃣字符串以数组形式存在,以数组或指针形式访问(更多以指针形式)。
string.h里有很多处理字符串的函数
字符串变量,编译器会替你生成一个结尾表示结束(即长度为肉眼可见的1)
要把字符串写成什么形式?
数组:这个字符串在这里(作为本地变量空间会自动被回收)
指针:字符串不知道在哪里(作处理参数,动态分配空间)
常见错误:以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用(✖);
由于没对string初始化为0,so不一定每次运行都出错
空字符串还是有效的“ ”