C 基础

C

c是一种介于高级语言与低级语言之间的语言,将原文最终编译为机器码执行的。程序员

  1. 编译工具 MinGW-W64 GCC
  2. 配置库环境 vscode 设置 "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
  • gcc 工具解析
指令 结果 文件 备注
-E 生成预编译文件 tmp.i 手动指定才可生成文件,>-o
-S 生成汇编文件 tmp.s 自动生成文件
-c 生成机器码文件 tmp.o 自动生成文件
-o 指定生成文件 tmp.exe 指定生成文件
null 生成可执行文件 tmp.exe 自动生成文件

变量

c语言的变量皆为强类型限定,且全部变量只要定义完成,其内存空间便已申请完成。windows

boolean

全部数值型变量(基础类型变量)以及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

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 的标准库文件不多,可是确实强大。

经常使用方法

  • 内存管理

此处全部的内存管理都是基于堆内存的处理。

  1. void* malloc(int size) 申请 size 大小的空间,并不初始化。

  2. void* calloc(int num,int size) 申请 num 个 size 大小的连续空间,且每一个字节都被初始化为 0。

  3. void* realloc(void* addr,int new_size) 对 addr 从新分配内存,新空间大小为 new_size 。

  4. 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类型数组,和堆很类似。

  1. strcpy(s1,s2)
  2. strcat(s2,s2)
  3. strcmp(s1,s2)
  4. strlen(s)
  5. strchr(s,ch)
  6. strstr(s1,s2)
// char 转为string
char p[] = {'1','2','3','\0'};
puts(p);

// 有效长度是4
char q[] = "4321";
printf("%s",q);
  • 输入输出
  1. prinf("hellow %d",p) scanf("%d",p)

  2. puts(s) gets(s)

  3. char a = getchar() putchar(a)

  4. fprintf() fscanf() 文件IO

  • 其余库
  1. stdio.h
  2. stdlib.h
  3. string.h
  4. math.h
  5. error.h
  6. ctype.h
  7. ......
  • others
  1. system("cls")
  2. system("pause")
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() ...... ,
相关文章
相关标签/搜索