一.前言
导图
- 本文首要对上一篇文章的精简归纳,首要分上面三个大部分
- 进阶篇暂不包含线程、进程、网络篇,计划单独写
上一篇传送
言语篇—现代C记载 –
学而时习之,不亦说乎
二.根本篇
2.1 HelloWorld
根本结构: 指令、函数、句子、显现、注释
#include <stdio.h>
int main(int argc, char**argv) {
printf("Hello, World!\n");
return 0;
}
编译输出:gcc/cc -o <outputfile> <sourcefile> [-w]
$ gcc hello.c
$ ./a.out
Hello, World!
2.2 类型
2.2.1 根本类型
数值&字符
根本类型 | 表述 | 一般读写格局 |
---|---|---|
short、int、long、long long | 有符号整数,最左边位为0代表正数,1代表负数 | %<h\l><d\o\u\x> |
在上面前加unsigned | 无符号整数 | |
float | 单精度浮点数 | %f , %g , %e |
double | 双精度浮点数 | %f , %g , %e 前加小l |
long double | 扩展浮点数 | %f , %g , %e 前加大L |
char | 字符类型 | %c |
2.2.2 其他类型
数组
#define N 10
#define M 11
int main(){
/**
* 1.一维数组
* 界说:int a[N];
* 初始化:int a[10]={1,2,3,4}
* 或许 a[10]={0}
* c99: int a[10]={[1]=9,[5]=7};指定方位初始化
* 一起存在 int a[]={1,2,9,4,[0]=5,8} 输出 {5,8,9,4}
*/
/**
* 2.多维数组
* 界说:int a[M][N];
* 初始化:int a[M][N] = {{1,2,3},{1,23,4},...};可不全列出
* C99(可指定方位初始化): int a[2][2] = { [0][1]=1,[1][1]=2};
*/
/**
* 3. 常量数组 constant arrays
* 界说:在数组前面加 const
*/
const char hex_chars[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
** 4. c99中的变长数组VLA
* 变长长度不一定用变量来指定,恣意表达式(能够包含运算):
* int a[3*i+5];
* int b[j+k];
* 变长数组首要约束是没有静态存储期限,没有初化式
*/
int vla[3*N+M];
}
字符串
char *p_str = "hello world!";\\指针式
char arry_str[] = "hello world!"; \\数组式
结构、联合、枚举
#include <stdio.h>
/*-----------------------结构----------------------------*/
//省掉了结构符号
struct {
int version_1;
char name_1;
} a1;
//(结构)界说办法1:包含了结构符号
struct Part {
int version_1;
char name_1;
} b1, b2, b3;//一起声明3个结构体变量,也可不在此处声明
struct Part b4;//声明一变量
//(结构)界说办法2:运用typedef
typedef struct {
int version_2;
char name_2;
} Part2;//此处运用类型界说将结构命名为类型Part2
Part2 c1, c2;//声明两个变量
/*-----------------------联合----------------------------*/
//(联合)界说办法1:包含了联合符号
union Union_1 {
int weight;
double name;
} d1, d2, d3;//一起声明3个联合变量,也可不在此处声明
//(联合)界说办法2:运用typedef
typedef union {
int version_2;
char name_2;
} Union_2;//此处运用类型界说将联合命名为类型Part2,
/*-----------------------枚举----------------------------*/
enum { ONE, TWO, THR } f1;//界说3个枚举常量 和一个变量
enum Enum_1 { FIV, SIX } f2;// 办法一:包含了枚举符号
typedef enum { TRUE=1, FALSE=0 } BOOL;//办法2:运用typedef类型界说
2.3 运算符与表达式
运算符
优先级 | 名称 | 符号 | 结合性 |
---|---|---|---|
1 | 数组取下标 | [] | 左结合性 |
1 | 函数调用 | () | 左结合性 |
1 | 取结构和联合的成员 | 点. 右箭头->
|
左结合性 |
1 | 自增(后级) | i++ | 左结合性 |
1 | 自减(后缀) | i– | 左结合性 |
2 | 自增(前缀) | ++i | 右结合性 |
2 | 自减(前缀) | –i | 右结合性 |
2 | 取地址 | & |
右结合性 |
2 | 直接寻址 | * |
右结合性 |
2 | 一元正号 | + |
右结合性 |
2 | 一元负号 | – | 右结合性 |
2 | 按位求反 | ~ | 右结合性 |
2 | 逻辑非 | ! | 右结合性 |
2 | 计算所需空间 | sizeof | 右结合性 |
3 | 强制类型转化 | () | 右结合性 |
4 | 乘法类运算符 | * / % | 左结合性 |
5 | 加法类运算符 | + – | 左结合性 |
6 | 移位 |
<< >>
|
左结合性 |
7 | 联系 |
< > <= >=
|
左结合性 |
8 | 判等 |
== !=
|
左结合性 |
9 | 按位与 | & |
左结合性 |
10 | 按位异或 | 左结合性 | |
11 | 按位或 | | |
左结合性 |
12 | 逻辑与 | && |
左结合性 |
13 | 逻辑或 | || |
左结合性 |
14 | 条件 | ? : |
左结合性 |
15 | 赋值 |
= *= /= %= += -= <<= >>= &= ^= |=
|
右结核性 |
16 | 逗号 | , |
左结合性 |
表达式
首要包含逻辑、算术、赋值表达式,前两种参阅上方表格,赋值表达式留意一下复合赋值
int android, java, python,c;
/*简单赋值*/
android = 1;
java = android;
c = android + java * 10;
/*复合赋值*/
c = android = python = java * 10;
操控流程
if,switch,while,for,break,continue,goto,参阅上一篇。此处额定列举一下longjump
#include <setjmp.h>
void callee(jmp_buf env) {
longjmp(env, 1);
/* unreachable */
}
void caller() {
jmp_buf env;
switch (setjmp(env)) {
case 0:
callee(env);
/* unreachable */
break;
case 1:
printf("returned from callee\n");
break;
default:
printf("unexpected setjump value\n");
}
}
三. 进阶篇
3.1 指针
界说、赋值、取址&、直接寻址*
int main(){
int i=0,*p;//声明指针变量p
p=&i;// & 取址运算符,把i的地址赋值给了指针p
int k=1,*q=&k;//合并写法
printf("%d",*p);// *(直接寻址运算符)拜访存储在目标中的内容 ,也可幻想成&的逆运算
}
作为回来值
int *find_middle(int n, int a[n]) {
return &a[n / 2];
}
作为参数
重要作用
- 函数调用中用作实际参数的变量无法改动,当希望函数能够改动变量时, 指针作为参数就能解决此问题。
- 指针传参高效原因是,假如变量需求很多存储空间,传递变量的值有时会浪费时间和空间。
void decompose(double x, long *int_part, double *frac_part) {
*int_part = (long) x;
*frac_part = x - *int_part;
}
算术运算
int main() {
int a[] = {1, 2, 3, 5, 6, 7, 8, 9, 10};
int *p = &a[0], *q = &a[6];
//1. 加减整数
p += 4;
p -= 3;
//2. 指针相减(相减为间隔)
printf("q-p = %lld\n", q - p);
//3. 指针比较(取决于在数组中的方位)
printf("q>p:%d q<p:%d\n", q > p, q < p);
//4. 复合常量的指针
int *k = (int[]) {2, 4, 6, 8, 10};
k += 4;
printf("*K=%d\n", *k);
return 0;
}
数组名作为指针和指针作为数组名
#define N 10
/* 1 .用数组名作为指针*/
void case1() {
int a[] = {1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, *p,*q;
// 原始写法
for (q = &a[0]; q < &a[N]; q++) {
//省掉
}
// 惯用法(更简练)
for (p = a; p < a + N; p++) {//此处a+N理解为,将a当作指针,然后做了指针的加法运算
}
}
/* 2. 用指针作为数组名*/
void case1() {
int a[] = {1, 2, 3, 5}, *p = a, *q = &a[0], *k;
k = a;
// 三种效果相同
for (int i = 0; i < N; ++i) {
printf("p[%d]=%d\n", i, p[i]);
printf("q[%d]=%d\n", i, q[i]);
printf("k[%d]=%d\n", i, k[i]);
}
}
数组指针和指针数组
int (*p)[n];//数组指针
int *p[n];//指针数组
指针高级应用malloc、calloc、realloc
动态存储分配&空判别&开释
三种内存分配函数(内存块都来至于堆区) | 描绘 | 原型 |
---|---|---|
malloc |
分配内存块,但不进行初始化,少一步比calloc 更高效 |
void *malloc(size_t size) |
calloc |
分配内存块,并进行清零 | void *calloc(size_t nmemb,size_t size) |
realloc |
调整先前分配的内存块 | void *realloc(void *ptr,size_t size) |
内存开释函数 | 描绘 | 原型 |
---|---|---|
free |
开释不需求的内存块 | void *free(void *ptr) |
空指针 | |
---|---|
NULL |
如分配内存时未找到满足巨细,就会回来空指针,if(P=NULL){...}
|
悬空指针 | 如在开释p指向的内存后,再拜访或修正开释掉的内存块 |
其他 | |
---|---|
-> 运算符 |
称为右箭头选择,用于 node->value 替代 *node.value 的组合 |
restrict 受限指针 |
int *restrict p; 1.假如p指向的目标需求修正,则目标不会答应除p之外的任何办法拜访 2.假如一个目标有多种拜访办法,通常将这些办法互称为别名 |
3.2 预处理器
3.2.1 预处理器原理
如
gcc <SourceFile> -E
能够看到预处理器的输出
留意: 预处理器仅知道少数C言语的规矩。因此在履行指令时是有或许产生不合法程序,有时看起来正常但过错找起来难,能够检查一下预处理输出是一种办法
3.2.2 预处理指令
特征:
- 指令都以#开端
- 指令的符号间能够插入恣意数量空格或水平制表符
- 指令总在第一个换行符出结束,除非运用
\
符加入当时行结束,明确地指明要延续 - 指令能够出现在程序的任何地方
- 注释能够和指令放在同一行
部分预处理指令:
预处理指令范畴 | 指令 |
---|---|
宏界说 |
#define 指令界说一个宏#undef 指令删去一个宏 |
条件编译 |
#if 、#ifdef 、#ifndef 、#elif 、#else 、#endif 以测验条件来确认程序是否包含一段文本块 |
文件包含 |
#include 指定的文件内容被包含到程序中 |
其他特殊指令 | 1. #error 显现出错音讯2. #line 改动程序行编号办法 3. #pragam 为编译器履行某些特殊操作特供一种办法 |
3.2.3 宏界说
标题 | 描绘 | 例子 |
---|---|---|
简单宏 | #define 标识符 替换列表 | |
参数宏 | #define 标识符(x1,x2,…,XN) 替换列表 | |
#运算符 | 将宏的一个参数字符串化,只答应出现在参数宏的替换列表 | |
##运算符 | 将两个记号(如标识符)粘合在一起,变成一个记号 | |
预界说宏 | 一种已经界说好的宏,每一个都是整数常量或字符串字面量 |
__DATE__ ,__TIME__ ,__STDC__ ,__FILE__ ,__LINE__
|
空宏参数 | 答应宏调用时恣意参数为空,首要出现在参数宏或# 运算符或## 运算符的调用中 |
|
参数个数可变宏 | 在宏界说的参数列表最终中运用... ,…省掉号在,__VA_ARGS__ 专用标识符,代表与... 对应的参数 |
特殊
__func__
标识符与预处理器
无关,相当于当时履行函数都的函数名字符串变量
#include <stdio.h>
/*
* 1. 简单宏
*/
#define N 10
#define D "=%d\n"
/*
* 2.参数宏
*/
#define IS_EVEN(n) ((n)%2==0)
/*
* 3. #运算符,用于参数宏的替换列表中,字符串化
*/
#define P_INT(x) printf(#x D,x)
/*
* 4. ##运算符,粘合两个记号,将两个记号变为一个记号
*/
#define M_K(n) i##n
#define JOIN(x,y,z) x##y##z
int main(){
IS_EVEN(3);
int a=3,b=2;
P_INT(a-b);
int M_K(1)=1,M_K(2)=2;// 相当于声明i1,i2
P_INT(i1-i2);
/*
* 5. 预界说宏,整数常量或字符串字面量
*/
puts(__DATE__);
puts(__TIME__);
printf("%d",__STDC__);
printf("%s %d",__FILE__,__LINE__);
/*
* 6. 空宏参数
*/
int M_K()=0;
P_INT(i);
int JOIN(r,,),JOIN(r,s,t),JOIN(r,,t);
r=1,rst=2,rt=3;
/*
* 7 参数个数可变的宏,...省掉号在参数列表最终,__VA_ARGS__专用标识符,代表与...对应的参数
*
*/
#define TEST(condition,...) ((condition)? \
(printf("test passed:%s\n",#condition)): \
(printf(__VA_ARGS__)))
TEST(3>2,"3>2 %s","test");
TEST(2>3,"output %d is not big than> %d\n",2,3);
/*
* 8. __func__标识符,与预处理器无关,每函数都可拜访
*/
printf("%s return",__func__ );
#undef N
return 0;
}
3.4 程序设计
多文件程序的文件
标题 | |
---|---|
源文件 |
.c 结束的全部文件 |
头文件 | 常规.h 结束的文件 |
#include 包含规矩
#include 记号
,如#include CPUFILE
,CPUFILE
是一个根据不同体系下不同架构的宏界说
头文件包含规矩 | |
---|---|
#include <文件名> |
搜索体系头文件 |
#include "文件名" |
搜索当时目录头文件,后体系文件目录 |
3.5 IO
3.5.1 流
标题 | |
---|---|
文件指针 |
File *fp1 C中对流的拜访是通过文件指针实现 |
规范流
文件指针 | 流 | 默认意义 |
stdin | 规范输入 | 键盘 |
stdout | 规范输出 | 屏幕 |
stderr | 规范过错 | 屏幕 |
重定向
重定向输入: 强制程序从文件输入而不是从键盘输入 |
办法:前面加上字符< ,如demo <in.dat
|
重定向输出: 强制程序从文件输出而不是从屏幕输出 |
办法:前面加上字符> ,如demo >out.dat
|
3.5.2 文件
<stdio.h>支持的两种类型文件
文本文件 | 字节表明字符,可检查或编辑文件 |
二进制文件 | 字节不一定表明字符,字节组能够表明其他类型数据 |
翻开、封闭、及其他操作
翻开文件 | fopen(文件名,形式) |
参阅形式表 |
封闭文件 | fclose(文件指针) |
参阅形式 |
翻开的流附加文件 | freopen(文件名,形式,附加文件指针) |
附加文件指针一般为规范流或其他 |
临时文件 | 1.File *tempfile(void) 2. char *tempnam(char *s)
|
1. tempfile,易用,缺陷不能命名,按需保存起来 2. tempnam生成一个唯一的、可用于命名临时文件的字符串,你需求手动将其作为参数传递给其他函数(如 fopen())以创立实际的临时文件。请留意,在某些体系上,由于安全性问题而不引荐运用此函数。 |
文件缓冲 | 1. fflush(文件指针) 2. void setbuf(文件指针,缓冲数组,<缓冲形式>,>运用缓冲巨细> ) |
1. fflush清洗文件缓冲区 2. setbuff 按巨细方位缓冲类型(_IOFBF,_IOLBF,_IONBF)操控缓冲流 |
文件重命名 | rename(旧名,新名) |
|
文件删去 | remove(文件名) |
|
文件定位 | 1.fseek 移动到文件的某些方位2. ftell 回来当时文件方位3. rewind 把文件方位设置在开端处4. fgetpos 获取文件方位5. fsetpos 设置文件方位 |
形式 | 二进制文件 | 文本文件 |
---|---|---|
读 | rb | r |
写(文件无需存在) | wb | w |
追加(文件无需存在) | ab | a |
读和写(从文件头开端) | r+b 或 rb+ | r+ |
读和写(假如文件存在就截去) | w+b 或 wb+ | w+ |
读和写(假如文件存在就追加) | a+b 或 ab+ | a+ |
代码操练片段链接
3.5.3 输入输出
输出类型 | 函数 | 表述 |
---|---|---|
流输出 |
printf()
|
向规范输出stdout写入内容 |
流输出 |
fprintf(File*,const char *,...)
|
向恣意输出流写入内容 |
字符串输出 |
sprintf(char*,const char *,...)
|
输出写入字符数组 |
字符串输出 |
snprintf(char*,size_t,const char *,...)
|
输出写入字符数组,约束长度 |
字符输出 |
putchar(int)
|
向规范流stdout写一个字符 |
字符输出 |
fputc(int,File*)
|
恣意流写一个字符 |
字符输出 |
putc(int,File*)
|
恣意流写一个字符,效果同上 |
行输出 |
puts(const char *)
|
向规范流stdout写字符串 |
行输出 |
fputs(const char *,File *)
|
向恣意流写字符串 |
块输出 |
fwrite(void*,size_t,size_t,File*)
|
将内存中的数组仿制给流,操控巨细 |
输入类型 | 函数 | 表述 |
---|---|---|
流输入 |
scanf()
|
从规范输入stdin读入内容 |
流输入 |
fscanf(File*,const char *,...)
|
从恣意输入流读入内容 |
字符串 |
sscanf(char*,const char *,...)
|
输入写入字符数组,通常用fgets 后,再运用sscanf 进一步处理 |
字符 |
getchar(void)
|
从规范流stdin读一个字符,#define getchar) getc(stdin)
|
字符 |
fgetc(File*)
|
从恣意流读一个字符 |
字符 |
getc(File*)
|
从恣意流读一个字符,效果同上 |
字符 |
ungetc(int,File*)
|
从恣意流读入的字符“放回”并清除流的文件结束指示器 |
行 |
gets(char *)
|
从规范流stdin 读一行 |
行 |
fgets(char *,int n,File *)
|
从恣意流读字符串,到n-1个,或换行符结束操作 |
块 |
fread(void*,size_t,size_t,File*)
|
从流读入到数组的元素,操控巨细 |
四.规范库
首要分为上面几个部分,参阅上篇
总结
- 整体上包含了C言语语法的概要,自身目的是为了少数描绘进行知识归纳,但对宏和指针部分还是保存一些细节描绘
- 对于Android JNI,Framework知识所需的进程线程部分知识还需单独文章罗列。