【c】搭建c知识体系:Basics

1.本篇目的:搭建并完善C知识体系

2.本篇的结构

  • 第一部分:基础点
  • 第二部分:调试技巧

第一部分:基础点

  • 预处理器

    • #include指令:文件包含,该指令所在的行都将被替换为由文件名指定的文件的内容,一种将所有声明捆绑在一起的较好的办法,保证所有的源文件有相同的定义与变量声明

      #include “文件” :先在源文件所在位置查找该文件,如果在该位置未找到,再根据相应规则查找

      #include <文件> :根据相应规则查找

    • #define指令:宏替换,#define 名字 替换文本


    • :将函数定义为宏可避免调用函数时所需的运行时开销

  • 变量存储类别

    静态存储方式程序运行期间分配固定的存储空间的方式。静态存储区中存放了在整个程序执行过程中都存在的变量,如全局变量。
    动态存储方式指在程序运行期间根据需要进行动态的分配存储空间的方式。动态存储区中存放的变量是根据程序运行的需要而建立和释放的,通常包括:函数形式参数;自动变量;函数调用时的现场保护和返回地址等。
    类型作用域存储位置生命周期特点实例
    static静态变量所在文件,准确地说作用域是从定义之处开始,到文件结尾处结束(static 全局变量)、 所在函数(static 局部变量)定义后一直占据存储空间,内存的静态区程序运行期间只被初始化一次(程序执行前,编译时),即使函数被调用多次,该变量的值也不会重置,初始化表达式是常量表达式,没有初始化表达式的元素被初始化为0或空字符void func1(void) { /* ‘thingy’ 是 ‘func1’ 的局部变量 - 只初始化一次 * 每次调用函数 ‘func1’ ‘thingy’ 值不会被重置。 */ static int thingy=5; thingy++; printf(“ thingy 为 %d , count 为 %d\n”, thingy, count); } thingy 为 6 , count 为 9 thingy 为 7 , count 为 8 thingy 为 8 , count 为 7 thingy 为 9 , count 为 6 thingy 为 10 , count 为 5 thingy 为 11 , count 为 4 thingy 为 12 , count 为 3 thingy 为 13 , count 为 2 thingy 为 14 , count 为 1 thingy 为 15 , count 为 0
    register定义存储在寄存器中而不是 RAM 中的局部变量 、函数形参(局部静态变量不能定义为寄存器变量)定义 ‘register’ 并不意味着变量将被存储在寄存器中,它意味着变量可能存储在寄存器中,这取决于硬件和实现的限制1.访问速度更快,在需要频繁访问的变量上使用 register 存储类可以提高程序的运行速度 2.变量的最大尺寸等于寄存器的大小(通常是一个字),且不能对它应用一元的 ‘&’ 运算符(因为它没有内存位置)即不能直接取地址{ register int miles; }
    外部变量external外部变量的意义是某函数可以调用在该函数之后定义的变量。或定义在其他文件中声明的全局变量或函数,当使用 extern 关键字时,不会为变量分配任何存储空间,而只是指示编译器该变量在其他文件中定义1.只被初始化一次(程序执行前),初始化表达式必须是常量表达式,没有初始化表达式的元素被初始化为0;2.通常用于当有两个或多个文件共享相同的全局变量或函数的时候#includ <stdio.h> int main() { //定义外部局部变量 extern int x; return 0; } int x=100;
    自动变量auto仅函数内部,所有局部变量默认的存储类函数被调用期间,在函数开始时被创建,在函数结束时被销毁每次进入函数或程序块都初始化,没有初始化表达式的元素被初始化为0{ int mount; auto int month; }
  • 函数

    函数形式说明
    内部函数(静态函数)static [数据类型] 函数名([参数])不能被其他源文件调用的函数称为内部函数 ,static是对函数的作用范围的一个限定,限定该函数只能在其所处的源文件中使用,因此在不同文件中出现相同的函数名称的内部函数是没有问题的。
    外部函数extern [数据类型] 函数名([参数])能被其他源文件调用的函数称为外部函数,C语言规定,在没有指定函数的作用范围时,系统会默认认为是外部函数,因此当需要定义外部函数时extern也可以省略。 extern可以省略;
  • 指针

    运算符&:取地址,只能应用于内存中对象,即变量与数组元素,不能作用与表达式、常量、或register变量
    ​ 运算符:间接寻址或间接引用运算符,作用于指针时,将访问指针所指向的对象。
    ​ &、
    优先级高于算术运算符
    ​ 指针变量可以相互赋值,指向相同的对象

    • 1)指针与数组 :

    一般,指针编写的程序比用数组下标编写的程序执行速度快
    ​ 一个通过数组和下标实现的表达式可等价地通过指针和偏移量实现

    char s[] == char *s 、&a[2] == a+2 (a数组)

    • 2)特性

      • 某些情况下对指针进行比较运算(如指针p、q指向同一个数组的成员)
      • 指针可以和整数相加或相减
      • 指针的算术运算具有一致性:所有的指针运算都会自动考虑它所指向的对象的长度
      • 有效的指针运算:
        • 相同类型指针之间赋值运算
        • 指针同整数之间的加法减法
        • 指向相同数组中元素的两个指针间的减法和比较运算
        • 指针赋值为0或指针与0之间的比较运算
    • 3)指针数组、指针的指针

      • 如char *lineptr[10]
        • 初始化:如,static char *name[] = {“xiaoming”,”xiaoqiang”}
      • 指针数组优点:数组的每一个元素的长度可以不同
    • 4)函数指针

      K&R P99

  • 运算符优先级

    1)优先级1:数组下标[]圆括号( )成员选择(对象).,成员选择(指针)->

    结合方向为左到右

    优先级1

    2)优先级2:负号运算符-,强制类型转换,自增运算符++,自减运算符–,取值运算符,取地址运算符&,逻辑非运算符!,按位取反运算符~,长度运算sizeof符,结合方向为右到左

    优先级2

    3)优先级3:除/,乘*,余数%,结合方向为左到右

    优先级3

    4)优先级4:加+,减-,结合方向为左到右

    优先级4

    5)优先级5:左移<<,右移>>,结合方向为左到右

    优先级5

    6)优先级6:大于>,大于等于>=,小于<,小于等于<=,结合方向为左到右

    优先级6

    7)优先级7:等于==,不等于!=,结合方向为左到右

    优先级7

    8)优先级8:级别先后为,按位与&,按位异或^,按位或| ,逻辑与&&,逻辑或|| 条件运算符?:,注意得是条件运算结合方向为右到左

    优先级8

    9)优先级9:赋值运算符=,除后赋值/=,乘后赋值*=,取模后赋值%=,加后赋值+=,减后赋值-=,左移后赋值<<=,右移后赋值>>=,按位与后赋值&=,按位异或后赋值^=,按位或后赋值|=,结合方向为右到左

    优先级9

  • 结构

    struct point {

    int x;

    int y;

    };

    • 声明:struct {int x,int y} x,y,z; #分配存储空间

      struct point pt;

  • 初始化:

    struct point maxpt = {320, 200};

  • 成员:maxpt.x

    maxpt.y

  • 嵌套:

    struct rect {

    struct point pt1;

    struct point pt2;

    };

    struct rect screen;

    • 成员: screen.pt1.x
  • 合法操作:

    1.作为一个整体赋值和赋值

    2.通过&取地址

    3.访问其成员

  • 结构指针:

    struct point *ptrstr;

    ptrstr -> x

  • 结构数组

1
2
3
4
5
6
7
8
struct key {
char *word;
int count;
} keytab[] = {
{ "auto", 0 },
{ "break", 0 },
{ "case", 0 }
}
};
  • 结构指针
  • 关键字统计
  • 自引用结构

    K$R P116 ex:统计输入所有单词出现频次(二叉树)

  • 表查找

  • 类型定义(typedef)
  • 联合
  • 位字段
  • 命令行参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <stdio.h>
    #include <string.h>
    int main(int argc, char *argv[] )
    {
    #./test -x 注意优先级顺序
    printf("%s\n",argv[0]);#./test
    printf("%c\n",(*++argv)[0]);#- argv指向argv[1]
    printf("%c\n",*++argv[0]);#x,先与[]结合,所以依然指向argv[1]
    printf("%d\n",argc);#2
    }
  • 运算符集合

  • 控制流语句

  • 标准库

    输入输出:printf (“%d”,i)、scanf(“%d”,&i)

    文件访问:

    1.打开文件,返回文件指针

    1
    2
    3
    FILE *fp;
    FILE *fopen(char *name,char *mode)
    fp = fopen(name,mode)
1
2
3
4
5
6
7
8
2.文件读写

int getc(FILE *fp)

int putc(FILE *fp)

#define getchar() getc(stdin)
#define putchar(c) putc((c), stdout)
1
2
int fscanf(FILE *fp, char *format, ...)
int fprintf(FILE *fp, char *format, ...)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
3.关闭文件

int fclose (FILE *fp)

错误处理

stderrexit

fprintf(stderr, "%s: can't open %s\n",prog, *argv);

int ferror(FILE *fp)#若流fp中出错,则函数返回一个非0

int feof(FILE *fp) #如果指定的文件到达文件结尾,返回一个非0值

行输入行输出

char *fgets(char *line, int maxline, FILE *fp) #读文件一行到line

int fputs(char *line, FILE *fp)#写line到文件

其他函数

![字符和字符串操作函数](https://github.com/hxlpub/imgs/blob/master/%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%AD%97%E7%AC%A6%E7%B1%BB%E5%87%BD%E6%95%B0.png?raw=true&ynotemdtimestamp=1650186921046)

第二部分:调试技巧

1
2
3
4
5
#gdb 段错误
gcc -o a -g a.c a.h
gcc ./a
r#运行
bt#查看seg fault