Skip to content

Instantly share code, notes, and snippets.

@Ran-ying
Created January 30, 2023 20:22
Show Gist options
  • Save Ran-ying/aa2a8eab6d46198313ec8eb6b4d1f9f2 to your computer and use it in GitHub Desktop.
Save Ran-ying/aa2a8eab6d46198313ec8eb6b4d1f9f2 to your computer and use it in GitHub Desktop.
#include <stdio.h>

int main(void){
    int i = 10;
    printf("%d",i);
    return 0;
}
    • stdio.h 标准输出流
    • string.h 字符串原型
    • ctype.h 字符类别测试和转换函数
    • limits.h 明示常量:整型范围限制
    • float.h 明示常量:浮点范围限制
  • 标准库

    <assert.h>
    <float.h>
    <math.h>
    <stdarg.h>
    <stdlib.h>
    <ctype.h>
    <limits.h>
    <setjmp.h>
    <stddef.h>
    <string.h>
    <errno.h>
    <locale.h>
    <signal.h>
    <stdio.h>
    <time.h>
    

预处理器

  • 明示常量 #define

    • #define 中使用参数,要用括号确保运算顺序正确
    • 在参数定义里面用 #x 输出 x 形参的值
    • ## 用于对象宏的替换部分,把两个记号组合成一个记号
    • #undef 指令用于取消已定义的 #define 指令
    #define NAME VALUE
    #define SQU(x, y) (((x)+(y))/2)  //宏参数 x y
  • 预定义宏

    说明
    DATE 日期, Nov 23 2019
    FILE 当前源代码文件名的字符串字面量
    LINE 当前源代码中行号的整型常量
    STDC 设置为 1 时,表明实现遵循 C 标准
    STDC_HOSTED 本机环境设置为 1,否则设置为 0
    STDC_VERSION 支持 C99 标准,设置为 199901L;支持 C11 标准,设置为 201112L
    TIME 翻译代码的时间,格式为 hh:mm:ss
  • #line

    • 用于重置 LINEFLIE 宏报告的行号和文件名
    • #line 1000 //重置行号
    • #line 10 "aaa.c" //重置行号和文件名
  • #error

    • 发出一条错误消息,包含指令中的文本
  • 文件包含 #include

    • 尖括号:查找系统目录
    • 双引号:查找指定目录
  • #typedef oldtype newtype

  • 条件编译

    • #ifdef (跟一个 #define)
    • #ifndef (与 #ifdef 相反)
    • #else
    • #endif
  • 内联函数

#include <stdio.h>
inline static void eatline(){}
int main(void){
    eatline();
}

  • 数学库

    • 在函数名后面加上 f,参数类型会变为 float
    • 在函数名后面加上 l,参数类型会变为 long double
    原型 描述
    double acos(double x) 返回余弦值为 x 的角度(0 ~ π 弧度)
    double asin(double x) 返回正弦值为 x 的角度(-π/2 ~ π/2 弧度)
    double atan(double x) 返回正切值为 x 的角度(-π/2 ~ π/2 弧度)
    double atan2(double y, double x) 返回正弦值为 y/x 的角度(-π ~ π 弧度)
    double cos(double x) 返回 x 的余弦值,x 的单位为弧度
    double sin(double x) 返回 x 的正弦值,x 的单位为弧度
    double tan(double x) 返回 x 的正切值,x 的单位为弧度
    double exp(double x) 返回 x 的指数函数的值(e^x)
    double log(double x) 返回 x 的自然对数值
    double log10(double x) 返回 x 的以 10 为底的对数值
    double pow(double x, double y) 返回 x 的 y 次幂
    double sqrt(double x) 返回 x 的平方根值
    double cbrt(double x) 返回 x 的立方根值
    double ceil(double x) 返回不小于 x 的最小整数值
    double fabs(double x) 返回 x 的绝对值
    double floor(double x) 返回不大于 x 的最大整数值
  • stdlib 通用工具库

    • qsort() 快速排序

      • 函数原型
        • 第一个参数是指向待排序数组的首元素
        • 第二个参数是待排序项的数量
        • 第三个参数是每个项的大小,因为第一个参数是 void 指针
        • 第四个参数是指向函数的指针,定义一个比较函数
    void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
  • string.h 库

    • memcpy(), memmove()

      • 函数原型
        • 从 s2 拷贝 n 字节到 s1 指向的位置
        • memcpy() 带关键字 restrict,表明两个内存区域之间没有重叠
        • memmove() 不做上面的假设
    void *memcpy(void *restrict s1, const void * restrict s2, size_t n);

I/O

  • print

    • 转换说明

      模板 输出
      %a %A 浮点数、十六进制数和 p 计数法
      %c 单个字符
      %d %i 有符号十进制整数 dec
      %e %E 浮点数,e 计数法
      %f 浮点数,十进制计数法
      %g %G 根据值的不同,自动选择 %f 或 %e(%E)。%e(%E) 格式用于指数小于 -4或者大于等于精度时
      %o 无符号八进制整数 oct
      %p 指针
      %s 字符串
      %u 无符号十进制整数
      %x %X 无符号十六进制整数,使用十六进制数 0f(0F) hex
      %% 打印一个百分号
    • 修饰符

      • 按下列顺序修饰
      修饰符 含义
      标记 - + " " # 0
      数字 最小字段宽度
      .数字 精度
      h 和整形转换说明一起使用,表示 short intunsigned short int 类型的值
      hh 和整形转换说明一起使用,表示 short charunsigned char 类型的值
      j 和整形转换说明一起使用,表示 intmax_tuintmax_t 类型的值,在 stdint.h 中定义
      l 和整形转换说明一起使用,表示 long intunsigned long int 类型的值
      ll 和整形转换说明一起使用,表示 long long intunsigned long long int 类型的值
      L 和浮点转换说明一起使用,表示 long double 类型的值
      t 和整形转换说明一起使用,表示 ptrdiff_t 类型的值。ptrdiff_t 是两个指针差值的类型
      z 和整形转换说明一起使用,表示 size_t 类型的值,size_t 是sizeof 返回的类型
    • 标记

      标记 含义
      - 待打印项左对齐
      + 有符号值若为正。则在值前面显示加号;若为负,则在值前面显示减号。
      空格,有符号值若为正,则在值前面显示前导空格;若为负,则在值前面显示减号。
      # 把结果转换为另一种形式。如果是 %o 格式,则以 0 开始;如果是 %x 或 %X 格式,则以 0x 或 0X 开始;对于所有的浮点格式,# 保证了即使后面没有任何数字,也打印一个小数点字符。对于 %g 和 %G 格式,# 防止结果后面的 0 被删除
      0 对于数值格式,用前导 0 代替空格填充字段宽度。对于整数格式,如果出现 - 标记或指定精度,则忽略该标记。
    • * 将修饰符中的数字作为参数在实参中指定

    • printf("dec = %d"); //dec 十进制

    • printf("octal = %o"); //oct 八进制

    • printf("hex = %#x"); //hex 十六进制,显示 0x 前缀

    • printf("%u"); //打印 unsigned 值

    • printf("%ld"); //打印 long 值

    • printf("%hd"); //打印 short 值

    • %c 打印 char 值

    • %e 打印指数计数法的浮点数

    • %zd 打印类型的大小或长度,参数配合 sizeof(int)

  • scanf

    • 参数

      • 读取基本变量类型的值,在变量名前加上一个 &
      • 把字符串读入字符数组,不要使用 &
    • 返回成功读取的项数

    • 转换说明

      转换说明 含义
      %c 把输入解释成字符
      %d 把输入解释称有符号十进制整数
      %e %f %g %a 把输入解释成浮点数
      %E %F %G %A 把输入解释成浮点数
      %i 把输入解释成有符号十进制整数
      %o 把输入解释成有符号八进制整数
      %p 把输入解释成指针(地址)
      %s 把输入解释成字符串。从第一个非空白字符开始,到下一个空白字符之前的所有字符都是输入
      %u 把输入解释成无符号十进制整数
      %x %X 把输入解释成有符号十六进制整数
    • 修饰符

      转换说明 含义
      * 抑制赋值
      数字 最大字段宽度。输入达到最大字段宽度外,或第一次遇到空白字符时停止
      hh 把整数作为 signed charunsigned char 类型读取
      ll 把整数作为 long longunsigned long long 类型读取 (C99)
      h 把 %d %i 作为 short int 类型读取,把 %o %x %u 作为 unsigned short int 类型读取
      l 把 %d %i 作为 long 类型读取,把 %o %x %u 作为 unsigned long 类型读取,把 %e %f %g 作为 double 类型读取
      L 把 %e %f %g 作为 long double 类型读取
      j 在整形转换说明后面时,表明使用 intmax_t 或 uintmax_t 类型
      z 在整形转换说明后面时,表明使用 sizeof 的返回类型
      t 在整形转换说明后面时,表明使用表示两个指针差值的类型
    • 抑制赋值 *

      • 在 % 和 转换说明中指定,读取时跳过这一个值
  • getchar putchar

char ch = getchar();     //表达式整体返回输入的值
putchar(ch);             //有缓冲区,要按下 [Enter] 清除缓冲区
  • 缓冲区

    • 完全缓冲 I/O
      • 缓冲区被填满时才刷新缓冲区
    • 行缓冲 I/O
      • 出现换行符时刷新缓冲区
    • 无缓冲 I/O
  • EOF end of file

    • 定义在 stdio.h 文件中:#define EOF (-1)
    • 与输入流比较的常量值,判断是否是输入流的结尾。输入流数据类型要支持负数。
    • 在不同操作系统中有不同的模拟文件结尾的组合按键
  • 重定向

    • 重定向输入
      • program.exe < io.txt
      • < 是 UNIX 和 DOS/Windows 的重定向运算符
    • 重定向输出
      • program.exe > out.txt
    • 组合拳
      • program.exe < io.txt > out.txt
  • ctype.h

    • 专门处理字符的函数库,判断字符是否是某一类别

      isalnum(char c);  //字母或数字
      isalpha(char c);  //字母
      isblank(char c);  //标准的空白字符(空格、水平制表符或换行符)或任何其他本地化指定为空白的字符
      iscntrl(char c);  //控制字符,如 Ctrl+B
      isdigit(char c);  //数字
      isgraph(char c);  //除空格之外的任意可打印字符
      islower(char c);  //小写字母
      isprint(char c);  //可打印字符
      ispunct(char c);  //标点符号(除空格或字母数字字符以外的任何可打印字符)
      isspace(char c);  //空白字符(空格、换行符、换页符、回车符、垂直制表符、水平制表符或其他本地化定义的字符)
      isupper(char c);  //大写字母
      isxdigit(char c); //十六进制数字符
      
      tolower(char c);  //如果参数是大写字符,该函数返回小写字符;否则,返回原始参数
      toupper(char c);  //如果参数是小写字符,该函数返回大写字符;否则,返回原始参数

文件输入/输出

  • 二进制模式,文本模式;底层 IO,标准高级 IO
  • 标准文件
    • 标准输入 standard input stdin
    • 标准输出 standard output stdout
    • 标准错误输出 standard error output stderr
  • fopen() 打开文件
    • 第一个参数是待打开文件的地址
    • 第二个参数是打开文件的模式
      • r
      • w
        • 重写
        • 若不存在则创建
      • a
        • 追加
        • 若不存在则创建
      • r+
        • 读写
      • w+
        • 读写
        • 若存在则长度截断为0
        • 若不存在则创建
      • a+
        • 读写
        • 追加
      • rb wb ab ab+ a+b wb+ w+b ab+ a+b
        • 与以上类似,但是以二进制模式打开文件
      • wx wbx w+x wb+x w+bx
        • (C11) 类似于非 x 模式,但是如果文件已存在或以独占模式打开文件,则打开文件失败
    • 成功打开文件后会返回一个文件指针(类型是 FLIE)
  • getc()
    • 参数是文件指针
    • 返回文件的一个字符
    • getc(stdin) 等价于 getchar()
    • 读取到文件结尾时返回特殊值 EOF
  • putc()
    • 第一个参数是待写入的字符
    • 第二个参数是文件指针
    • putc('C', stdout) 等价于 putchar()
  • fclose()
    • 参数是文件指针
    • 如果成功关闭,则返回 0,否则返回 EOF
  • fprintf()
    • 与 printf 类似
    • 第一个参数是文件指针
  • fscanf()
    • 与 scanf 类似
    • 第一个参数是文件指针
  • rewind()
    • 参数是文件指针
    • 让指针回到文件开始处
  • fgets() fputs()
    • 在字符串输入有介绍
  • fseek()
    • 第一个参数是 FILE 指针
    • 第二个参数是 offset,类型是 long int
    • 第三个参数是模式,该模式确定起始点
      • SEEK_SET (0L) 文件开始处
      • SEEK_CUR (1L) 当前位置
      • SEEK_END (2L) 文件末尾
    • 如果一切正常,则返回 0
    • 如果出现错误,则返回 -1
  • ftell()
    • 参数是 FILE 指针
    • 返回当前文件的指针位置
  • fgetpos()
  • fsetpos()
    • 不使用 long 表示位置,而使用 fpos_t,该类型用于超大文件
  • 其他的标准 IO 函数
    • ungetc(char c, FILE *fp)
      • getc 的逆操作
    • fflush(FILE * fp)
      • 刷新缓冲区
    • setvbuf(FILE * restrict fp, char * restrict buf, int mode, size_t size)
    • fread(void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp)
      • 二进制写入
      • ptr 待写入数据块的地址
      • size 待写入数据块的大小
      • nmemb 待写入数据块的数量
      • fp 指定待写入文件的指针
    • fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict fp)
      • 二进制 I/O
      • ptr 待写入数据块的地址
      • size 待写入数据块的大小
      • nmemb 待写入数据块的数量
      • fp 指定待写入文件的指针
      • 返回成功写入项的数量,正常情况下等于 nmemb
    • feof(FILE * fp)
      • 如果检测到 EOF,则调用此函数,如果返回非零值,则是文件结尾
    • ferror(FILE * fp)
      • 如果检测到 EOF,则调用此函数,如果返回非零值,则表明出现错误

数据和 C

  • 数据类型

    • int
      • long
      • short
      • unsigned
      • signed (C90)
    • char
    • float
    • double
    • void (C90)
    • _Bool (C99)
    • _Complex (C99)
    • _Imaginary (C99)
  • 存储

    • 位 bit
      • 最小的存储单位
    • 字节 byte
      • 1 byte = 8 bit
    • 字 word
      • 1 word = 64 byte (64 位系统)
  • 整数

    • 使用二进制存储整数
      • 7 : 0000 0111
    • 进制
      • 16(10) = 10(16) = 0x10 / 0X10
      • 16(10) = 20(8) = 020
      • 16(10) = 10000(2) = 0b10000
    • int 32 bit
    • int a = 'ABCD'; //第一个字节存 A 的 ASCII 码,依此类推
  • 浮点数

    • 小数部分
    • 指数部分
  • char

    • 8 bit

    • 转义序列

      转义序列 含义
      \a 警报
      \b 退格
      \f 换页
      \n 换行
      \r 回车
      \t 水平制表符
      \v 垂直制表符
      \\ 反斜杠
      \' 单引号
      \" 双引号
      \? 问号
      \0oo 八进制值(每个 o 可表示 0~7)
      \xhh 十六进制值 (每个 h 可表示 0~f)
  • 字符串

    • 使用 char 数组存储字符串
    • \0 标识字符串的结尾,数组中要保留一位给 \0
    • scanf 只会读取句中的一个单词
    • 字符串长度
      • strlen() 得到字符串的有效字符数
      • sizeof() 得到字符串的占用总空间
    • 字符串是指向字符的指针,直至 \0 停止,所以字符串 s[1] 是字符串 s 从第一位到最后一位,截断了第零位(s = "jack", s[1] = "ack")
    char c[10];
    scanf("%s",c);
    printf("%s",c);
  • 常量

    • define
    • const
  • 数组

    • char name[10]
    • 指定初始化器 (C99)
    int arr[3] = {0, 0, 3};   //No
    int arr[3] = {[3] = 10};  //Yes
    • 指针和数组
    arr == &arr[0];   //数组名是该数组首元素的地址
    int* arr1 = arr+1;//指针+1是指针下一个元素的地址,而不是下一个字节的地址
    arr+2 == &arr[2]  //相同的地址
    *(arr+2) == arr[2]//相同的值
    • 复合字面量
    int * p;
    p = (int[2]){1,2};  //临时创建变量

运算符、表达式和语句

  • 多重赋值
a = b = c = 30;  //cba依次等于30
  • 求模
a%b = a-(a/b)*b
  • 函数
#include <stdop.h>
void func(int n);   //ANSI 函数原型声明
int main(void){

}
void func(int n){  //ANSI 风格函数头
    //code...
}

函数

#include <iostream>
void shabi();           //函数原型
int main()
{
	shabi();            //函数调用
	void (*p)(int) = &shabi;  //函数指针
    p(10);   //调用函数值针
}

void shabi()            //函数定义
{
	for(int i=0;i<10;i++)
	printf_s("*");
	printf_s("\n");
}
  • 自定义库
#include "jack.h"

//jack.h file:
//code

指针

  • 查找地址:& 运算符
    • 后接一个变量名时,& 给出该变量的地址
  • 取值间接运算符:*
    • 后接一个指针名或地址时,* 给出储存在指针指向地址上的值
    • int * c = &b;

字符串

  • 以空字符('\0')结尾的 char 类型数组
  • 表示字符串
char words[] = "I am your father";
char words[] = "I" " am" " your father";
//以上两种内容一致
const char * words2 = "I am your big father";
//和上述写法几乎一样,但是数组形式有两份字符串副本,指针形式只有一份。
//words 本身有一个地址,然后他又指向了字符串的开头的地址
puts(words);   //只输出字符数组类型的数据,并在结尾加 \n
  • 数组和指针的区别

    • 数组是常量
    • 指针是变量
  • 字符串输入

    • 分配空间
    char words[20];
    • 读取输入
      • gets(words)
        • 读取直至遇到 \n,不安全,可能会有缓冲区溢出
        • 丢弃输入的换行符
      • fgets(words, length, in)
        • 第一个参数表示字符串存入的位置
        • 第二个参数表示读取字符的最大数量。如果该参数是 n,那么该函数将读入 n-1 个字符,或者读到遇到的第一个换行符为止。
        • 第三个参数指明读取的来源
          • 如果读入从键盘输入的数据,则以 stdin 作为参数,该标识符定义在 stdio.h 中
        • 读取到文件末尾时,函数返回空指针(NULL)
        • 读取时保留换行符
      • getchar()
      • scanf()
    • 输出
      • puts(words)
        • 自动在末尾加 \n
        • 读取 words 直至遇到空字符 \0
      • fputs(words, stdout)
        • 第一个参数表示输出的字符串
        • 第二个参数表示输出的位置
          • 如果要显示在计算机显示器上,应该使用 stdout 作为该参数。
        • 不在结尾加 \n
      • putchar()
      • printf()
    • 字符串函数
      • strlen(str)
        • 返回字符串长度
      • strcat(str1, str2)
        • 接受两个字符串作为参数
        • 把第二个字符串的备份附加在第一个字符串末尾,并把拼接后形成的新字符串作为第一个字符串,第二个字符串不变
        • 返回第一个参数的地址
        • 无法检验第一个数组是否能容纳第二个字符串
      • strncat(str1, str2, maxlen)
        • 第三个参数指定了对第一个参数的最大添加字符数,即读取第二个参数的最大字符数。
      • strcmp(str1, str2)
        • 如果两个字符串参数相同,该函数就返回 0,否则就返回非零值
        • 返回两个字符串的 ASCII 码之差,或保留符号返回 1
      • strncmp(str1, str2, limitlen)
        • 限制比较的字符数量
      • strcpy(str1, str2)
        • 复制字符串,而非指针
        • 无法检验第一个数组是否能容纳第二个字符串
      • strncpy(str1, str2,)
        • 限制复制的字符数量
      • sprintf()
        • 将参数输出至字符串,第一个参数是目标字符串的地址,其余参数和 printf() 相同
    • 字符串转换成数字
      • stdlib.h
      • 无错误检测的函数
        • int a = atoi("10");
        • double a = atof("10.0");
        • long int a = atol("10");
      • 更智能的函数
      long strtol(const char * restrict nptr, char ** restrict endptr, int base);
      long int a = strtol("10");
      unsigned long int a = strtoul("10");
      double a = strtod("10");

命令行参数

int main(int argc, char * argv[]){
    argc //参数数量
    argv //参数数组
}

链接

  • 具有文件作用域的变量

    • 外部链接
      • 可以在多文件程序中使用
      • 又称为全局作用域或程序作用域
    • 内部链接
      • 只能在一个翻译单元中使用,及一个源代码文件和她所包含的头文件。
      • 又称为文件作用域
    int num1 = 5;           //文件作用域,外部链接
    static int num2 = 10;   //文件作用域,内部链接
    int main(){
    
    }
  • 无链接

    • 具有块作用域、函数作用域或函数原型作用域的变量
    • 这些变量属于定义它们的块、函数或原型私有

变量存储

  • 静态存储期
    • 在程序执行期间一直存在
    • 文件作用域变量具有静态存储期
    • 具有块作用域的变量,用 static 声明后也能具有静态存储期
  • 线程存储期
    • 用于并发程序设计
    • 从被声明时到线程结束一直存在
    • _Thread_local 声明对象
    • 每个线程都获得该变量的私有备份
  • 自动存储期
    • 块作用域的变量
    • 进入这些块时,为变量分配内存
    • 退出这个块时,释放分配的内存
  • 动态分配存储期
  • 5 种存储类别
存储类别 存储期 作用域 链接 声明方式
自动 自动 块内
寄存器 自动 块内,使用关键字 register
静态外部链接 静态 文件 外部 所有函数外
静态内部链接 静态 文件 内部 所有函数外,使用关键字 static
静态无链接 静态 块内,使用关键字 static
  • 自动变量
    • 显式使用关键字 auto
    • 在代码块中隐藏外部同名变量(尽量不要用)
  • 寄存器变量
    • 如果幸运的话,寄存器变量储存在 CPU 的寄存器中,或最快的可用内存中
    • 由于储存在寄存器中,所以无法获取寄存器变量的地址
    • 使用 register 声明
    • 有可能在寄存器中,也有可能在普通的内存中
  • 块作用域的静态变量
    • 在多次函数调用之间会记录它们的值
    • 用 static 声明
  • 外部链接的静态变量
    • 把变量的定义性声明放在函数外面
    • 当某一个函数使用外部变量时,可以在函数中用 extern 再次声明
    • 如果一个文件要使用的外部变量定义在另一个文件中,必须在该文件中用 extern 声明
    • 只能用常量表达式初始化变量
  • 内部链接的静态变量
    • 在函数外面声明
    • 用 static 关键字声明
    • 只能用于同一个文件的函数
    • 当某一个函数使用外部变量时,可以在函数中用 extern 再次声明

函数存储

  • 外部函数(默认)
    • 可以被其他文件的函数访问
    • 其他文件访问时要用 extern 声明
  • 静态函数
    • static 声明函数
    • 只在文件内使用
  • 内联函数(C99)

随机数

static unsigned long int next = 1; //种子
unsigned int rand0(void){
    next = next * 1103515245 + 12345;
    return (unsigned int)(next / 65536) % 32768;
}
void srand1(unsigned int seed){
    next = seed;
}
srand1((unsigned int)time(0)); //自动重置种子,返回值是 time_t,参数是结果要储存的地址 time_t,可以传入一个空指针 0 作为参数

分配内存

  • malloc()

    • 接受一个参数:所需的内存字节数
    • 返回动态分配内存块的首字节地址
    • 使用 malloc 创建数组
    • 创建失败时返回空指针
    double * ptd;
    ptd = (double *) malloc(30 * sizeof(double));
  • calloc()

    • 接受两个参数:所需的存储单元数量、存储单元的大小
    • 把块中的所有位都设置为零
    double * ptd;
    ptd = (double *) calloc(30, sizeof(double));
  • free()

    • 接受一个参数:malloc() 返回的地址
    • 释放内存

ANSI C 类型限定符

  • const

    • 在指针中使用 const,在 * 左边表示指针指向的数据不能改变,在 * 右边表示指针本身不能改变
    • 对全局变量使用 const
  • volatile

    • 表示代理(而不是变量所在的程序)可以改变该变量的值。
    • 通常它被用于硬件地址以及在其他程序或同时运行的线程中共享数据
    • 涉及到编译器的优化,不会高速缓存变量的值
  • restrict

    • 允许编译器优化某部分代码以更好地支持计算
    • 只能用于指针,表明该指针时访问数据对象的唯一且初始的方式
    • 防止一个指针用两边
    int ar[10];
    int * restrict restar = (int *) malloc (10 * sizeof(int));  //唯一方式
    int * par = ar;  //不是唯一方式,可以直接 ar
  • _Atomic (C11)

    • 通过可选的 stdatomic.h threads.h 管理多线程
    int num;   //普通声明
    num = 12;  //普通赋值
    
    _Atomic int num;         //原子类型的变量
    atomic_store(&num, 12);  //原子性的存储12

结构

  • 建立结构声明
struct book{
    char title[10];
    char author[10];
    float value;
};
int main(void){
    struct book library;
}
  • 初始化结构
struct book library;

struct book{
    char title[10];
    char author[10];
    float value;
} library;

struct book library = {
    "Book",
    "book",
    .value = 2
};
  • 访问结构成员
library.value;   //结构名
struct library * another = &library;
another->value;  //指针
  • 和数组不同的是,结构名不是结构的地址
  • 允许把一个结构赋值给另一个结构
  • 结构可以设置指针,初始化结构后动态申请空间并赋值,然后释放内存。
  • 用指针访问结构要用 ->
  • 复合字面量
    • 不用创建变量
(struct book){"book", "book", 10}
  • 匿名结构
struct person{
    int id;
    struct {char first[20]; char last[20]}; //匿名结构
}
struct person jack;
jack.first; //访问匿名结构成员

联合

union hold{
    int digit;
    int bbb;
}
  • 在联合中,一次只能存储一个值,即 digit 和 bbb 只能同时存储一个,给某一个赋值时,另一个释放。
  • 访问联合中的任意一个成员,会返回当前被赋值的成员
  • 用指针访问联合要用 ->
  • 用联合名访问成员 .
  • 匿名联合
struct Person{
    union {
                struct ccc ccccc;
    }
}

枚举

  • 创建新类型并指定他可具有的值
enum level {
    low,
    medium
}
enum level {
    low = 10,
    medium = 20
}

声明

  • 使用符号
    • () []
    • *
double (*pdf)(double);
pdf = sin;  //sin() 函数在 math.h 中有原型

double x;
x = (*pdf)(1.2);  //调用 sin(1.2)
x = pdf(1.2);     //调用 sin(1.2)

  • 一字节包含八位
    • 编号 7 的位是高阶位,编号 0 的位是低阶位
    • 最大存储 255,最小存储 0
7 6 5 4 3 2 1 0
128 64 32 16 8 4 2 1
  • 原码

    • 原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值.
    • 第一位是符号位. 因为第一位是符号位, 所以8位二进制数的取值范围就是:[1111 1111 , 0111 1111] 即 [-127 , 127]
    • 原码是人脑最容易理解和计算的表示方式
  • 反码

    • 反码的表示方法是:
      • 正数的反码是其本身
      • 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
    • 将两个反码包括符号位直接相加,可以得到正确结果,但是会出现 +0 和 -0
  • 补码

    • 解决了高位只表示正负和 +0 -0 的问题
    • 要得到二进制补码的相反数,反转每一位然后加 1
    • 补码的表示方法是:
      • 正数的补码就是其本身
      • 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)
    • 将两个补码相加,不会出现 -0
  • 二进制枚举每一位:while(i++<32) if(nun & 1 << i);

  • 二进制小数

    • $0.527_{(10)} = 510^{-1}+210^{-2}+7*10^{-3}$
    • $0.101_{(2)} = 12^{-1}+02^{-2}+1*2^{-3}=0.625_{(10)}$
  • 八进制

    • 每个八进制位对应三个二进制位

    • 一个三位的八进制可能要用 9 位的二进制表示(一字节是八位)

      八进制 等价二进制
      0 000
      1 001
      2 010
      3 011
      4 100
      5 101
      6 110
      7 111
  • 十六进制

    • 0 1 2 3 4 5 6 7 8 9 A B C D E F

    • 每个十六进制位都对应一个四位的二进制位

      十进制 十六进制 等价二进制
      0 0 0000
      1 1 0001
      2 2 0010
      3 3 0011
      4 4 0100
      5 5 0101
      6 6 0110
      7 7 0111
      8 8 1000
      9 9 1001
      10 A 1010
      11 B 1011
      12 C 1100
      13 D 1101
      14 E 1110
      15 F 1111
  • 位运算符

    • 二进制反码或按位取反 ~
      • 把 1 变为 0,把 0 变为 1
    • 按位与 &
      • 对每个位,只有两个运算对象中相应的位都为 1 时,结果才为 1
    • 按位或 |
      • 对每个位,只有两个运算对象中相应的位不都为 0 时,结果才是 1
    • 按位异或 ^
      • 对每个位,只有两个运算对象中相应的位不同时,结果才是 1
  • 位应用

    • 掩码
      • 利用按位与的特性,在想出现的位置设为 1
    • 打开位
      • 利用按位或的特性,在想打开的位置设为 1
    • 关闭位
      • 利用按位与的特性,在想关闭的位置设为 0,不想关闭的位置设为 1
    • 切换位
      • 利用按位异或的特性,在想反转的位置设为 1,不想反转的位置设为 0
  • 位移运算符

    • 左移
      • value << offset
    • 右移
      • value >> offset
        • 无符号类型:用 0 填充
        • 有符号类型:用 0 或 符号位(最左边的位) 填充
  • 位字段

    • 创建了 2 个 1 位的字段
struct {
    unsigned int a : 1;
    unsigned int b : 1;
} jack;

数据结构

  • 链表
struct Node {
    int num;
    struct Node * next; //指向下一个链表的指针
}

int main(void){
    struct Node * head = NULL;
    struct Node * prev, * current;
    while(true){
                current = (struct Node *)malloc(sizeof(struct Node));
                if(head == NULL){
                    head -> next = current;
                }
                else{
                    prev -> next = current;
                }
                prev = current;
                current -> next = NULL; //空指针
    }

    current = head;
    while(current != NULL){
                puts(current -> num);
                current = current -> next;
    }
}

拓展字符支持

  • 在编译器中设置后,C 将三字符序列替换为字符,即使它在双引号中。
三字符序列 字符
??= #
??) ]
??! |
??( [
??' ^
??> }
??/ \
??< {
??- ~
  • 不会替换双引号中的双字符
双字符 符号
<: [
:> ]
<% {
%> }
%: #
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment