c是一种介于高级语言与低级语言之间的语言,将原文最终编译为机器码执行的。程序员
"C_Cpp.updateChannel": "Insiders"
#
为c语言之中的编译符号
gcc
为基本编译工具spring
预编译shell
预编译是指在编译以前对原文所作的预处理,生成 tmp.i
文件。编程
- 文件预编译:引入
include<exp.h>
(系统库),include"exp.h"
(自定义库) 等头文件。- 条件预编译:
#if
根据条件执行特定编译。- 常量预编译:
#FLAG
替换预编译常量。- 删除代码注释
- 将原文编译为汇编语言,生成
tmp.s
文件。- 将原文编译为机器码,生成
tmp.o
文件。- 将原文编译连接生成最终可执行文件,
tmp.exe
文件。
# 将tmp.c编译为可执行文件tmp.exe windows平台默认为.exe文件 gcc ./tmp.c -o tmp.exe # 将tmp.c预编译为tmp.i gcc -E ./tmp.c -o tmp.i # 将tmp.c预编译并重定向到tmp.txt gcc -E ./tmp.c > tmp.txt # 将tmp.c编译为汇编 gcc -S ./tmp.c gcc -S ./tmp.c -o tmp.s # 将tmp.c编译为obj文件 gcc -c ./tmp.c gcc -c ./tmp.c -o tmp.o
指令 | 结果 | 文件 | 备注 |
---|---|---|---|
-E | 生成预编译文件 | tmp.i |
手动指定才可生成文件,> ,-o |
-S | 生成汇编文件 | tmp.s |
自动生成文件 |
-c | 生成机器码文件 | tmp.o |
自动生成文件 |
-o | 指定生成文件 | tmp.exe |
指定生成文件 |
null | 生成可执行文件 | tmp.exe |
自动生成文件 |
c语言的变量皆为强类型限定,且全部变量只要定义完成,其内存空间便已申请完成。windows
全部数值型变量(基础类型变量)以及NUll,可安全做为boolean。
完成空间申请的指针为true,未完成空间申请的指针会出 error。数组
boolean | 标识范围 |
---|---|
true | 非0数值,成功定义的任意级指针 |
false | 0,NULL |
c语言是一种块级代码,某一块内部变量外部是没法使用的,函数,以及循环体头部变量在外部是没法使用的。缓存
关键词 | 名称 | 做用域 | 备注 |
---|---|---|---|
#define |
宏变量 | 文件内部 | #define ~ #undef 或至文件结尾 |
auto |
块变量 | 当前快结构,或某些循环体头部 | 变量默认都是 auto |
register |
寄存器变量 | 定义与当前cpu寄存器同宽的值 | 运行时存在位宽限制 |
static |
静态变量 | 全局变量 | 读写皆可生效 |
extern |
引用变量 | 本文件生效 | 引用全局变量,或函数至本文件 |
const |
只读变量 | 变量可读不可修改 | 特殊修饰词,某些函数中的参数限定词 |
#define
#undef
定义撤销宏变量,此类型变量会在预编译时,替换或终止替换指定变量。安全
#define MAX_SIZE 1000 //...... #undef MAX_SIZE
类型 | 符号 | 字节 | 备注 |
---|---|---|---|
char | yes | 1 | 由读取方式决定类型(small int/char) |
short int | yes | 2 | 简写 short |
int | yes | 4 | 最经常使用的整型 |
long int | yes | 8 | 简写 long |
float | no | 4 | 经常使用浮点数 |
double | no | 8 | 大浮点 |
结构体 struct数据结构
结构体数据用于各种自定义数据结构。ide
typedef ... Example{...}example; // 全名才可以使用 struct Example exp; union Example exp; enum Example exp; // 别名使用 example exp;
结构体定义之时,以全部部份内存的最小公倍数为基本单位(字节为基础单位),对结构体内部进行内存分配,且空间大小从上至下递增而非其余,其中多出的部份内存会被填充没法正常使用。即定义数据结构时,基本数据类型尽可能不要变,且内存大小从上至下递增。
- typedef定义
1. 对变量类型赋予别名
2. 定义结构体类型- 结构体使用
1. 当为普通方式访问时用.
2. 当为指针方式访问时用->
typedef unsigned char byte; typedef struct List{ byte* base; byte* top; byte* size; }*list; void main(){ int x = 10; printf("输出:%d",x); scanf("%d",&x); //...... byte y = list->base; byte z = (*list).base; }
枚举类型 enum
经常使用于映射某些有限分类值,如星期几,几月,季节,性别...
一种映射模式,指定某些变量表明某些值,这些变量会变为常量。
typedef enum Season{ spring = 1, summer, autumn, winter }season; typedef enum Season{ spring = 1, summer = 2, autumn = 3, winter = 4 }season_cmp; void main(){ season sea = spring; printf("The season's number is %d",sea); printf("The season's number is %d",spring); }
位域结构体
经常使用于高频次二项分布运算 0~1。
基本类似于结构体,不一样之处在于位域结构体仅支持(unsigned int / int)的数据类型,以"位"为基本单位定义数据结构,数据默认存在为0,但存在BUG,不赋值时慎用。其内部是将int数值拆分做为一个总体运算的。
// 内存以 int 为单位,x,y不能超过位上限 typedef struct Flag{ unsigned int x:1; //0~1 unsigned int y:2; //0~3 }flag;
共用体 union
经常使用于高速缓存数据,或短期记录某些值。
基本结构类似于结构体,惟一不一样的是,其本省内存占用量就是最大数据的内存占用量,即同一时间里,通常仅为最近一被改写的数据是有效的,其余数据因为内存结构被覆盖会致使数据失效。
// 同一时间段仅有一个值有效,且内存为 4 typedef union Record{ unsigned int x; unsigned int y; unsigned int z; }record;
指针,包括指针空间自己以及所保存的一个地址,这个地址指向一个存储空间。
符号 | 普通变量 | 指针变量 | 数组变量 |
---|---|---|---|
&p |
原始变量指针化 | 指针变量指针化(多级指针) | |
(int)&p |
存储空间地址(int) | 指针空间地址(int) | 存储空间头地址(int) |
(int)p |
强转int类型 | 存储空间地址(int) | 存储空间头地址(int) |
*
将任意变量指针化,定义时使用。&
对任意变量取地址,运算时使用。
类型 | 定义 | 内存管理 |
---|---|---|
普通变量 | 自动申请存储空间 | 栈区自动处理 |
指针变量 | 自动申请指针空间,无存储空间 | 栈区自动处理,非栈区手动处理 |
利用一个虚拟地址(整数),强制转化出一个指定类型的指针变量。
int p = 100; int addr = &p; int* q = (int*)addr; // 强制地址转化指针 int x = *q; int x = *(int*)(&p);
静态变量为被
static
修饰的变量,全局变量则为在程序原文最外侧所设置的变量。
静态区亦称全局区,由编译器管理,编译一次便生效,程序结束失效。
普通变量或称临时变量以及函数参数等,以及指针变量都在这里。
栈区亦称为临时区,由编译器管理,随着程序运行而改变。
一些自定义结构体以及大型数据都在这里操做,配合指针便可灵活使用。
堆区彻底由程序员手动控制,包括内存的申请以及释放等;需注意的是此处易发生内存的各种错误,需谨慎使用。
代码区由编译器管理,主要用于存储函数体的二进制原文。
指针空间泛指一些指针能够活动的区域,主要包括栈区指针区,部分堆区;按照使用类型又可分为引用其余变量的空间,以及使用本身申请的空间。
引用区域 | 内存管理 | 备注 |
---|---|---|
堆区 | 程序设计者管理内存 | 需严谨设计以避免发生内存错误 |
栈区普通变量 | 自动内存管理 | 普通变量消失时,指针需变为NULL |
栈区指针变量 | 自动内存管理 | 指针连接指针即为多级指针 |
int* p; p = (int*)malloc(sizeof(int)); *p = 100; free(p);
int p = 100; int* m; // &p 取一个普通变量的 起始地址 m = &p;
int p = 100; // 一级引用 int* m; m = &p; // 二级引用 int** n; n = &m; **n = 1000;
多级指针
多级指针的内在形式主要包括前面几个节点所指出的,栈区指针,堆内部指针之间的自我引用以及相互引用。且多级指针在操做上和单级指针别无原理上差异。
多级指针需由内向外或由外向内嵌套操做。
- 单级指针:引用其余存储空间,或手动申请堆空间,其后做为普通变量便可。
- 多级指针:逐级引用下一级别指针,最后将其做为普通指针使用便可。
数组索引,自己就是一个指针,但其没有指针空间地址,仅有存储空间头地址。
(int)p
以及(int)&p
都是一个数值,都是存储空间头地址。
数组,一段连续的内存空间,由定义方式不一样致使栈区和堆区之中均可以存在。
为肯定数组存储空间大小,可在定义阶段初始化数组,不然必须给定数组长度。
int p[] = {0,1}; int p[2]; p[0] = 0; p[1] = 1;
数组索引能够彻底看成指针来使用。
(int)(&p)
与(int)p
均可以正确获取头指针位置。
int p[] = {0,1,2,3,4}; int addr = p;
指针运算仅支持
+
-
操做,且位移距离为指针所指向的空间的大小。当数组作指针运算时,是向上或向下移动一整个数组的位置。
int p[] = {0,1,2,3,4}; // 数组名直接就是地址值 int* q = p; // 向量运算支持自增自减 q++; // 数组的两种访问方式 printf("%d %d %d %d %d",p,*q,*(q+1),*(q+1)+1,p[2]);
指针数组是指一个由指针所组成的数组,本质是一个标准数组。
数组指针是指一个数组的索引彻底由一个指针替代,本质是一个标准指针变量。
// 此处是指 p[10] 的 基本组成单位为 int* int* p[10]; // 此处是指 (*p)[10] 的基本组成单位为 int int (*p)[10];
多级指针能够拿来当数组使用。
函数是一种结构化编程的思惟方式。
返回值限定
函数名定义
函数参数定义
实际操做之中,还包括内存管理,异常处理,权限管理等
int example(int x,int y){ return x+y; }
函数的参数定义在编译阶段就已完成,因此参数的地址是固定的,可是参数的地址仅仅函数内部是可读的,且没法将函数参数地址从函数之中取出。若是能够拿出来会出现严重的系统性安全问题。
将原始值的一个
copy
传递给函数,参数和原始值是彻底分离的。
其参数是一个指针,其是将原始参数的存储地址赋予参数的存储地址,保证参数指向原始参数,此时参数的指针空间地址不变,而所保存的指向空间则变为数据的存储地址。
void demo_int(int x){ printf("值传递-> 参数存储空间:%d 参数值:%d\n",(int)&x,x); } void demo(int* x){ printf("址传递-> 参数指针空间:%d 参数存储空间:%d 值:%d\n",(int)&x,(int)x,*x); } void main(){ int x = 10; int z = 11; int* y = &x; printf("原始存储空间:%d 值:%d\n",(int)&x,x); printf("指针存储空间:%d 值:%d 指针空间:%d\n",(int)y,*y,(int)&y); demo(&x); demo(y); demo(&z); demo_int(x); demo_int(z); }
const 自己就是一个只读的修饰符。
// 等价于 int const p,p 只读变量 void demo(const int p){} // 指针不可变,指向的内存空间则无需关心 void demo(const int* p){} // 指针不可变,指向的内存空间依然不可变 void demo(const int* const p){}
全部的逻辑判断必须保证全部路径都被覆盖到,不然会出现严重漏洞!
int true = 1; int false = 0; if(true){} if(true){}else{} if(true{}elseif(){}else{} int key = 0; switch(key){ case 1: example; break; //如无此语句,则会直接静茹下一次循环 case 1: example; break; default: example; //此处通常无需 break }
for语句头部定义的变量为for块级变量。
break
,continue
。
goto 语句,基本不多用。
// for头部定义的变量为块级别变量,仅在for内部有效,且定义可覆盖外部定义。 for(int i=0;i<=10;i++){ printf("%d",i); } while(true){} do{}while(); STA: goto STA;
函数指针就是将函数引用指针化。
(int)add
(int)&add
均可正确获取函数的开始地址。
(int)p
(int)&p
前者获取指针函数所指向函数的地址,后者获取指针自己的地址。
int add(int x,int y){ return x+y; } void main(){ // 指针函数定义 int (*p)(int,int); p = &add; printf("%d\n",(*p)(10,13)); printf("原函数地址:%d %d\n",(int)add,(int)&add); printf("指针函数地址:%d 指针函数所包含的函数地址:%d\n",(int)&p,(int)p); }
就是返回值是指针类型的函数。
C 的标准库文件不多,可是确实强大。
此处全部的内存管理都是基于堆内存的处理。
void* malloc(int size)
申请 size 大小的空间,并不初始化。
void* calloc(int num,int size)
申请 num 个 size 大小的连续空间,且每一个字节都被初始化为 0。
void* realloc(void* addr,int new_size)
对 addr 从新分配内存,新空间大小为 new_size 。
void free(void* addr)
全部申请的空间都须要从这里释放,不及时释放或丢失空间地址会形成内存泄漏。
c语言的文件管理有种面向对象的感受,但实际就是一个结构体。
r w a r+ w+ a+ 等即为文件模式的表示符号。
FILE *p = NULL; p = fopen("./example.txt","w"); fclose(p); p = NULL;
字符串
char 用 'c'
表示,string 用 "str"
表示。
c语言的string就是一个char类型数组,和堆很类似。
// char 转为string char p[] = {'1','2','3','\0'}; puts(p); // 有效长度是4 char q[] = "4321"; printf("%s",q);
prinf("hellow %d",p) scanf("%d",p)
puts(s) gets(s)
char a = getchar() putchar(a)
fprintf() fscanf() 文件IO
const int* a 与 int const *a 做用相同。此时没法对 a 指向的变量作修改。 const int* const a 则指所指向的变量与指针自己皆不可修改。 // 此时修饰的是 const (int* a) 故实际是,值没法改变 const int* a // 此时修饰的是 const (a) 归实际是指针自己没法改变 int* const a // 下面这两个东西有数组和string的影子,会出现BUG,尽可能少使用。 calloc() memset(maze,0,size);
- 算术运算:+ - * / % ++ --
- 关系运算:== != > >= < <=
- 逻辑运算:! && ||
- 位运算 :& | ^ ~ >> <<
- 赋值运算:= += -= *= /= %= <<== >>== &= ^= |=
- 特殊运算:sizeof() & * ( ? : )
位运算是一种二进制对位运算
类型 | 符号 | 1 | 0 |
---|---|---|---|
与运算 | a & b |
a,b全都为1 | a,b至少有一个0 |
或运算 | a | b |
a,b至少一个1 | a,b全都为0 |
亦或运算 | a ^ b |
a,b不等 | a,b相等 |
反运算 | ~ a |
a为0 | a为1 |
位移运算 >> << 则是直接执行位移操做,0用以补位
c语言之中存在不少的运算符,各个运算符冲突之时,会优先执行等级高的运算。
// p++ 等级比较高 再到 ++p () [] -> . ++ -- (type) * & sizeof() ...... ,