一.前言

导图

语言篇|现代C记录—简略篇

  • 本文首要对上一篇文章的精简归纳,首要分上面三个大部分
  • 进阶篇暂不包含线程、进程、网络篇,计划单独写

上一篇传送
言语篇—现代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 类型

语言篇|现代C记录—简略篇

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 运算符与表达式

语言篇|现代C记录—简略篇

运算符

优先级 名称 符号 结合性
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 指针

语言篇|现代C记录—简略篇

界说、赋值、取址&、直接寻址*

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];//指针数组

语言篇|现代C记录—简略篇

指针高级应用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 预处理器原理

语言篇|现代C记录—简略篇

gcc <SourceFile> -E 能够看到预处理器的输出

留意: 预处理器仅知道少数C言语的规矩。因此在履行指令时是有或许产生不合法程序,有时看起来正常但过错找起来难,能够检查一下预处理输出是一种办法

3.2.2 预处理指令

特征:

  1. 指令都以#开端
  2. 指令的符号间能够插入恣意数量空格或水平制表符
  3. 指令总在第一个换行符出结束,除非运用 \ 符加入当时行结束,明确地指明要延续
  4. 指令能够出现在程序的任何地方
  5. 注释能够和指令放在同一行

部分预处理指令:

预处理指令范畴 指令
宏界说 #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

语言篇|现代C记录—简略篇

3.5.1 流

标题
文件指针 File *fp1 C中对流的拜访是通过文件指针实现

规范流

文件指针 默认意义
stdin 规范输入 键盘
stdout 规范输出 屏幕
stderr 规范过错 屏幕

重定向

重定向输入:
强制程序从文件输入而不是从键盘输入
办法:前面加上字符<,如demo <in.dat
重定向输出:
强制程序从文件输出而不是从屏幕输出
办法:前面加上字符>,如demo >out.dat

3.5.2 文件

<stdio.h>支持的两种类型文件

文本文件 字节表明字符,可检查或编辑文件
二进制文件 字节不一定表明字符,字节组能够表明其他类型数据

翻开、封闭、及其他操作

语言篇|现代C记录—简略篇
翻开文件 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记录—简略篇

首要分为上面几个部分,参阅上篇

总结

  • 整体上包含了C言语语法的概要,自身目的是为了少数描绘进行知识归纳,但对宏和指针部分还是保存一些细节描绘
  • 对于Android JNI,Framework知识所需的进程线程部分知识还需单独文章罗列。