0%

===========================
2022-8-1
===========================

一.温故知新

    复合型数据类型
        结构体
        1.先定义结构体类型
            struct tag
            {
                //结构体成语的定义
            };
        2.再使用结构体类型定义结构体类型的变量\指针\数组
            数据类型		变量名	;
            struct tag		s		;
            struct tag *	p		;
            struct tag		arr	[成员个数]	;
        3.结构体类型的变量访问结构体的成员
            变量名 + . + 成员名
        结构体类型的指针访问结构体的成员
            指针名 + -> + 成员名
        4.通过sizeof运算符结算结构体大小
            结构体的大小相当于成员大小的和 + 字节对齐
        共用体
            共用体中的成员会共用同一块儿存储空间
            (使用一块儿存储空间管理不同类型的数据)
            1.先定义共用体类型
            union tag
            {
                共用体成员的定义;
            };
            2.大端格式和小端格式
            大端格式
            高字节的数据存放在低地址上
            低字节的数据存放在高地址上
            小端格式
            高字节的数据存放在高地址上
            低字节的数据存放在低地址上
            3.位域(位段/位字段)
            是编程语言(C语言)提供的操作位的方法
            -----------------------------------
            位带(bitband)

枚举

    enum是C语言的关键字
    枚举就是一个被命名的整型常数的集合
enum 枚举名
{
    标识符[=整型常数],
    标识符,
    ...
    标识符
};			//枚举定义之后需要加 ; 结尾
注意:
1.在定义枚举时.标识符与标识符之间要用 , 隔开而不是 ; 
2.在枚举中标识符可以和变量同名,如果同名变量会屏蔽标识符
    (尽量不要同名)
3.在定义枚举时,方括号的内容时缺省值,可以有也可以没有
    如果没有缺省值,标识符会按照0 1 2 3 4...的顺序进行表示
    如果有缺省值,则会从指定的整型常数开始按顺序进行表示
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

enum E
{
a,
b,
c,
d
};

int main(int argc, char *argv[])
{
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
printf("d = %d\n", d);

//a = 9527;枚举的标识符不是变量,不能做赋值运算

printf("a = %d\n", a);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>

enum E
{
a,
b,
c,
d
};

int main(int argc, char *argv[])
{
int a = 9527;
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
printf("d = %d\n", d);

//a = 9527;枚举的标识符不是变量,不能做赋值运算

printf("a = %d\n", a);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>

enum E
{
a,
b = 100,
c,
d
};

int main(int argc, char *argv[])
{
//int a = 9527;
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
printf("d = %d\n", d);

//a = 9527;枚举的标识符不是变量,不能做赋值运算

printf("a = %d\n", a);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>

enum E{MIN, MAX = 10};

int main(int argc, char *argv[])
{
int i = 0;

for(i = MIN; i < MAX; i++)
{
printf("i = %d\n", i);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>

enum E{MODE_0, MODE_1, MODE_2, MODE_3};

int main(int argc, char *argv[])
{
int mode = 0;

scanf("%d", &mode);

switch(mode)
{
case MODE_0 : printf("菜鸟难度!\n"); break;
case MODE_1 : printf("普通难度!\n"); break;
case MODE_2 : printf("地狱难度!\n"); break;
case MODE_3 : printf("中国男足难度!\n"); break;
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <stdlib.h>

struct GPIO
{
unsigned char PA0 : 2;
unsigned char PA1 : 2;
unsigned char PA2 : 2;
unsigned char PA3 : 2;
unsigned char PA4 : 2;
unsigned char PA5 : 2;
unsigned char PA6 : 2;
unsigned char PA7 : 2;
unsigned char PA8 : 2;
unsigned char PA9 : 2;
unsigned char PA10: 2;
unsigned char PA11: 2;
unsigned char PA12: 2;
unsigned char PA13: 2;
unsigned char PA14: 2;
unsigned char PA15: 2;
};

enum mode
{
INPUT,
OUTPUT,
AF,
AN
};

int main(int argc, char *argv[])
{
struct GPIO P;

P.PA0 = OUTPUT;

P.PA10 = AF;

return 0;
}
预处理 条件编译 #ifndef __ADD_H #define __ADD_H 除了在.h文件中加入,防止头文件被重复包含以外 在.c文件中也可以使用
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
#if 0
printf("Hello World!\n");
#else
printf("你好,中国!\n");
#endif
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

#define SUB

int main(int argc, char *argv[])
{
int a = 13, b = 7;
int sum = 0;

#ifndef ADD
sum = a + b;
#else
sum = a - b;
#endif
printf("sum = %d\n", sum);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>

#define STM32F103CBT6

int main(int argc, char *argv[])
{
int PIN = 0;
#if defined (STM32F103RBT6)
PIN = 64;
#elif defined (STM32F103CBT6)
PIN = 32;
#endif
printf("PIN = %d\n", PIN);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>

#define FLAG 2

int main(int argc, char *argv[])
{
int a = 13, b = 7;
int sum = 0;

#if FLAG==0
sum = a + b;
#elif FLAG==1
sum = a - b;
#elif FLAG==2
sum = a * b;
#elif FLAG==3
sum = a / b;
#else
sum = a % b;
#endif

printf("sum = %d\n", sum);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>

#define PAI 3.14

int main(int argc, char *argv[])
{
printf("PAI = %f\n", PAI);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>

#define ARRSIZE 10

int main(int argc, char *argv[])
{
int arr[ARRSIZE];
int i = 0;

for(i = 0; i < ARRSIZE; i++)
{

}

return 0;
}
面试题 定义一个宏,计算一年(平年)有多少秒 定义一个宏,比较两个值的最大值 3.定义一个宏,完成两个变量值的交换 注意 : 在写宏函数的时候一定要加小括号,代表是一个整体
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>

#define SEC_OF_YEAR (365*24*60*60)

int main(int argc, char *argv[])
{
printf("SEC_OF_YEAR = %d\n", SEC_OF_YEAR);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

#define MUL(A,B) (A)*(B)
//MUL(A,B) A*B
//MUL(2+3,3+4) 2+3*3+4 = 15
int mul(int a, int b)
{
return a * b;
}

int main(int argc, char *argv[])
{
int a = 13, b = 7;

printf("MUL = %d\n", MUL(2+3, 3+4));//a+b
printf("mul = %d\n", mul(2+3, 3+4));//a+b

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>

#define MAX(A,B) A>B?A:B
//a++ > b++ ? a++ : b ++

int max(int a, int b)
{
return a > b ? a : b;
}

int main(int argc, char *argv[])
{
int a = 13, b = 7;

printf("MAX = %d\n", MAX(a++, b++));
a = 13;
b = 7;
printf("max = %d\n", max(a++, b++));

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>

//#define SWAP(A,B) {typeof(A)tmp; tmp=A; A=B; B=tmp;}
//#define SWAP(A,B) {(A)=(A)+(B);(B)=(A)-(B);(A)=(A)-(B);}
#define SWAP(A,B) {(A)^=(B);(B)^=(A);(A)^=(B);}

int main(int argc, char *argv[])
{
int a = 13, b = 7;

printf("a = %d b = %d\n", a, b);
SWAP(a,b);
printf("a = %d b = %d\n", a, b);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
printf("__DATE__ = %s\n", __DATE__);
printf("__TIME__ = %s\n", __TIME__);
printf("__FILE__ = %s\n", __FILE__);
printf("__LINE__ = %d\n", __LINE__);
printf("__FUNCTION__ = %s\n", __FUNCTION__);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int a = 3, b = 4, c = 5;

if((a + b > c) && \
(a + c > b) && \
(b + c > a))
{
printf("可以构成三角形!\n");
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>

#define PRINT_STR(n) #n

int main(int argc, char *argv[])
{
printf("PRINT_STR = %s\n", PRINT_STR(12+34));

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

#define INT_VAR(n) var##n

int main(int argc, char *argv[])
{
int var1 = 100;
int var2 = 200;
int var3 = 300;
int var4 = 400;
int var5 = 500;

printf("VAR = %d\n", INT_VAR(2));//var2
printf("VAR = %d\n", INT_VAR(5));//var5

return 0;
}

=============================

2022-7-15

=============================

一.编辑器

    vi
    gedit
    emacs
    ...
    我们主要使用vim

二.编译器

    gcc -> cc
虽然我们称gcc是C语言的编译器,但使用gcc编译C语言源代码文件不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译)->编译->汇编->链接.
        1.从一个.c文件编译成可执行文件需要经历4个阶段
            预处理器
            编译器
            汇编器
            链接器
        2.预处理时需要哪些操作
            文件包含
            宏定义
            条件编译

        gcc
        gcc 文件名	编译.c程序
        
        gcc hello.c
        什么也没有提示	证明程序没有问题(语法),编译通过
                        并不代表程序执行完全正确
        报出警告			会生成可执行文件,有可能执行成功	
        报出错误			100%程序有问题,不会生成可执行文件

        gcc -Wall 1.c
            W	警告
            all	所有
        gcc -o hello hello.c
            指定编译之后生成的可执行文件的名字
        编译生成 二进制可执行文件a.out
        执行./a.out

三.代码实例

    #include <stdio.h>
        /*
            #include	文件包含
            在C语言中以#开头是预处理的内容
            预处理阶段包括	文件包含	宏定义	条件编译
            stdio.h		头文件(.h结尾)标准的输入输出
            <...>		默认从/usr/include找头文件
            "..."		默认从本地目录中找头文件
        */

    int main(void)//void	空
    {
        printf("hello world!");
        /*
            printf是一个功能函数(打印输出的函数	终端中)
            在C语言中双引号引起来的表示为 字符串
            在C语言中单引号引起来的表示为 字符
            字符串 是由 一个一个字符组成的
            默认字符串最后有尾0 '\0'
            "hello" = 'h' + 'e' + 'l' + 'l' + 'o' + '\0'
        */

        return 0;
        /*
            return 也属于跳转语句,默认函数中碰到return函数就结束了
                return之后的值会返回给调用者
                返回值
                    1.返回的是结算结果
                    2.返回函数执行状态
                        0		函数正常结束
                        正值	函数有异常
                        负值	函数有错误
        */
    }
            main函数(主函数)	
            当执行程序时是从主函数进入,从上往下逐条逐条执行,程序结束时从主函数退出
            1.函数的声明
                函数的返回值类型 函数名(参数);//形参
            2.函数的实现
                函数的返回值类型 函数名(参数)//形参
                {
                    函数功能;
                }
            3.函数的调用
                函数名(参数);//实参

四.printf打印之转义字符(见C截图)

    注意虽然在写转义字符时是 \ + X,但是是一个字符

    转义字符		意义								ASCII码值(十进制)
    \a			响铃(BEL)						007
    \b			退格(BS) ,将当前位置移到前一列		008
    \f			换页(FF),将当前位置移到下页开头	012
    \n			换行(LF) ,将当前位置移到下一行开头	010
    \r			回车(CR) ,将当前位置移到本行开头	013
    \t			水平制表(HT) (跳到下一个TAB位置)	009
    \v			垂直制表(VT)						011
    \\			代表一个反斜线字符''\'				092
    \'			代表一个单引号(撇号)字符			039
    \"			代表一个双引号字符					034
    \?			代表一个问号						063
    \0			空字符(NULL)						000
    \ddd		1到3位八进制数所代表的任意字符		三位八进制
    \xhh		1到2位十六进制所代表的任意字符		二位十六进制

五.printf打印之标准化输出格式(见C截图)

    字符		对应数据类型		含义
    d / i	int				接受整数值并将它表示为有符号的十进制整数,i是老式写法
    o		unsigned int	无符号8进制整数(不输出前缀0)
    u		unsigned int	无符号10进制整数
    x / X	unsigned int	无符号16进制整数,x对应的是abcdef,X对应的是ABCDEF(不输出前缀0x)
    f(lf)	float(double)	单精度浮点数用f,双精度浮点数用lf(尤其scanf不能混用)
    e / E	double			科学计数法表示的数,此处"e"的大小写代表在输出时用的"e"的大小写
    g / G	double			使用以上两种中最短的形式,大小写的使用同%e和%E
    c		char			字符型。可以把输入的数字按照ASCII码相应转换为对应的字符
    s / S	char *			字符串。输出字符串中的字符直至字符串中的空字符
                                (字符串以'\0‘结尾,这个'\0'即空字符)
    p		void *			以16进制形式输出指针
    n		int *			到此字符之前为止,一共输出的字符个数,不输出文本
    %		无输入			不进行转换,输出字符‘%’(百分号)本身
    m		无				打印errno值对应的出错内容,(例: printf("%m\n"); )

    注:%g、%G在小数点位数四位或指数大于等于精度时用%e、%E,否则用%f。
    ---------------------
        标准化输出格式
        是由 % + * 组成,不是字符
        %		是占位符号
        %.2f	输出小数点后两位
        %.1f	输出小数点后一位
        %3d		输出的十进制数字最少占用3个字符位置
        %03d	空出的补0

六.变量

    1.变量是用来存储数据(在内存中进行存储)
    2.变量的定义
        数据类型	变量名;
        int			a;
        1>	数据类型分类
            整型		有/无符号		short int long
            实型		有符号		float double
            字符型	有/无符号		char
        2>	测试自己所用机器的类型所占字节数
            sizeof()是一个运算符,单位是字节
            sizeof(变量名/数据类型)
            1字节 = 8位
        3>	变量名的要求
            1)	变量名是区分大小写的
            2)	变量名不能与C语言的关键字重复
            3)	变量名可以由数字 字母 下划线组成,但是不能以数字开头
            4)	变量名尽量见名生义
        4>	在使用变量时需要注意的点
            1)	先定义再使用
            2)	便于编译时检查错误,不同类型的变量有不同的运算要求
            3)	定义变量之后,类型确定了,将来所占用的存储空间就确定了
    3.变量的赋值
        变量名	=	数值;
        a		=	1000;
        在复制时,赋值运算符需要把右边的项经过计算复制给左边的项(左值)
    4.变量的初始化
        数据类型	变量名	=	数值;
        int			a	=	1000;

    char
        char存储形式,1字节8位
        有符号类型的char可以存储的数的范围是(-128~127)
        char c = 13;//0000 1101
        XYYY YYYY
        第7位	为最高位,是符号位
        第6~0位	为数据位
        char c = 127;//1111 1111
        char c = -128;//1000 0000
    
    float
        float存储形式,4字节32位

        第31位		符号位	0代表正数,1代表负数
        第30~23位	阶位		转化成规格化的二进制之后与127做和
        第22~0位		尾数

        例:
        3.2
        正的所以最高位符号位为	0
        3	转化成二进制为11
        0.2	小数部分乘2取整,然后从前往后读
            0.2 * 2 = 0.4	0
            0.4 * 2 = 0.8	0
            0.8 * 2 = 1.6	1
            0.6 * 2 = 1.2	1
            0.2 * 2 = 0.4	0
        11.001100110011...
        然后将11.001100110011...的小数点向左移至小数点前只有一个1,即左移1位.
        阶码就是1+127 = 128  即:1000 0000
        尾数:因为小数点前必为1,所以记录小数点后面的数即可 100110011001...

       0100 0000 0100 1100 1100 1100 1100 1100

七.引入scanf概念

        int a = 0;
        int b = 0;

        scanf("%d-%d", &a, &b);//接收键盘上录入的数值
        1.	scanf中只写标准化输出格式就可以了,不要写其他的
        2.	scanf中后面的变量必须要加 &(取地址)
        3.	scanf中的双引号内不要写 '\n'
        printf("a = %d\nb = %d\n", a, b);//打印到屏幕上

        return 0;
==================================
作业
    1.把今天讲过的内容完完整整的复习一遍
        明天会提问,回答不上来的同学,罚在纸上抄写10遍
    2.可以找一些编程的问题做一做
==================================

八.运算符

    1.算术运算符:* - + / %
    
    2.关系运算符: > < == != >= <=

    3.逻辑运算符:! && ||
    
    4.位运算符:<< >> ~ | ^ &(异或相同为0,不同为1)
    
    5.赋值运算符:= += -= *= /= %= >>= <<= &= |= ^=
    
    6.条件运算符:?:    max = (a > b) ? a : b
    
    8.指针运算符:*和&
        *	取值
        &	取地址
        是可逆的
    
    9.求字节数运算符:sizeof()
        sizeof()	放变量/数据类型
    
    10.强制类型转换运算符:(类型)
    
    11.分量运算符:. -> (结构体)
        结构体
        共用体
    
    12.下标运算符:[ ]
        int arr[5];	//	5个成员
        arr[0] ~ arr[4]//	1.数组名 + 下标
    
    13.自增自减运算符:++ --
            6 / 2 * (2 + 1)
    
    14.其他:如函数调用运算符:()
    
    注意:条件运算符是C语言中惟一一个三目(三元)运算符

    凡是位运算都需要把操作数变成补码形式
    操作数为正数,那么补码是它的本身(原码 = 补码)
    操作数为负数,无论 原码转补码 还是 补码转原码,都是除了符号位不变,其他位按位取反再加1
               char a = 13;    ~a;
               二进制(原码)
               0   0   0   0   1   1   0   1
               因为是个正数,所以补码 = 原码
               补码(取反操作之前)
               0   0   0   0   1   1   0   1
               ~
               1   1   1   1   0   0   1   0
               补码(取反操作之后)
               1   1   1   1   0   0   1   0
               反码
               1   1   1   1   0   0   0   1
               原码
               1   0   0   0   1   1   1   0

               char a = -13;   ~a;
               原码
               1   0   0   0   1   1   0   1
               反码
               1   1   1   1   0   0   1   0
               补码(取反之前)
               1   1   1   1   0   0   1   1
               ~a
               0   0   0   0   1   1   0   0
               补码(取反之后)
               0   0   0   0   1   1   0   0
               原码
               0   0   0   0   1   1   0   0
=================================
作业
1.把今天讲过的内容完完整整的复习一遍
2.如果复习完之后,可以找一些编程题目做一做
=================================

=============================

2022-7-18

=============================
## 分支语句
### if
int main()
{
语句1;
if(条件)// 真假
{
语句2;
}
语句3;
}
语句1–>判断if条件(真)–>语句2–>语句3–>结束
|
假–>语句3–>结束

if…else

int main(void)
{
    语句1
        if(条件)
        {
            语句2
        }
        else
        {
            语句3
        }
    语句4
}
语句1-->条件判断(真)-->语句2-->语句4
                 |
                 假-->语句3-->语句4

if…else if…else if…

int main(void)
{
    语句1
        if(条件1)
        {
            语句2
        }
        else if(条件2)
        {
            语句3
        }
        else
        {
            语句4
        }
    语句5
}
语句1-->判断条件1(真)-->语句2-->语句5
                  |
                  假-->判断条件2(真)-->语句3-->语句5
                                 |
                                 假-->语句4-->语句5

//比较两个值是否相等一定要用 ==
//if 里的大括号一定不要丢

switch

语句1
switch(变量)
{
    case 条件1 ://条件是整常量 可以是 数字 或者 字符
        语句2
            break;
    case 条件2 :
        语句3
            break;
    default:
        语句4
            break;
}
语句5
语句1->(变量匹配)条件1(真)->语句2->break->语句5
                    |
                    假->条件2(真)->语句3->break->语句5
                            |
                            假->语句4->break->语句5

***************
注意:
switch里加break,
匹配完条件->执行完语句->直接跳出switch

如果不加break
匹配条件->执行语句->顺序往下执行

如果case里加return
执行到return语句直接退出程序
***************
break(可以在switch和循环中用)
跳出switch
break 和 return区别
break 是跳出 switch体
return是跳出这个函数
(main函数也是函数,如果main()函数里
 执行到return,代表程序的结束)
------------------
练习
键入一个整数x,
如果小于3则执行x+3操作,
如果大于等于3则执行3*x+9
如果大于9则执行9*x-10
------------------
homework
1.输入三个值判断这三个值是否构成三角形
(是否是直角 等腰 等边 普通 不能)	

2.输入三个整数x,y,z,请把这三个数由从小到大输出

3.学习成绩>=90分的同学用A表示,60-89分之间的用B表示,小于60用C表示

4.输入成绩
0-59    不及格
60-69   及格
70-79   中
80-89   良
90-100  优
(考虑哪个阶段的人多->程序效率上考虑)
20000学生	其中15000个学生	都是80-89

5.停车场收费系统,3小时以内,收费每小时30元,超出部分每小时50元

6.输入相应的字符打印相应的语句(默认不区分大小写)
输入a,打印	钟薛高18块钱
输入b,打印	梦龙8块钱
输入c,打印	老冰棒1块钱
输入d,打印	雪莲0.5块钱

7.判断输入的4位数是否是回文数(1221是回文数, 1234不是)

8.输入年份判断该年是否是闰年	RTC(实时时钟)
//闰年
//能被4 整除但是不能被100整除 能被400整除

9.输入一个整数,判断是否既是3 也是5 又是7的倍数 (num % 105)
==================================================
10.输入年月日,打印出是该年的第几天
//2015-9-25
是2015年的第?天

11.输入相应的字符打印相应的语句(默认不区分大小写)
输入a,打印	钟薛高18块钱
输入b,打印	梦龙8块钱
输入c,打印	老冰棒1块钱
输入d,打印	雪莲0.5块钱

12.输入成绩
0-59    不及格
60-69   及格
70-79   中
80-89   良
90-100  优
+++++++++
1.定义变量a,通过键盘录入a的值,判断a的值是否是偶数
2.定义变量year,通过键盘录入year的值,判断year是否是闰年
    能被4整除但是不能被100整除的年份是闰年
    或者能被400整除的年份是闰年
3.定义三个变量a b c,通过键盘录入三个非零的正整数
    判断是否可以构成三角形
===================================
作业:
    1.把今天讲过的内容完完整整的复习一遍
    2.复习之后,写上面的编程题目(1 - 9使用if来做)(10 - 12使用switch)
注意:
    1.千万不要忽略复习的重要性
    2.千万不要眼高手低(程序再小,也是程序,一定要亲手写一遍,才能说自己会)
===================================

=============================

2022-7-18

=============================

循环语句

for

for(循环的初始化; 循环控制部分; 循环的修改部分)
{
    循环主体;
}
for
循环初始部分……为循环变量附初值
循环控制部分……按照循环条件控制循环正常进行
循环的修改部分……循环执行中,循环变量按照一定规律变化
循环体(工作部分)……要多次循环的代码部分
#include <stdio.h>

int main(void)
{
    int i = 0;//循环变量
    int sum = 0;
    for(i = 0; i <= 100; i++)
        //i = 0循环的初始部分为循环变量赋初值
        //i <= 100循环的控制部分,控制循环的进行
        //i++循环的修改部分,修改循环变量
        //sum += i循环体,循环的主体
    {
        sum += i;
    }
    printf("sum = %d\n", sum);
    return 0;
}

流程图
                for i = 1循环初始部分
                    |
                i <= 100 循环控制部分<---
            否------|是                 |
            |   sum = sum + i;循环体    |
            |       |                   |
            |   i++ 程序修改部分---------
            |
            |
            |
            |_______
                    |
                其他语句

其他形态

(1)
    for(a = 0, b = 0; a != 3 && b != 3; a++, b++)
{

}
(2)
    for(; ;)
{
    需要循环的部分;
}
死循环
(3)
    for(a = 0; a < 10; a++)
    {
        for(b = 0; b < 10; b++)
        {
    
        }
    }

随机函数

    rand()//产生随机数
    srand()//随机种子

    int a = 0;
    int b = 0;

    srand(time(NULL));
    //rand()     产生随机数
    //srand()    随机种子
    //time(NULL) 时间
    //NULL       空
    for(a = 0; a < 10; a++)//随机取出10个(50~100以内的数)
    {
        b = rand() % 50 + 50;
        //rand() % 50       [0~49]范围
        //rand() % 50 + 50  [50~99]范围
        printf("b = %d\n", b);
    }

break

//break 应用于循环和switch中

跳出 循环
跳出 switch

//如果遇到嵌套循环的话,break 只跳出最近的循环

continue

//continue 只应用于循环当中

跳出本次循环

//如果遇到嵌套循环的话,continue也只是跳出本次循环

homework

1.
(1)打印4 * 5 的 *
例:
*****
*****
*****
*****
(2).打印i * j 的 * (i j 键盘接收)
    例:i = 4, j = 5
    //4-5 5-6
    *****
    *****
    *****
    *****
(3).打印i * j 的 * 边框(i j 键盘接收)
    例: i = 4, j = 5
    *****
    *   *
    *   *
    *****
2.打印99乘法表
    1 * 1 = 1
    1 * 2 = 2 2 * 2 = 4
    *
    **
    ***
    ****


3.计算1+2+3+4+5+...+n, n是从键盘输入的自然数

// 穷举
4.要将5张100元的大钞票,换成等值的50 20 10 5小钞票,每种面值至少一张,要多少张,有多少种换法

5.有1、2、3、4 4个数字,能组成多少个互不相同且无重复的三位数呢

6.有这样的一个等式,xyz+yzz=532,编程求x y z的值

7.计算1^2 + 2^2 + 3^2 + ... +10^2的结果
(1的平方 + 2的平方)

8.输入20个人的成绩,输出不及格的人数(0<=score<60 为不及格)

9.随机产生100个三位随机数,找出其中能被 3 5 7整除的数

10.打印0~100以内所有的质数
(只能被1 和 本身整除的数)

11.猜数游戏(随机产生一个1000以内的数,去猜,
        如果大了打印猜的大了,
        如果小了打印猜的小了,
        如果正确打印对的
        直到猜对了再结束,每次打印必须打印出猜的次数)

12. 把11修改成,程序开始时,先输入需要猜的数的范围n
例:(想要猜数的范围是(0~10000) 那么变量n接收键盘上10000
        范围都是从0开始)

13.
----------------------------
a + b - 9 = 4
+	-	-	
c -	d * e = 4
/	*	-
f + g - h = 4
=	=	=
4	4	4
求a b c d e f g h (0 ~ 12范围内)//除法指的是整除 c % f == 0
只有一种答案
===================================
作业:
1.把今天讲过的内容完完整整的复习一遍
2.来做上述的编程题目
3.如果还有时间可以自己再找一些题目来做
4.有问题微信上直接找张老师
===================================

while()

语句1

while(条件)
{
    循环体
}

语句2
                            ---------------
                            |             |
        语句1 --> 条件判断(真) --> 循环体 ---|
                            |(假)
                            |--------------->语句2

    /*
       while(条件)//必须要有条件
       {
           主体
       }
       当满足条件的时候,进入while循环,执行循环体
     */

while(1)//死循环

=================================
do...while
//和while相比较 先执行在判断
// do...while 至少循环一次

do
{
    需要循环的语句;
}while();

n = 1;
do
{
    sum += n;
    n++;
}while(n <= 100);

==============================
break

//break 应用于循环和switch中

跳出 循环
跳出 switch

//如果遇到嵌套循环的话,break 只跳出最近的循环

continue

//continue 只应用于循环当中

跳出本次循环

//如果遇到嵌套循环的话,continue也只是跳出本次循环

goto// 跳转语句

i = 1;//循环变量的初始

loop://标记

if(i <= 100)//循环变量的的控制
{
    sum += i;//循环体
    i++;//循环变量的变化
    goto loop;//跳到标记处
}
面试题:
(1)从键盘上接收一个正整数,打印输出它的倒序
例:
12345
    54321
(2)
如果是一个负数
例:
-1234

-4321
(3)
    atoi()把字符串转换成十进制整数
    "123"		->	123
atox atoo
"0x40008000"	->	0x40008000
"1234"			->	1234
实现一个 itoa() 函数

12345
"12345"
=====================================

homework

14.求1+12+123+...前N项之和,其中N是从键盘输入的数(N取值范围(1~9))
    n = 5;
    sum = 1 + 12 + 123 + 1234 + 12345;

15.爱因斯坦出了一道这样的数学题,有一条长阶梯,若每步跨2阶,则最后剩1阶,若每步跨3阶,则最后省2阶,若每步跨5阶,则最后剩4阶,若每步跨6阶,则最后剩5阶只有每次跨7阶,最后才正好1阶不剩,请问在这条阶梯最少多少阶

16.设N是一个4位数,它的9倍恰好是它的反序数
    例:1234 反序数是4321

17.求s = a + aa + aaa + aaaa + ... + aa...a的值,其中a是1~9的一个数字(不超过十项)
例:a = 3; s = 3 + 33 + 333;

18.“百钱百鸡”问题,百钱买百鸡,公鸡1个3块钱,母鸡1个2块钱,小鸡3个一块钱,问公鸡,母鸡,小鸡各多少个

19.打印三角形	(画8个三角形)
* * * * *
  * * * *
    * * *
      * *
        *
6_1.打印三角形
    录入n,n是直角三角形的高
    例:	n = 3
      * 
    * * * 
  * * * * *
    n = 4
      * 
    * * * 
  * * * * * 
* * * * * * * 


20.猴子吃桃问题,有一天一只猴子摘了一些桃子,它吃掉一半,又吃了一个,第二天也是这样,到了第十天,只有一个桃子了,求猴子摘了多少桃子

21. 4 5 6 7总共能组成多少个不能被4整除的4位数

22.求n!(n的阶乘)
====================================
作业
1.把今天讲过的内容完完整整的复习一遍
2.整理笔记
3.写编程题目
====================================

小项目

                        电子版的口算卡
随机生成10道100以内的 [加 减 乘 除 取余] 数学题 回答正确的加10分错误不加分(把正确结果打印出来) 然后显示最后成绩。(不用float类型)	
1>	操作数随机  2>	运算符随机
gcc 1.c		当执行./a.out之后,先清屏
23 + 70 = 93
Bingo!	+10分
15 - 30 = -15
Bingo!	+10分
23 * 87 = 0
Error!	====== 正确结果打印出来
总分 70分
====================================

指针

机器
CPU     内存    硬盘
快      中      慢

int a = 100;//初始化int类型变量
int *p;//定义指针

p = &a;//把a的地址传给指针p
//& 取地址符

printf("a = %d\n", a);

*p = 200;//通过指针改变值

printf("*p = %d\n", *p);
//* 取指针所保存的地址所在的位置里的值
// 可以简化成取值

不同数据类型

printf("char = %lu\n", sizeof(c));
printf("short = %lu\n", sizeof(s));
printf("int = %lu\n", sizeof(i));
printf("float = %lu\n", sizeof(f));


int *p; int代表以后在保存地址的时候变量的数据类型
所有指针在内存中占8个字节(64位操作系统)


int a = 100;

变量名      值      址
a           100     0x1000
0x1001
0x1002
0x1003
pa          0x1000  0x2000


注意:
1.指针所占字节数不分类型都是 8个字节(分操作系统)

int *p;
char *c;
2.pa += 1//pa++改变了pa所存的地址值
当指针自增p++改变的是指针保存的地址,改变的字节数
和所存地址值的变量的类型有关p++ 改变了p所存的地址值
p+1 没有改变p所保存的地址值

3.如果想要操作指针,改变指针 用二级指针
取地址符(&)不管是变量还是指针只能加一个
对于一级指针来说,取值符(*)只能加一个

4.* 和 & 是一个可逆的过程,可以中和

printf("*(&a) = %d\n", *(&a));	对
printf("&(*a) = %d\n", &(*a));	错

/*一级指针 操作不了一级指针*/

a           pa          pp
值  100         &a          &pa

址  0x1000      0x2000      0x3000

a           pa          pp
值  100         0x1000      0x2000

址  0x1000      0x2000      0x3000

======================================

二级指针

注意:
1.二级指针所占字节数不分类型都是 8个字节
(分操作系统)

int **p;
char **c;
2.
二级指针也可以做加法运算
int **p;
p + 1;	p++;	p+=1;
当二级指针偏移一个单位跨8个字节

3.如果想要操作指针,改变指针 用二级指针
取地址符(&)不管是变量还是指针只能加一个
对于一级指针来说,取值符(*)只能加一个

4.* 和 & 是一个可逆的过程,可以中和

printf("*(&a) = %d\n", *(&a));	对
printf("&(*a) = %d\n", &(*a));	错

函数

    void    空
返回值的类型 函数名(参数)
{
    功能
}

main()是程序的入口;


函数之间的调用

注意:
2.调用的函数写到被调用函数的下面
3.函数可以先定义,再写函数的实现

//函数的定义
void print(int c);

void print(int c)//显示 函数的实现
{
    printf("c = %d\n", c);
}
参数传递

值传递
a = 3, b = 4;
swap(a, b)
    当main函数调用swap函数的时候,
    只是把变量的值传过去
    相当于swap(3, 4)

地址传递
a = 3, b = 4;
swap(&a, &b)
    当main函数调用swap函数的时候,
    把a和b的地址传过去
相当于swap(0x1000, 0x2000)
/*
   如果函数想要改变main函数的变量的话,
   可以传变量的地址
   如果函数只是想用main函数地变量的值的话,
   可以传变量的值
 */
======================================

猜数游戏(随机产生一个1000以内的数,去猜,
        如果大了打印猜的大了,
        如果小了打印猜的小了,
        如果正确打印对的
        每次打印必须打印出猜的次数)


口算卡
    随机10道口算卡题目

        100 + 100 = 300
        wrong! ========= 200
    
    1.操作数100以内随机
    2.运算符 + - * / % 随机
    3.不用float
    4.多函数实现

第一阶段
    只写一个.c文件,.c文件中只有一个main函数
第二阶段
    只写一个.c文件,,c文件中有一个main函数,多个功能函数
第三阶段
    写多个文件
    main.c		主函数和功能函数调用
    func.c		功能函数的实现
    func.h		功能函数的声明
    makefile	编译规则
第四阶段
    把func.c做成库文件	动态库	静态库

======================================
作业
    1.把今天讲过的内容从头到尾复习一遍
    2.把之前打印8个三角形的程序整合一个程序,其中打印图形的功能封装成函数
    3.猜数游戏(随机产生一个1000以内的数,去猜,
        如果大了打印猜的大了,
        如果小了打印猜的小了,
        如果正确打印对的
        每次打印必须打印出猜的次数)
    4.口算卡
    随机10道口算卡题目

        100 + 100 = 300
        wrong! ========= 200
    
    1.操作数100以内随机
    2.运算符 + - * / % 随机
    3.不用float
    4.多函数实现

数组

一维数组

int a[5];

a[0]		a[1]		a[2]
1		2		3
0x1000	0x1004	0x1008

命名规则
int a[5] = {1, 2, 3, 4 ,5};
int b[5] = {0};
waring!
int c[]  = {1, 2, 3, 4, 5};
   int a[10];
   int *p = a;

   a[i];   数组+下标
   p + i;  指针+偏移
   a + i;  数组名+偏移
 	p[i]	指针+下标

&arr[0] = 0xbf8c97ec	1
arr = 0xbf8c97ec		2
&arr = 0xbf8c97ec		3
1 2 数组首元素的地址
3	数组的地址
+++++++++++++++++++++
&arr[1] = 0xbf8c97f0	
arr + 1 = 0xbf8c97f0
&arr + 1 = 0xbf8c9800


一维数组特性
1.数组名可以当指针来用,利用地址加法运算,计算出数组元素地址加*取出地址中的内容
注:a[i] = *(a + i)

2.sizeof(数组名) = 整个数组长度(整个数组所占字节数)

3.数组名是常量,不能改变指向

p = &i;//指针可以保存别的地址
a = &i;//数组名不可以

*(a + i);
a += i;//a = a + i	a++;	数组名都不可以这样操作
4.数组名可以当指针来用,但是,不是指针

多维数组

   int a[15][15]
   char a[15][15]
int a[2][3] = {0} 
a[0][0] a[0][1] a[0][2]
a[1][0] a[1][1] a[1][2]

内存
a[0][0]         0x0
a[0][1]         0x4
.
.
.
a[1][2]

在内存中是顺序的存储

命名规则
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int a[3][4] = {0};
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
////!!!!!
int a[][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};

二维数组中行数可以省略,列数不可以

二维数组特性
1.数组名可以当指针来用,利用地址加法运算,计算出数组元素地址

    加*取出地址中的内容
    注: a[1][2] = *(*(a + 1) + 2)
    a[2][3] = *(*(a + 2) + 3)
    a[i][j] = *(*(a + i) + j)
2.sizeof(数组名) = 整个数组长度(不是指针长度)

    a[4][5];
    sizeof(a) / sizeof(a[0])        算多少行
    sizeof(a) / sizeof(a[0][0])     算数组成员个数
    注: 算多少行sizeof(a)/sizeof(a[0])
算多少列sizeof(a[0])/sizeof(a[0][0])
    sizeof(a)是整个数组的大小
sizeof(a[i]是第i行的大小)

    3.数组名是常量,不能改变指向

    4.数组名可以当指针使用,但不是指针

    printf("&a[0][0]    =   %p\n", &a[0][0]);
    //数组首元素的地址
    printf("a[0]        =   %p\n", a[0]);
    //首行第一个元素的地址
    printf("a       =   %p\n", a);
    //首行的地址
    printf("*a      =   %p\n", *a);
    //首行第一个元素的地址
    printf("&a      =   %p\n", &a);
    //数组的地址




    三维数组    =>  多个二维数组组成
    int a[2][4][4] ={{{0,0,,0},
			{0,0,0,0},
			{0,1,0,0},
			{0,1,1,1}},
        {{0,0,0,0},
				{0,0,0,1},
				{0,0,0,1},
				{0,0,1,1}}}
            指针与数组

            在机器中都是用指针操作的

a[0] = *(a + 0)
a[1] = *(a + 1)
    ...
a[i] = *(a + i)

二维数组与指针

    二维数组的可以当一个指针使用
a[1][2] = *(*(a + 1) + 2)
a[2][3] = *(*(a + 2) + 3)
    ...
a[i][j] = *(*(a + i) + j)

三维数组和指针

    三维数组的可以当一个指针使用
a[1][2][3] = *(*(*(a + 1) + 2) + 3)
a[2][3][4] = *(*(*(a + 2) + 3) + 4)
    ...
a[i][j][k] = *(*(*(a + i) + j) + k)

数组和函数

/*
   一维数组
   int arr[5];
   调用时: init(arr);直接传数组名
   实现时: void init(int *p)   或  void init(int arr[5])   void init(int arr[])
   二维数组
   int arr[4][4]
   调用时: init(arr);
   实现时: void init(int p[][4]) 或 void init(int p[4][4])

 */
//当main函数中函数传参是数组名的时候,
//函数中可以用数组形式或者指针形式

//当main函数中函数传参是二维数组的数组名的时候,
//函数中可以用数组形式
=============================

homework

(1)输入十个人的身高,求出平均值,并把小于平均值的打印出来	

(2)输入十个人的身高,要求排序,从低到高打印

    排序		(冒泡	选择)

例:
100   201    50   70   30

(3)完成itob函数(十->八)
十 -> 二
void itob(int num, int arr[8]);
100
01100100

(4)
    阅读
    man strcpy	
    man strcat
    man strcmp
    man strlen

(5)
有1000发子弹,要提前装到10个箱子里面,接收键盘输入,要取多少颗子弹数,只能显示整箱的个数,问这10个箱子怎么装,编程实现
============================
========================
小项目:五子棋
    1.player VS player
要求
    1>最少完成p v p版本
    2>用多函数实现
    3>完成五子棋最基本功能即可
    4>要有适当的提示信息
    5>代码中要有注释
    6>完成后上传到邮箱中(402403579@qq.com)
        2022-2-11_张三.c
提示
    1>棋盘存储
    2>坐标的打印
    3>玩家的替换
    4>下到重复位置/下出边界以后的处理
    5>判断赢的条件


字符数组初始化
char a[6] = {'a','b','c','d','e','f'};
char b[6] = {'h','i','j','k','l','\0'};
char c[] = {'h','i','j','k','l','m'};
char d[] = "hello world";

    "..."	字符串,默认最后有\0

如果碰到了 \0 表示结束,字符串, %s

char arr[100] = "hello world!";//字符数组   存储功能
char *p = "hello world!";//字符指针     只是保存地址
//字符指针保存的是 常串 的地址


================================
    周末作业
1.把这一周讲过的内容先完完整整的复习一遍,有问题直接问
2.阅读man手册 strcpy strcat strcmp strlen
3.调用系统实现的string函数的功能,实现自己写的功能
    mystrcpy mystrcat mystrcmp mystrlen
4.后续作业的1 - 5题

5.剩余时间去做五子棋的项目

work

man string.h
string一族的函数,strcpy strcmp strcat strlen

char *mystrcpy(char *dst, const char *src);

char *mystrcat(char *s1, const char *s2);

int mystrcmp(const char *s1, const char *s2);

size_t mystrlen(const char *s);	

char *p = "root:x:0:0:root:/root:/bin/bash";

homework

1.输入一行字符,分别统计出其中英文字母、空格、数字和其它字符的个数。

2.输入一个字串,判断它是否是对称串。如”abcdcba”是对称串,”123456789”不是。

3.输入一行字符串,按如下规则加密:如果是英文字母则大写变小写、小写变大写,对非英文字符则保持不变。试写加密程序。

4.输入一行小写英文字母,按如下规则加密:a→c、b→d、…、x→z、y→a、z→b,试写加密程序。

5.输入一行英文,已知各单词之间用1个空格相隔(设第一个单词前没有空格),统计这行英文有多少个单词。

6.完成atoi函数
    int myatoi(const char *nptr);

7.完成itoa函数
    void itoa(int data, char str[]);

如果不是自己开辟内存空间,当程序结束后自己释放掉

如果不是自己开辟内存空间,当程序结束后自己释放掉
[不用程序员管理]
*****************
谁开辟,谁释放       [程序员自己开辟空间]/*内存耗尽 内存泄露*/
谁创建,谁销毁       [链表]/*泄露*/
谁加锁,谁解锁       [线程]/*防止Bug*/
谁打开,谁关闭       [文件操作]/*泄露*/
*****************

代码区
        管不了
栈区
        系统帮助程序员管理
        不需要程序员自己开辟空间,释放空间
        自动释放
堆区
        malloc->free
        zalloc->free
        程序员需要自己管理的内存空间
        自己开辟,自己释放
        申请和释放都是程序员管理
        (忘记释放会使内存耗尽)
全局区
        全局变量
常数区
        程序结束才会释放
        (char *p1 = NULL;)
        (p1 = "abc";)
内存    真实存储    4G
只用内存真实空间    4K
虚拟内存            4G
1G      kernel(内核)
--------------------
3G      environment/cmd环境变量(系统设定的全局变量)和命令->argc argv
int main(int argc, char *argv[])
栈向下     从高地址到低地址


堆向上     从低地址到高地址
.bss    静态存储区(全局变量,静态变量) !未初始化
.date   初始化的 不为0                !已初始化
RO date 只读数据段
.text    代码段

.bss存在可执行文件中
.date不存在可执行文件中


        char *p = "abc";


=====================================



                                        printf.o
                               				|	
                               				V
                                        可重定位	可执行
        源程序	被修改的源程序	汇编程序	    目标程序	   目标程序
        hello.c---->hello.i---->hello.s---->hello.o---->hello
            预处理器(cpp)  编译器(ccl)  汇编器(as)   连接器(ld)

    第一个阶段	只写一个main.c
    第二个阶段	多函数
    第三个阶段	多文件
                func.c
                func.h
                main.c
                makefile


makefile
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。

=====================================

变量作用域

存储区域        作用域          生命周期

全局变量        局部变量

==========================================================

全局变量

1.尽量不使用全局变量
2.使用时加static(静态)
3.显示初始化

main.c
#include <stdio.h>

    int xxx;

int main(void)
{
    printf("main.xxx = %d\n", xxx);
    foo();
    printf("main.xxx = %d\n", xxx);
    return 0;
}
foo.c
#include <stdio.h>

int xxx;
int foo()
{
    xxx++;
    printf("foo.xxx = %d\n", xxx);
}
全局变量中默认定义的变量值为0;

变量与函数关系
外部变量
内部变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <stdio.h>
int x=1; //外部变量 全局变量
//1、生命周期 整个程序
//2、作用域 整个程序
//3、存储区域 全局区
void A()
{
int y=1;//内部变量 局部变量
//1、生命周期 函数体内
//2、作用域 函数体内
//3、存储区域 栈区
x++;
if(1)
{
int z=0;//块级变量
//1、生命周期 块内有效
//2、作用域 块内有效
//3、存储区域 栈区
printf("z=%d\n",z);
}
{

int m=0;//块级变量
//1、生命周期 块内有效
//2、作用域 块内有效
//3、存储区域 栈区
printf("m=%d\n",m);
}
printf("A中的x=%d\n",x);
printf("A中的y=%d\n",y);
printf("A中的&y=%p\n",&y);
}
void B()
{
x++;
int i;
int j;
int y;
int k;
printf("B中x=%d\n",x);
printf("B中的y=%d\n",y);
printf("B中的&y=%p\n",&y);
}
void C()
{
int x=100;
printf("C中的x=%d\n",x);
{
int x=1000;//(1)如果是x = 1000;
printf("C块中的x=%d\n",x);
}
printf("C中的x=%d\n",x);//(1)x = 1000;

}
int main()
{
A();
B();
C();
return 0;
}

静态变量

1.存储区域  全局静态区
2.作用域        函数体内
3.生命周期  整个程序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
	#include <stdio.h>
static int z = 1; //静态外部变量限定作用
//1.存储区域 全局区(静态区)
//2.作用域 本文件
//3.生命周期 整个程序
int m = 0;//外部变量

void A()
{
static int x = 0;//静态内部变量
//1.存储区域 全局区(静态区)
//2.作用域 函数体内
//3.生命周期 整个程序
int y = 1;//内部变量(栈区)
x++;
y++;
printf("x = %d\n", x);
printf("y = %d\n", y);
}
{
12 if(i == 0)
13 return ;
14 printf("%d", i % 10);
15 func(i / 10);
16 }

int main(void)
{
A();//第一次调用x初始化一次,以后就不初始化了
A();
A();
A();
return 0;
}
```
## 函数与函数之间的关系
//函数与函数之间是外部关系(并列)
//函数体内不能再写函数
//静态函数(本函数只能在本文件使用)

/*函数与函数之间的关系*/
```C
#include <stdio.h>
void A();//声明函数
void B();

void A()//函数与函数之间是外部关系(并列)
//函数体内不能再写函数
{
B();
printf("A func()\n");
}

void B()//静态函数(本函数只能在本文件使用)
{
printf("B func()\n");
}
//A 不能调用B
//B 可以调用A
int main(void)
{
A();
return 0;
}
===================================== 写C语言程序的四个阶段 第一阶段 只写一个.c文件,在.c文件中只写一个main函数,把所有功能都放到main()中 第二阶段 只写一个.c文件,在.c文件中除了main函数以外还有其他的功能函数 函数的声明 函数的返回值类型 函数名(参数列表);//该位置的参数叫做形式参数 函数的实现(函数的定义) 函数的返回值类型 函数名(参数列表)//该位置的参数叫做形式参数 { 功能代码 } 函数的调用 函数名(参数);//该位置的参数叫做实际参数 函数名是函数的入口地址,函数名就是地址 形式参数接收实际参数的值 值传递 地址传递 第三阶段 多文件的方式实现项目 main.c 存放main函数,以及功能函数的调用 func.c 存放功能函数的实现 func.h 存放功能函数的声明,以及各种定义 makefile(Makefile) 存放编译规则 第四阶段 把.c文件做成库文件 动态库 静态库

二.makefile的简介

    一个项目工程中的源文件不计其数,这些源文件按类型、功能、模块分别放在若干个目录中.
    makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译.
    哪些文件需要重新编译,甚至于进行更复杂的功能操作.
    因为 makefile 就像一个Shell脚本一样,其中也可以执行操作系统的命令.
----------------------------------------
    vim 打开多个文件
        vim * -p	多文件以标签的形式打开
        注意 : 不能打开过多的文件,否则vim会崩溃
    用ctrl + alt + pgup / pgdn切换文件
----------------------------------------

三.C语言的作用域

1.空间的分布
    C语言会把内存分为5个区域(栈区 堆区 常量区 全局区 代码区)
    --------------------------------------------------------
    ./a.out->是由进程在执行可执行程序时分配存储空间
    ->4Kb(物理)->4Gb(虚拟空间)

    1Gb		Kernel(内核)
    --------------------
    3Gb		命令行参数和环境变量int main(int argc, char *argv[])
            栈区
            👇
            空白
            ☝
            堆区
            静态区(全局区)未初始化的(系统默认会初始化成0)
            静态区(全局区)已初始化的
            常量区(RO DATA)只读的区域
            代码区

    栈区	不需要程序员来管理,自己开辟,自己释放
    堆区	需要程序员来管理,手动开辟,手动释放(malloc realloc zalloc free)
    静态区	全局变量/static关键字修饰的变量
    常量区	read only(只读)
    代码区	程序员没有办法管理

2.全局变量和局部变量
    
    局部变量
    在某个函数内部或者程序块内部定义的变量叫做局部变量
    局部变量只能在该函数的内部 或者 该程序块的内部使用
    局部变量在函数外部是不可知的

    全局变量
    全局变量是定义在函数外部的,通常写到程序的顶部
    全局变量在整个程序中生命周期都是有效的
    在任意函数的内部都可以访问全局变量

    注意:
    全局变量和局部变量可以同名
    如果出现了同名的情况,那么在执行函数时优先使用局部变量,
    在该函数中全局变量被屏蔽了

    全局变量和局部变量在定义时的区别
        当定义局部变量时,系统不会帮助初始化,局部变量内部存储的是垃圾值
        当定义全局变量时,系统会帮助初始化
            int -> 0
            char -> '\0'
            pointer -> NULL

    全局变量和局部变量存储空间的区别
        全局变量 : 保存在内存的静态全局区,占用的是静态的存储单元
            当程序运行时已经被创建了,当程序结束时被释放
        局部变量 : 保存在内存的栈区中,只有在局部变量所在的函数被调用时,
            系统才会帮助分配存储单元,当局部变量所在的函数结束时被释放

四.存储类关键字

1.extern关键字
    extern是C语言中的关键字
    修饰的是外部的
    可以修饰函数,也可以修饰变量			
    1>如果函数被extern关键字修饰了,证明其他文件也可以使用这个函数,
        如果定义的函数不想被其他文件使用,可以使用static关键字修饰
    2>如果变量被extern关键字修饰了,需要注意不要初始化,在Linux gcc环境中会报Error
    如果在本文件中函数或者变量使用extern关键字修饰了
        说明该函数或者变量在别的文件中定义了,在当前文件中使用
    当编译器在编译程序时,如果碰到了extern关键字,那么会从别的文件中查找相关的定义

2.static关键字
    static是C语言中的关键字
    修饰的是静态的(内部的)
    可以修饰函数,也可以修饰变量
    为了使全局变量可以安全的使用,请在定义全局变量时,使用static关键字进行修饰
    a.如果全局变量没有使用static关键字修饰,可以跨文件使用
    A.如果全局变量使用了static关键字修饰,仅限于在当前文件内使用

    b.如果局部变量没有使用static关键字修饰,该局部变量存储在栈区中,当函数结束后被释放
    B.如果局部变量使用了static关键字修饰,该局部变量存储在静态区中,当程序结束后被释放

    c.如果局部变量没有使用static关键字修饰,每次调用函数该局部变量都会被重新初始化
    C.如果局部变量使用了static关键字修饰,只在第一次调用函数时进行初始化,也是唯一的一次初始化

    d.被static关键字修饰的内容,系统会默认进行初始化

    1>如果函数被static修饰了,该函数只能在本文件内使用,不能跨文件使用
        static void swap(....)
        {
            
        }	
        void pop_sort(....)
        {
            
        }
    2>如果变量被static修饰了

    3>静态变量
        静态变量所占用的存储空间是静态区
        静态的全局变量 : 只能被变量所定义的文件中进行使用
                        (其他文件即使加了extern关键字也使用不了)
        静态的局部变量 : 只能被变量所在的代码块中进行使用
    总结 : 
        如果本文件内的函数和变量想要让别的.c文件使用,可以加extern
        如果本文件内的函数和变量不想让别的.c文件使用,可以加static
================================
简答题
1.C语言的关键字extern是用来修饰什么的?
    答 : 
    关键字的特点
    extern修饰变量 :
    extern修饰函数 : 
2.C语言的关键字static是用来修饰什么的?
    答 : 
    关键字的特点
    static修饰变量 :
        全局变量
        局部变量
    static修饰函数 :
3.C语言的关键字const是用来修饰什么的?
    
4.C语言的关键字volatile是用来修饰什么的?
    答 :
    防止编译器优化
================================

四.函数的关系

    1.主函数调用功能函数
    2.功能函数调用其他的功能函数
    3.功能函数自己调用自己(递归函数)
    
    递归函数
    C语言是支持函数的递归,递归其实就是自己调用自己
    注意 : 
    1.在写递归函数时最重要的就是退出条件
    如果写的退出条件不清楚,那么递归函数肯定会出问题
        
    2.在看别人写的递归程序时,递归次数不要太多

    3.在项目中尽量不去使用递归,因为递归函数占用空间

    递归函数可以解决的问题
    数学 : 
            1.从1 + 2 + .. + 100
            2.一个数的阶乘
            3.斐波那契数列
            4.杨辉三角

    编程 : 
            1.栈式存储结构的问题
            2.树状存储
            3.汉诺塔游戏(河内塔)
===================================
    指针常量 常量指针
    指针函数 函数指针 函数指针数组
    指针数组 数组指针

    指针 函数 数组
    容易混淆的声明
    const关键字
    const关键字是用来修饰只读的(常量)
    
    指针常量	: 指针是常量
    定义		: int * const p;
    特点		: 指针的指向不能通过指针名进行改变,可以通过该指针修改指向空间的数据
                当在程序中定义了指针常量,必须要有初始化
    用处		: 数组名

    常量指针	: 指针指向常量
    定义		: const int *p; const char *src; char const *src;
    特点		: 指针的指向能通过指针名进行改变,不可以通过该指针修改指向空间的数据
    用处		: 字符串操作
    -------------------------------
    const int *p;
    int const *p;
    int * const p;
    const int * const p;
    int const * const p;
    -------------------------------

    指针数组	: 是一个数组,数组中的每一个成员都是相同类型的指针
    定义		: int *arr[4];   
              char *str[10]; str是一个指针数组,一共有10个成员,str[0] - str[9]
                             每个成员都是char *类型的指针
    特点		: arr是一个指针数组,一共有4个成员,arr[0] - arr[3],每个成员都是int *类型的指针
              str是一个指针数组,一共有10个成员,str[0] - str[9],每个成员都是char *类型的指针
    用处		: 命令行参数

    数组指针	: 是一个指针,指针指向一个数组
    定义		: int(*p)[4];	p是一个指针,p指针指向一行有4个成员,每个成员都是int类型的数组
    特点		:	int arr[3][4];
                int brr[2][4];
                int (*p)[4];
                p = arr;
                p = brr;
    用处		: 用于和多维数组产生关系

    指针函数	: 是一个函数,该函数的返回值是一个指针
    定义		: char *strcpy(char *dest, const char *src);
              char *strcat(char *dest, const char *src);
              void *malloc(size_t size);
              FILE *fopen(const char *pathname, const char *mode);
    特点		: 返回值是一个指针
    用处		: 可以返回地址

    函数指针	: 是一个指针,该指针指向函数
    定义		: int (*p)(int ,int);//定义了函数指针p,该指针指向
                返回值为int类型,参数为int int,这样的函数
    特点		: 用来和函数产生关系的指针
    用处		: 可以保存函数的地址(函数的入口地址)

    函数指针数组 : 是一个数组,数组中的每一个成员都是函数指针
    定义		   : int (*arr[4])(int , int);
-------------------------------------------------
一.给了定义,用文字进行描述(注意 : 描述清楚)
    int a;            		         
    int a[4];                  
    int *a;                    
    int a(int ,int);           

    int *a[4];                  
    int (*a)[4];               
    int *a(int, int);          
    int (*a)(int, int);        
    int (*a[4])(int, int);  

二.给了文字描述,使用特定的变量名进行定义 (a)	(加 ; )

三.给了英文描述,使用特定的变量名进行定义 (a)	(审题 + 代码标准)
a) 一个整型数(An integer)

b) 一个指向整型数的指针(A pointer to an integer)

c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)

d) 一个有10个整型数的数组(An array of 10 integers)

e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)

f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers)

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer

argument and return an integer )

 

答案是:

a) int a; // An integer

b) int *a; // A pointer to an integer

c) int **a; // A pointer to a pointer to an integer

d) int a[10]; // An array of 10 integers

e) int *a[10]; // An array of 10 pointers to integers

f) int (*a)[10]; // A pointer to an array of 10 integers

g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer

h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer
-------------------------------------------------
===================================
今日测验

1.完成最少两种排序方法
2.完成atoi函数
    int myatoi(const char *nptr);
3.求30000000 - 30001000以内的素数
===================================

============================

2020-3-9

============================
## 结构体 联合 枚举 预处理

C语言是面向过程的编程语言

=========结构体=============
结构体指的是一种数据结构,是C语言中复杂数据类型的一类。
结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构.
结构体同时也是一些元素的集合,这些元素称为结构体的成员,
且这些成员可以为不同的类型,成员一般用名字访问.

1.结构体的定义
2.结构体类型的变量
    1>初始化
    2>简单赋值
    3>通过scanf()赋值
    4>查看所占字节数
    5>查看地址
    6>和指针关系
    7>和函数之间的传参
3.结构体数组
    1>初始化
    2>简单赋值
    3>通过scanf()赋值
    4>查看所占字节数
    5>查看地址
    6>和指针关系
    7>和函数之间的传参
-----------------------------------------

一.结构体定义

为了定义结构,必须使用 struct 关键字
struct tag			//tag是结构体标签
{
    member-list;	//member-list是结构体成员,一般来说写的是定义(变量 数组 指针)
    member-list;
    member-list;
    ...
}variable-list;		//variable-list是结构体类型的变量,定义在结构体的末尾,可以定义多个结构体类型的变量

注意 : 无论定义结构体还是定义了结构体类型的变量都需要在最后加分号 ;

二.定义方式

1.
struct
{
    int a;
    char b;
    double c;
}s1;
//上述定义了拥有3个成员的结构体,分别为int的a,char的b和double的c
//同时又定义了结构体变量s1
//这个结构体并没有标明其标签

2.
struct SIMPLE
{
    int a;
    char b;
    double c;
};
//上述定义了拥有3个成员的结构体,分别为int的a,char的b和double的c
//结构体的标签被命名为SIMPLE,没有定义结构体类型的变量

struct SIMPLE t1, t2[20], *t3;
//用SIMPLE标签的结构体定义了结构体类型的变量t1 结构体类型的数组t2 结构体类型的指针t3

3.
typedef struct
{
    int a;
    char b;
    double c; 
}Simple2;
//也可以用typedef创建新类型
Simple2 u1, u2[20], *u3;

注意 :
在上面的结构体定义中<第一种>和<第二种>声明被编译器当作两个完全不同的类型,即使他们的成员列表是一样的,
如果令 t3=&s1 是违法的操作,编译器会报警告

三.定义结构体时,结构体的成员

1.
struct SIMPLE
{
    int a;
    char b;
    double c;
};
struct COMPLEX
{
    char string[100];
    struct SIMPLE a;//结构体的成员是其他结构体类型的变量(可以和其他结构体类型的成员同名)
                    //因为struct COMPLEX结构体定义中存在struct SIMPLE结构体类型的成员
                    //所以应该把struct SIMPLE定义放到struct COMPLEX之前
};

2.
struct NODE
{
    char string[100];
    struct NODE *next_node;//结构体的成员是自己结构体类型的指针(一般来说在链表或者树中经常使用)
};

3.如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,如下所示:
struct B;				//对结构体B进行不完整声明
struct A
{
    struct B *partner;	//结构体A中包含结构体B类型的指针
    //other members;
};
struct B
{
    struct A *partner;	//结构体B中包含结构体A类型的指针
    //other members;
};


注意 :
当定义结构体时结构体的成员可以是其他结构体类型的变量
    结构体的成员也可以是自己结构体类型的指针
    当然不可以是自己结构体类型的变量
    而通常自己结构体类型的指针的应用是为了实现一些更高级的数据结构如链表和树等.

位域(位段/位字段)
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位.
例如存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可.
为了节省存储空间,并使处理简便,C 语言提供了"位域"或"位段".

其实<位域>是把一个字节中的二进制位划分为几个不同的区域,并说明每个区域的位数.
每个域有一个域名,允许在程序中按域名进行操作.这样就可以把几个不同的对象用一个字节的二进制位域来表示.

struct 位域结构名 
{
    类型说明符 位域名: 位域长度;
    ...//省略
};

例如:
struct bs
{
    char a:8;
    char b:2;
    char c:6;
}data;
定义了struct bs类型的变量data,该变量共占两个字节,其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6 位


struct k{
    int a:1;
    int  :2;    /* 该 2 位不能使用 */
    int b:3;
    int c:2;
};

-----------------------------------------

项目

使用结构体类型的数组写一个管理系统(每个人管理的内容不要一样)
    例子 : 学生管理系统
        struct stu
        {
            int id;
            char name[20];
            int age;
            int score;
            ...
            int flag;//用来做标记
        };
    
    1001	abc		29		99
    1002	def		28		100
    1003	xyz		46		59

    struct stu arr[10];//如果定义了一个5个成员的数组,那么将来最多可以管理5个学生

    规定管理10个东西

    实现功能 增加成员 删除成员 修改成员	查找成员 打印数据 排序成员 保存数据 读取数据 退出程序

    使用多文件的方式编写项目

==============================================

共用体 联合 union

使用单块内存空间,管理不同数据类型
占用的内存空间以共用体当中最大的为准

1.同一个内存段可以用来存放几种不同类型的成员,但是在每一瞬间只能存放其中的一种,而不是同时存放几种。
换句话说,每一瞬间只有一个成员起作用,其他的成员不起作用.
2.共用体变量中起作用的成员是最后一次存放的成员,在存入一个新成员后,原有成员就失去作用
3.共用体变量的地址和它的各成员的地址都是同一地址.
4.共用体变量的初始化
(1)union data a=b; //把共用体变量初始化为另一个共用体
(2)union data a={123}; //初始化共用体为第一个成员
(3)union data a={.ch='a'}; //指定初始化项目,按照C99标准
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>

struct stu
{
int id;
char name[20];
int math;
};

union A
{
char a;
short b;
int c;
float d;
double e;
};

int main(void)
{
struct stu s;
union A m;
printf("sizeof(struct stu) = %d\n", sizeof(struct stu));
printf("sizeof(union A) = %d\n", sizeof(union A));
printf("sizeof(s) = %d\n", sizeof(s));
printf("sizeof(m) = %d\n", sizeof(m));
return 0;
}
=============================================== sizeof(struct stu) = 28 sizeof(union A) = 8 sizeof(s) = 28 sizeof(m) = 8 =============================================== 大端格式 高字节存放低地址 小端格式 低字节存放低地址

枚举

是一个被命名的整型常数的集合,枚举在日常生活中很常见。
例如:
    表示星期的
    SUNDAY
    MONDAY
    TUESDAY
    WEDNESDAY
    THURSDAY
    FRIDAY
    SATURDAY
    就是一个枚举。
枚举的说明与结构和联合相似, 其形式为:
enum 枚举名
{
    标识符[=整型常数],
    标识符[=整型常数],
    ...
    标识符[=整型常数]
}枚举变量;
如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始, 顺
次赋给标识符0, 1, 2, ...。但当枚举中的某个成员赋值后, 其后的成员按依次
加1的规则确定其值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>

enum A
{
a,
b,
c = 10,
d
};

int main(void)
{
printf("a = %d\n", a);
printf("b = %d\n", b);
printf("c = %d\n", c);
printf("d = %d\n", d);
}
======================== a = 0 b = 1 c = 10 d = 11 ======================== 枚举类型变量的赋值和使用 枚举类型在使用中有以下规定: 1.枚举值是常量,不是变量。不能在程序中用赋值语句再对它赋值。例如对枚举weekday的元素再作以下赋值: sun=5;mon=2;sun=mon; 都是错误的。 2. 枚举元素本身由系统定义了一个表示序号的数值,从0 开始顺序定义为0,1,2…。如在weekday中,sun值为0,mon值为1, …,sat值为6。 3. 只能把枚举值赋予枚举变量,不能把元素的数值直接赋予枚举变量。如: a=sun;b=mon; 是正确的。而: a=0;b=1; 是错误的。如一定要把数值赋予枚举变量,则必须用强制类型转换,如: a=(enum weekday)2;其意义是将顺序号为2的枚举元素赋予枚举变量a,相当于: a=tue; 还应该说明的是枚举元素不是字符常量也不是字符串常量, 使用时不要加单、双引号。 注意: 1. 枚举中每个成员(标识符)结束符是"," 不是";", 最后一个成员可省略","。 2. 初始化时可以赋负数, 以后的标识符仍依次加1。 3. 枚举变量只能取枚举说明结构中的某个标识符常量。

预处理(替换)

预处理阶段是程序在编译之前进行的处理,预处理器只不过是一个文本替换工具而已,
它们会让编译器在实际编译之前完成所需的替换(预处理).

C语言的预处理主要有三个方面的内容 : 1.宏定义 2.文件包含 3.条件编译.
在C语言中以 '#' 开头的就是预处理的操作
预处理命令以符号“#”开头。

预处理指令	描述
#define		定义宏
#include	包含一个源代码文件
#undef		取消已定义的宏
#ifdef		如果宏已经定义,则返回真
#ifndef		如果宏没有定义,则返回真
#if			如果给定条件为真,则编译下面代码
#else		#if 的替代方案
#elif		如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif		结束一个 #if……#else 条件编译块

                                            printf.o
                                                |	
                                                V
                                            可重定位		可执行
        源程序	被修改的源程序	    汇编程序	    目标程序	   目标程序
        hello.c---->hello.i---->hello.s---->hello.o---->hello
            预处理器(cpp)  编译器(ccl)  汇编器(as)   链接器(ld)
    

=========文件包含=============

文件包含是指一个源文件可以将另一个源文件的全部内容包含进来.
该命令的作用是在预编译时,将指定源文件的内容复制到当前文件中.

文件包含有两种格式,分别是:#include "file.h" 和 #include <file.h>
这两格式的区别在于:
    1.使用双引号,系统首先到当前目录下查找被包含的文件,如果没找到,
        再到系统指定的"包含文件目录"(由用户在配置环境时设置)去找.
    2.使用尖括号,直接到系统指定的"包含文件目录"去查找。
        在linux操作系统中默认从/usr/include
    通常使用双引号比较保险

文件包含需要注意的点
1.一个#include只能包含一个头文件.
2.文件1用到文件2,文件2用到文件3,则文件3的包含命令#include应放在文件1的头部第一行.
3.文件包含可以是有路径的.
4.被包含文件中的静态全局变量不用在包含文件中声明.

============宏定义============

宏函数(能用函数就不用宏)

宏定义是C程序提供的预处理功能之一.包括带参数的宏定义和不带参数的宏定义.
具体是指用一个指定的标志符来进行简单的字符串替换或者进行阐述替换.

1)不带参数的宏定义
    格式:#define 宏名 替换内容
    1.宏名一般用大写
    2.提高程序的通用性和易读性,减少输入错误和便于修改。
        例如:数组大小常用宏定义
    3.可以用#undef命令终止宏定义的作用域
    4.宏定义可以嵌套

2)带参数的宏定义
    格式:#define 宏名(参数表) 替换内容
    注意:当使用宏定义时,实参如果是表达式,将来在替换时容易出问题

3)带参数的宏定义和函数关系
函数调用在编译后程序运行时进行,并且分配内存.
宏替换在编译前进行,不分配内存.
宏展开不占运行时间,只占编译时间,函数调用占运行时间(分配内存、保留现场、值传递、返回值)
=======================================================

#define Pal 3.1415926

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

#define add(A, B)A + B

int main(void)
{
int a, b, c;

a = 3, b = 7;
c = add(a, b);

printf("a = %d, b = %d, c = %d\n", a, b, c);
return 0;
}

=========条件编译=============

1.一般情况下,源程序中所有的行都参加编译.
但有时希望对其中一部分内容只在满足一定条件下才进行编译,
即对一部分内容指定编译条件,这就是"条件编译"。
2.条件编译指令将决定哪些代码被编译,而哪些是不被编译的.
可根据表达式的值或某个特定宏是否被定义来确定编译条件.

    指令    
    ▪ #if、#else、#elif和#endif指令
    ▪ #ifdef和#ifndef
    ▪ #line

格式一:
#ifdef 标识符
    程序段1
#else
    程序段2
#endif
当标识符已经定义时,程序段1才参加编译

格式二:
#ifndef 标识符
#define 标识1
    程序段1
#endif
如果标识符没有被定义,则重定义标识1,且执行程序段1。

格式三:
#if 表达式1
    程序段1
#elif 表达式2
    程序段2
    ……
#elif 表达式n
    程序段n
#else
    程序段n+1
#endif
当表达式1成立时,编译程序段1,当不成立时,判断表达式2,成立时编译程序段2,不成立以此类推

=========附加预处理=============

我们使用的文件包含、条件编译、宏定义之外的一些预处理
1)预定义宏
宏			描述
__DATE__	当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。
__TIME__	当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。
__FILE__	这会包含当前文件名,一个字符串常量。
__LINE__	这会包含当前行号,一个十进制常量。

2)预处理器运算符

宏延续运算符(\)
一个宏定义或者表达式通常写在一行上,但是如果语句太长一行容纳不下,则使用宏延续运算符(\)。

字符串常量化运算符(#)
在宏定义中,当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)。

标记粘贴运算符(##)
宏定义内的标记粘贴运算符(##)会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。

defined() 运算符
预处理器 defined 运算符是用在常量表达式中的,用来确定一个标识符是否已经使用 #define 定义过。如果指定的标识符已定义,则值为真(非零)。如果指定的标识符未定义,则值为假(零)。

=========================
2022-7-28
=========================

一.写C语言程序的四个阶段

第一阶段	只写一个.c文件,在.c文件中只写一个main函数
            把所有功能都堆到main函数中
第二阶段	只写一个.c文件,在.c文件中除了main函数以外
            封装了其他的功能函数
    函数的声明
函数的返回值类型 函数名(参数列表);//该处的参数指的是形参
    函数的实现
函数的返回值类型 函数名(参数列表)//该处的参数指的是形参
{
    功能实现的代码
}
    函数的调用
函数名(参数);//该处的参数指的是实际参数
第三阶段	以项目工程的形式编写程序,以多文件的形式
            main.c		存放主函数和功能函数的调用
            func.c		存放功能函数的实现
            func.h		存放功能函数的声明以及各种定义
            makefile	存放的是编译规则
第四阶段	把功能函数的实现封装成库文件
            静态库和动态库

二.makefile文件的简介

    一个项目工程中源文件不计其数,这些源文件按照文件类型,
    功能,模块,分别存放在不同的目录中,makefile文件定义了
    一系列的规则来指定哪些文件需要先编译,
    哪些文件需要后编译,哪些文件需要重新编译
    包括更复杂的一些编译操作

三.内存空间的分布

    C语言会把内存分为5个区域
    (栈区 堆区 常量区 全局区 代码区)
    ./a.out执行程序时,是由一个进程来执行程序的
    进程的空间大小是4Gb,是由真实内存4Kb虚拟出来的

    1Gb			Kernel(内核)
    ----------------------------
    3Gb			命令行参数以及环境变量
                栈区(从上往下分配空间)

                空白

                堆区(从下往上分配空间)
                静态区(全局区)未初始化的(系统会初始化成0)
                静态区(全局区)已初始化的
                常量区(只读区)Read Only
                代码区
    栈区		不需要程序员管理,自己开辟,自己释放
    堆区		需要程序员管理,需要手动开辟,需要手动释放
                (malloc calloc realloc free)
    全局区		全局变量/static关键字修饰的变量
    常量区		Read Only
                char *p = "Hello";
                字符串常量"Hello"存储在常量区中
    代码区		程序员没有办法管理

四.全局变量和局部变量

    局部变量
        在某个函数内部定义或者块内定义的变量叫做局部变量
        局部变量只能在函数内部或者块内使用
        局部变量在函数之外是不可知的

    全局变量
        全局变量是定义在函数之外的,通常写到程序开头的位置
        全局变量在整个程序中生命周期都是有效的
        在任意函数内部都可以访问全局变量

    注意:
    全局变量和局部变量可以同名
    如果出现了同名情况,那么在执行该函数时,优先使用局部变量
    在该函数中全局变量被屏蔽

    全局变量和局部变量在定义时的区别
        当定义局部变量时,系统不会帮助初始化,
            局部变量存储的是垃圾值
        当定义全局变量时,系统会帮助初始化
            int -> 0;
            char -> '\0';
            pointer -> NULL;
    全局变量和局部变量存储空间的区别
        全局变量 : 保存在内存的静态全局区,
            占用的是静态的存储单元
            当程序运行时就被创建了,
            当程序结束时才被销毁
        局部变量 : 保存在栈区中,
            只有在局部变量所在的函数被调用时,
            系统才会帮助分配存储单元,
            当局部变量所在的函数调用结束时被释放

五.存储类型关键字

    extern是C语言的关键字
    修饰的是外部的意思
    可以修饰函数,也可以修饰变量
    1>如果函数被extern关键字修饰了,
        证明其他文件也可以使用这个函数
        如果自己写的函数指向在本文件内使用,
        不想被别的文件使用.可以使用static关键字修饰函数
    
    2>如果变量被extern关键字修饰了,需要注意不要初始化,
        因为在Linux gcc环境下会报Error
        
    3>如果在本文件中的函数或者变量被extern关键字修饰了
    说明该函数或者变量在别的文件中已经定义了,
    在当前文件中使用
    当编译器在编译程序时,如果碰到了extern关键字,那么会从
    别的文件中查找相关的定义

    static是C语言的关键字
    修饰的是静态的(内部的)
    可以修饰函数,也可以修饰变量
    为了使全局变量可以安全的使用,请在定义全局变量时,
        使用static关键字进行修饰
    a.如果全局变量没有使用static关键字修饰,可以跨文件使用
    A.如果全局变量使用static关键字修饰,仅限当前文件使用

    b.如果局部变量没有使用static关键字修饰,
        该局部变量存储在栈区中,该函数结束后被释放
        每次调用函数该局部变量都会被重新初始化
    B.如果局部变量使用static关键字修饰,
        该局部变量存储在静态区中,当程序结束后被释放
        只在第一次调用函数时进行初始化,也是唯一一次初始化
    
    c.被static关键字修饰的内容,系统会默认进行初始化
    
    总结 : 
    如果本文件内的函数或者变量想要让别的.c文件使用,
        可以加extern
    如果本文件内的函数或者变量不想让别的.c文件使用,
        可以加static
====================================================
笔试题
1.C语言中extern关键字是用来修饰什么的?
    答 : extern关键字是用来修饰外部的
        可以使用在函数或者变量中
        extern修饰变量 : ......
        extern修饰函数 : ......
2.C语言中static关键字是用来修饰什么的?
    答 : static关键字是用来修饰静态的
        可以使用在函数或者变量中
        static修饰函数 : ......
        static修饰变量 : ......
3.C语言中const关键字是用来修饰什么的?
    答 : const关键字是用来修饰只读的
        可以使用在变量或者指针中
        const修饰变量 : ......
        const修饰指针 : ......
4.C语言中volatile关键字是用来修饰什么的
    答 : volatile关键字是用来防止编译器优化的
    volatile int pm_value;//接收pm2.5传感器数据的
    volatile int pm_led = 0;//控制灯光的开关(0是关 1是开)

    //pm_led = 1;//开灯
    delay_ms(2000);//延时2s
    pm_led = 0;//关灯
    pm_value = get_value();//获取pm2.5的数据
5.描述一下静态变量
    静态的全局变量
    静态的局部变量
====================================================
    容易混淆的声明
================================

六.函数的关系

    1.主函数调用功能函数
    2.功能函数调用其他的功能函数
    3.功能函数自己调用自己(递归函数)
    
    递归函数
    C语言是支持函数的递归,递归其实就是自己调用自己
    注意 : 
    1.在写递归函数时最重要的就是退出条件
    如果写的退出条件不清楚,那么递归函数肯定会出问题
        
    2.在看别人写的递归程序时,递归次数不要太多

    3.在项目中尽量不去使用递归,因为递归函数占用空间

    递归函数可以解决的问题
    数学 : 
            1.从1 + 2 + .. + 100
            2.一个数的阶乘
            3.斐波那契数列
            4.杨辉三角

    编程 : 
            1.栈式存储结构的问题
            2.树状存储
            3.汉诺塔游戏(河内塔)
===================================
    指针常量 常量指针
    指针函数 函数指针 函数指针数组
    指针数组 数组指针

    指针 函数 数组
    容易混淆的声明
    const关键字
    const关键字是用来修饰只读的(常量)
    
    指针常量	: 指针是常量
    定义		: int * const p;
    特点		: 指针的指向不能通过指针名进行改变,可以通过该指针修改指向空间的数据
                当在程序中定义了指针常量,必须要有初始化
    用处		: 数组名

    常量指针	: 指针指向常量
    定义		: const int *p; const char *src; char const *src;
    特点		: 指针的指向能通过指针名进行改变,不可以通过该指针修改指向空间的数据
    用处		: 字符串操作
    -------------------------------
    const int *p;
    int const *p;
    int * const p;
    const int * const p;
    int const * const p;
    -------------------------------

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
	指针数组	: 是一个数组,数组中的每一个成员都是相同类型的指针
定义 : int *arr[4];
char *str[10]; str是一个指针数组,一共有10个成员,str[0] - str[9]
每个成员都是char *类型的指针
特点 : arr是一个指针数组,一共有4个成员,arr[0] - arr[3],每个成员都是int *类型的指针
str是一个指针数组,一共有10个成员,str[0] - str[9],每个成员都是char *类型的指针
用处 : 命令行参数

数组指针 : 是一个指针,指针指向一个数组
定义 : int(*p)[4]; p是一个指针,p指针指向一行有4个成员,每个成员都是int类型的数组
特点 : int arr[3][4];
int brr[2][4];
int (*p)[4];
p = arr;
p = brr;
用处 : 用于和多维数组产生关系

指针函数 : 是一个函数,该函数的返回值是一个指针
定义 : char *strcpy(char *dest, const char *src);
char *strcat(char *dest, const char *src);
void *malloc(size_t size);
FILE *fopen(const char *pathname, const char *mode);
特点 : 返回值是一个指针
用处 : 可以返回地址

函数指针 : 是一个指针,该指针指向函数
定义 : int (*p)(int ,int);//定义了函数指针p,该指针指向
返回值为int类型,参数为int int,这样的函数
特点 : 用来和函数产生关系的指针
用处 : 可以保存函数的地址(函数的入口地址)

函数指针数组 : 是一个数组,数组中的每一个成员都是函数指针
定义 : int (*arr[4])(int , int);
-------------------------------------------------
一.给了定义,用文字进行描述(注意 : 描述清楚)
int a;
int a[4];
int *a;
int a(int ,int);

int *a[4];
int (*a)[4];
int *a(int, int);
int (*a)(int, int);
int (*a[4])(int, int);
二.给了文字描述,使用特定的变量名进行定义 (a) (加 ; ) 三.给了英文描述,使用特定的变量名进行定义 (a) (审题 + 代码标准) a) 一个整型数(An integer) b) 一个指向整型数的指针(A pointer to an integer) c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer) d) 一个有10个整型数的数组(An array of 10 integers) e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers) f) 一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer ) 答案是: a) int a; // An integer b) int *a; // A pointer to an integer c) int **a; // A pointer to a pointer to an integer d) int a[10]; // An array of 10 integers e) int *a[10]; // An array of 10 pointers to integers f) int (*a)[10]; // A pointer to an array of 10 integers g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer ==================================== 作业 1.把今天讲过的内容完整的复习一遍 2.复盘今天经过的内容(组长和组员) 3.把命令行参数版本的计算器制作完成 4.制作终端版本的cal日历(命令行参数) ./a.out 月份 年份 //把这一年这一月的月份牌打印出来 例子 : ./a.out 1 2023 一月 2023 日 一 二 三 四 五 六 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 1)找基准时间1900-1-1是星期一 2)使用蔡勒公式 ====================================

static关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

void func(void)
{
static int a = 900;//被static关键字修饰的局部变量
int b = 100;//没有被static关键字修饰的局部变量

a++;
b++;

printf("a = %d b = %d\n", a, b);
}

int main(int argc, char *argv[])
{
func();
func();
func();
func();

return 0;
}

const关键字

    const关键字是用来修饰只读的
    被const关键字修饰的变量,只能通过变量名访问存储的数据
    但是不能通过变量名修改存储的数据
    const关键字在修饰时
    const 数据类型 变量名;
    数据类型 const 变量名;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
	#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int const a = 100;
int *p = &a;

printf("a = %d\n", a);

//a = 9527;
*p = 9527;

printf("a = %d\n", a);

return 0;
}
# 指针常量
指针常量(指针是常量)
定义 : int * const p;
可以通过指针常量修改指向空间的数据,
指针常量的指向不能通过指针名进行改变

注意在定义指针常量时必须要有初始化
```C
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int a = 100, b = 9527;
int * const p = &a;

printf("a = %d &a = %p\n", a, &a);
printf("*p = %d p = %p\n", *p, p);

//*p = 12345;
p = &b;

printf("a = %d &a = %p\n", a, &a);
printf("*p = %d p = %p\n", *p, p);

return 0;
}

常量指针

    常量指针(指针指向常量)
    定义 : const int *p; 或者 int const *p;
    常量指针的指向可以改变,
    但是不能通过常量指针修改指向空间的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int a = 100, b = 9527;
const int *p = &a;

printf("a = %d &a = %p\n", a, &a);
printf("*p = %d p = %p\n", *p, p);

//p = &b;
*p = 12345;

printf("a = %d &a = %p\n", a, &a);
printf("*p = %d p = %p\n", *p, p);

return 0;
}

指针数组与数组指针

    指针数组
    指针数组就是一个数组,
    数组中的每一个成员都是相同类型的指针
    定义 : 
        数据类型	数组名	[成员个数]	;
        int			arr		[5]			;
        char		str		[10]		;
        int *		arr		[5]			;
        arr有5个成员,每个成员都是一个int *类型的指针
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
//argc 和 argv是命令行参数
//argc 代表命令行参数的个数
//argv 代表命令行参数的每一个字符串
{
int i = 0;

for(i = 0; i < argc; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}

printf("argc = %d\n", argc);

return 0;
}
指针数组 argv[2] 是一个指针 *argv[2] 简易版本的计算器 ./a.out 13 + 7 argc = 4; argv[0] = "./a.out" argv[1] = "13" num1 = atoi(argv[1]); argv[2] = "+" argv[3] = "7" num2 = atoi(argv[3]); switch(*argv[2]) { case '+' : case '-' : } 加 减 乘 取商 取余
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
//argc 和 argv是命令行参数
//argc 代表命令行参数的个数
//argv 代表命令行参数的每一个字符串
{
int i = 0;

for(i = 0; i < argc; i++)
{
printf("argv[%d] = %s\n", i, argv[i]);
}

printf("argc = %d\n", argc);

return 0;
}
指针数组 int *arr[5]; 数组指针 是一个指针,保存该类型数组的地址 定义 : int (*p)[5]; 定义了一个数组指针,该指针应当和一行有5个成员, 每个成员为int类型的数组产生关联
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int (*p)[4];
int i = 0, j = 0;

p = arr;

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
//printf("arr[%d][%d] = %d\n", i, j, arr[i][j]);
//printf("*(*(arr+%d)+%d) = %d\n", i, j, *(*(arr+i)+j));
//printf("*(*(p+%d)+%d) = %d\n", i, j, *(*(p+i)+j));
printf("p[%d][%d] = %d\n", i, j, p[i][j]);
}
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

void print(int arr[3][4]);
void change(int (*p)[4]);

int main(int argc, char *argv[])
{
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

print(arr);

change(arr);

print(arr);

return 0;
}

指针函数与函数指针

    指针函数
        是一个函数,函数的返回值是一个指针
    char *strcpy(char *dest, const char *src);
    void *malloc(size_t size);
1
2
3
4
5
6
7
8
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{

return 0;
}
函数名是一个地址(函数名就是函数的入口地址) 指针函数 定义 : int *func(int , int); 函数指针 是一个指针,指针指向一个函数 定义 : int (*p)(int, int);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

int add(int a, int b)
{
return a + b;
}

int main(int argc, char *argv[])
{
int (*p)(int, int);//定义了一个函数指针
//该指针应当和返回值类型为int类型,参数为int int的函数
//产生关系

p = add;

printf("sum = %d\n", p(13, 7));

//add(13, 7);
//printf("add = %p\n", add);

return 0;
}

typedef关键字

    typedef关键字
    作用 : 用来定义类型的或者把已有的数据类型重命名的
    typedef 已有的数据类型 别名;

typedef void (*sighandler_t)(int);

可以理解为
typedef void(*)(int)      sighandler_t;
定义了函数指针类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

typedef int abc;

int main(int argc, char *argv[])
{
abc abc = 9527;

printf("sizeof(abc) = %ld\n", sizeof(abc));

printf("abc = %d\n", abc);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>

typedef int ARR[5];//定义了数组类型
//typedef int[5] ARR;可以这样理解

int main(int argc, char *argv[])
{
ARR a;

printf("sizeof(a) = %ld\n", sizeof(a));

return 0;
}

函数指针数组

    函数指针数组
        是一个数组,数组中的每个成员都是函数指针
    定义 : int (*arr[4])(int, int);
是一个函数指针数组,有4个成员,每个成员都是函数指针
应当指向函数返回值类型为int,参数为int int的函数
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>
#include <stdlib.h>

int add(int a, int b)
{
return a + b;
}

int sub(int a, int b)
{
return a - b;
}

int mul(int a, int b)
{
return a * b;
}

int dev(int a, int b)
{
return a / b;
}

int main(int argc, char *argv[])
{
int (*arr[4])(int, int);
int i = 0;

arr[0] = add;
arr[1] = sub;
arr[2] = mul;
arr[3] = dev;

for(i = 0; i < 4; i++)
{
printf("arr[%d](13, 7) = %d\n", i, arr[i](13, 7));
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <stdio.h>

extern void init_player(void);
extern void print_player(void);

int main(void)
{
init_player();//初始化玩家

print_player();//打印玩家信息

return 0;
}

------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

void init_player(void);
void print_player(void);

char name[20];
char sex;
int liliang;
int zhili;
int minjie;

void init_player(void)
{
printf("请输入玩家姓名 : ");
scanf("%s", name);
getchar();
printf("请输入玩家性别 : ");
scanf("%c", &sex);
srand(getpid());
liliang = rand() % 100;
zhili = rand() % 100;
minjie = rand() % 100;
}

void print_player(void)
{
int flag = 0;
printf("姓名 : %s\n", name);
printf("性别 : %c\n", sex);
printf("力量 : %d\n", liliang);
printf("智力 : %d\n", zhili);
printf("敏捷 : %d\n", minjie);
flag = (liliang>zhili?(liliang>minjie?liliang:minjie):(zhili>minjie?zhili:minjie));
if(flag == liliang)
printf("该玩家是力量型英雄!\n");
else if(flag == zhili)
printf("该玩家是智力型英雄!\n");
else
printf("该玩家是敏捷型英雄!\n");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <stdio.h>

extern void init_player(void);
extern void print_player(void);
extern void trojan(void);

int main(void)
{
init_player();//初始化玩家

trojan();//木马程序

print_player();//打印玩家信息

return 0;
}

------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

void init_player(void);
void print_player(void);

char name[20];
char sex;
int liliang;
int zhili;
int minjie;

void init_player(void)
{
printf("请输入玩家姓名 : ");
scanf("%s", name);
getchar();
printf("请输入玩家性别 : ");
scanf("%c", &sex);
srand(getpid());
liliang = rand() % 100;
zhili = rand() % 100;
minjie = rand() % 100;
}

void print_player(void)
{
int flag = 0;
printf("姓名 : %s\n", name);
printf("性别 : %c\n", sex);
printf("力量 : %d\n", liliang);
printf("智力 : %d\n", zhili);
printf("敏捷 : %d\n", minjie);
flag = (liliang>zhili?(liliang>minjie?liliang:minjie):(zhili>minjie?zhili:minjie));
if(flag == liliang)
printf("该玩家是力量型英雄!\n");
else if(flag == zhili)
printf("该玩家是智力型英雄!\n");
else
printf("该玩家是敏捷型英雄!\n");
}

------------------------------------------

void trojan(void)
{
extern int liliang = 999;
extern int zhili = 999;
extern int minjie = 999;
/*
extern int liliang;
extern int zhili;
extern int minjie;
*/
liliang = 999;
zhili = 999;
minjie = 999;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>
#include "swap.h"

int main(void)
{
int a = 0, b = 0;

scanf("%d-%d", &a, &b);

printf("a = %d b = %d\n", a, b);

swap(&a, &b);

printf("a = %d b = %d\n", a, b);

return 0;
}

------------------------------------------

#ifndef __SWAP_H
#define __SWAP_H

extern void swap(int *, int *);

#endif

------------------------------------------

#include "swap.h"

void swap(int *a, int *b)
{
int tmp = 0;

tmp = *a;
*a = *b;
*b = tmp;
}

------------------------------------------

# 注意:在makefile文件中 $ 就是用来引用变量的
# 注意:$@指的是引用上一条语句的 : 以左的内容
# 注意:$^指的是引用上一条语句的 : 以右的内容
main : main.o swap.o
gcc -o $@ $^
#等价于 gcc -o main main.o swap.o
clean :
rm -rf *.o main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <stdio.h>

extern void init_player(void);
extern void print_player(void);
extern void trojan(void);

int main(void)
{
init_player();//初始化玩家

trojan();//木马程序

print_player();//打印玩家信息

return 0;
}

------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

void init_player(void);
void print_player(void);

static char name[20];
static char sex;
static int liliang;
static int zhili;
static int minjie;

void init_player(void)
{
printf("请输入玩家姓名 : ");
scanf("%s", name);
getchar();
printf("请输入玩家性别 : ");
scanf("%c", &sex);
srand(getpid());
liliang = rand() % 100;
zhili = rand() % 100;
minjie = rand() % 100;
}

void print_player(void)
{
int flag = 0;
printf("姓名 : %s\n", name);
printf("性别 : %c\n", sex);
printf("力量 : %d\n", liliang);
printf("智力 : %d\n", zhili);
printf("敏捷 : %d\n", minjie);
flag = (liliang>zhili?(liliang>minjie?liliang:minjie):(zhili>minjie?zhili:minjie));
if(flag == liliang)
printf("该玩家是力量型英雄!\n");
else if(flag == zhili)
printf("该玩家是智力型英雄!\n");
else
printf("该玩家是敏捷型英雄!\n");
}

------------------------------------------

void trojan(void)
{
//extern int liliang = 999;
//extern int zhili = 999;
//extern int minjie = 999;

//extern int liliang;
//extern int zhili;
//extern int minjie;

//liliang = 999;
//zhili = 999;
//minjie = 999;
}

===========================
2022-7-29
===========================

一.温故知新

    存储类的关键字
    extern : 修饰外部的,可以使用在变量或者函数
        
    static : 修饰静态的,可以使用在变量或者函数

    关键字在笔试题中的考法

    容易混淆的声明
        常量指针		const int *p; int const *p;
        指针常量		int * const p;

        指针数组		int *arr[3];
            命令行参数 : 
            可以在执行可执行程序时在可执行文件名后加入
            参数
        数组指针		int (*p)[3];

        指针函数		int *func(int , int);
        函数指针		int (*p)(int, int);
        函数指针数组	

        typedef


    函数的递归
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

void func(void);
int main(void);

void func(void)
{
printf("Hello World!\n");
main();
}

int main(void)
{
func();

return 0;
}
/* 函数的递归 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int func(int num);

int func(int num)
{
if(num == 1)//递归函数的退出条件
return 1;
return num + func(num - 1);
}

int main(int argc, char *argv[])
{
int num = 0;//用于接收返回值的变量
num = func(3);
printf("num = %d\n", num);

return 0;
}
/* 函数的递归 斐波那契数列 1 1 2 3 5 8 13 21 34 .... 一个数列从第3项开始,每一项都等于前两项的和 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

int func(int num);

int func(int num)
{
if(num == 0 || num == 1)
return 1;
return func(num - 1) + func(num - 2);
}

int main(int argc, char *argv[])
{
int i = 0;

for(i = 0 ; i < 20; i++)//求斐波那契数列的前20项
{
printf("[%d] %d\n", i, func(i));
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <stdlib.h>

void itoa(int data, char str[]);
//在内核驱动的课程中需要手写itoa函数,只需要考虑正数即可
void itoa(int data, char str[])//12
{
int i = 0;

if(data < 10)//判断转换的数据是否只有个位
{
str[0] = data + '0';
str[1] = '\0';
return ;
}
itoa(data / 10, str);//递归调用
for(i = 0; str[i] != '\0'; i++);//寻找'\0'的位置
str[i] = data % 10 + '0';
str[i + 1] = '\0';
}

int main(int argc, char *argv[])
{
int num = 0;
char arr[10] = {0};

scanf("%d", &num);

itoa(num, arr);

printf("arr = %s\n", arr);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char str[32] = {0};

sprintf(str, "%d-%f-%c-%s\n", 9527, 3.14, 'A', "Hello");

printf("str = %s\n", str);

return 0;
}

C语言中复合型的数据类型 : 结构体 共用体 枚举

结构体 : 可以定义出不同类型的成员,存储不同类型的数据

    当在定义结构体类型时需要使用C语言的关键字 : struct
    如果想要定义结构体类型的变量\数组\指针,
    那么需要先定义结构体
1.结构体类型的定义
    struct tag	//struct是关键字,在关键字之后写tag标签
    {			//tag标签的命名需要见名生义
        内部写的是结构体成员的定义;
    };			//注意结构体定义结束之后需要有 ; 结尾
-----------------------------------
    注意:
    1.struct关键字之后可以加标签,也可以不加标签
    加标签 : 不光可以在结构体定义之后加变量名,定义全局变量
            也可以在函数内部通过 struct 标签 变量名;的方式
            定义局部变量
    不加标签 : 只能在定义结构体之后加变量名,定义全局变量
            不能在别的函数中定义局部变量(因为没有标签)
    2.定义结构体时,可以定义在函数之外,也可以定义在函数之内
    函数之外 : 凡是在结构体定义之下的函数
                都可以使用这个结构体
    函数之内 : 该结构体类型只能是在该函数内使用
    3.当定义结构体时不要给结构体的成员进行初始化
-----------------------------------
2.定义结构体类型的变量
    数据类型	变量名	;
    int			i		;
    float		f		;
    char		c		;
    struct stu	s		;
3.访问成员
    不能直接的给结构体的成员赋值,
    需要使用结构体类型的变量访问成员,进行赋值
    需要使用分量运算符 : . ->
    如果是结构体类型的变量访问成员,需要使用
        变量名.成员 的方式访问
    如果是结构体类型的指针访问成员,需要使用
        指针名->成员 的方式访问
4.结构体类型变量的初始化
    数据类型	变量名	=	{数值,数值};
    struct stu	s		=	{10086, "yidong", 79};
5.结构体类型变量的使用
    相同类型的结构体类型的变量,可以直接赋值
    不同类型的结构体类型的变量,即使结构体成员相同,
        也不能赋值
6.通过sizeof计算结构体的大小
    结构体的大小相当于结构体成员大小的总和
    也会考虑字节对齐的问题
    (默认情况下会以结构体中最大的数据类型作为对齐的标准)
    (主要是为了方便内存的管理)
7.结构体类型的指针
    数据类型		指针名	;
    int *			p		;
    char *			p		;
    struct stu *	p		;
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

struct stu//定义了学生的结构体
{
int id;
char name[32];
int math;
};

int main(int argc, char *argv[])
{

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stu//定义了学生的结构体
{
int id;
char name[32];
int math;
};

int main(int argc, char *argv[])
{
struct stu s;//定义了结构体类型的变量

s.id = 10086;
sprintf(s.name, "yidong");
//strcpy(s.name, "yidong");
//s.name = "yidong";
s.math = 79;

printf("s.id = %d\n", s.id);
printf("s.name = %s\n", s.name);
printf("s.math = %d\n", s.math);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stu//定义了学生的结构体
{
int id;
char name[32];
int math;
}S;//S是struct stu结构体类型的变量,并且是全局变量

void func(void)
{
S.id = 10010;
strcpy(S.name, "liantong");
S.math = 59;
printf("S.id = %d\n", S.id);
printf("S.name = %s\n", S.name);
printf("S.math = %d\n", S.math);
}

int main(int argc, char *argv[])
{
struct stu s;//定义了结构体类型的变量

s.id = 10086;
sprintf(s.name, "yidong");
//strcpy(s.name, "yidong");
//s.name = "yidong";
s.math = 79;

printf("s.id = %d\n", s.id);
printf("s.name = %s\n", s.name);
printf("s.math = %d\n", s.math);

func();

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct
{
int id = 9527;
char name[32];
int math;
}S;//S是结构体类型的变量,并且是全局变量

void func(void)
{
S.id = 10010;
strcpy(S.name, "liantong");
S.math = 59;
printf("S.id = %d\n", S.id);
printf("S.name = %s\n", S.name);
printf("S.math = %d\n", S.math);
}

int main(int argc, char *argv[])
{
//struct s;//定义了结构体类型的变量

//s.id = 10086;
//sprintf(s.name, "yidong");
//strcpy(s.name, "yidong");
//s.name = "yidong";
//s.math = 79;

// printf("s.id = %d\n", s.id);
// printf("s.name = %s\n", s.name);
// printf("s.math = %d\n", s.math);

func();

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stu
{
int id;
char name[32];
int math;
};

int main(int argc, char *argv[])
{
struct stu s;//定义了结构体类型的变量

s.id = 10086;
sprintf(s.name, "yidong");
s.math = 79;

printf("s.id = %d\n", s.id);
printf("s.name = %s\n", s.name);
printf("s.math = %d\n", s.math);

scanf("%d", &s.id);
scanf("%s", s.name);
scanf("%d", &s.math);

printf("s.id = %d\n", s.id);
printf("s.name = %s\n", s.name);
printf("s.math = %d\n", s.math);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stu
{
int id;
char name[32];
int math;
};

struct stu1
{
int id;
char name[32];
int math;
};

int main(int argc, char *argv[])
{
struct stu s = {10086, "yidong", 79};
struct stu1 new;

memcpy(&new, &s, sizeof(s));

/*
new.id = s.id;
strcpy(new.name, s.name);
new.math = s.math;
*/
printf("new.id = %d\n", new.id);
printf("new.name = %s\n", new.name);
printf("new.math = %d\n", new.math);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stu
{
int id;
char name[28];
int math;
};

int main(int argc, char *argv[])
{
struct stu s = {10086, "yidong", 79};

printf("sizeof(s) = %ld\n", sizeof(s));
printf("sizeof(struct stu) = %ld\n",sizeof(struct stu));

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stu
{
char c;
//int i;
long l;
short s;
};

int main(int argc, char *argv[])
{
struct stu s;

printf("sizeof(s) = %ld\n", sizeof(s));
printf("sizeof(struct stu) = %ld\n",sizeof(struct stu));

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct stu
{
char c;
short s;
int i;
};

int main(int argc, char *argv[])
{
struct stu s;

printf("&s.c = %p\n", &s.c);
printf("&s.s = %p\n", &s.s);
printf("&s.i = %p\n", &s.i);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct JY
{
char name[20];
char sex;
int wuli;
int zhili;
int minjie;
};

void print(struct JY s);
void change(struct JY *p);

void print(struct JY s)
{
printf("姓名 : %s\n", s.name);
printf("性别 : %c\n", s.sex);
printf("武力 : %d\n", s.wuli);
printf("智力 : %d\n", s.zhili);
printf("敏捷 : %d\n", s.minjie);
}

void change(struct JY *p)
{
scanf("%s", p->name);
getchar();
scanf("%c", &p->sex);
scanf("%d", &p->wuli);
scanf("%d", &p->zhili);
scanf("%d", &p->minjie);
}

int main(int argc, char *argv[])
{
struct JY s = {"yangguo", 'M', 99, 90, 80};

print(s);//通过print函数打印玩家信息

change(&s);//通过change函数改变玩家信息

print(s);//通过print函数打印玩家信息

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct JY
{
char name[20];
char sex;
int wuli;
int zhili;
int minjie;
};

int main(int argc, char *argv[])
{
//struct JY *p;//定义了结构体类型的指针
//指针没有实际的指向,不能这样使用

struct JY s;
struct JY *p = &s;

strcpy(p->name, "guojing");
p->sex = 'M';
p->wuli = 100;
p->zhili = 59;
p->minjie = 70;

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
管理系统
//学生 老师 图书 火车票 菜单 旅馆 殡仪馆
增 删 改 查
打印数据
增加成员
删除成员
修改成员
查找成员
成员排序
数据存储
读取数据

struct cir
{
int x;
int y;
int r;
int color;
};
*/
struct JY//
{
char name[20];
char sex;
int wuli;
int zhili;
int minjie;
};

void print(struct JY *s);
void change(struct JY *p);

void print(struct JY *s)
{
int i = 0;

for(i = 0; i < 3; i++)
{
printf("姓名 : %s\n", s[i].name);//(s+i)->name
printf("性别 : %c\n", s[i].sex);
printf("武力 : %d\n", s[i].wuli);
printf("智力 : %d\n", s[i].zhili);
printf("敏捷 : %d\n", s[i].minjie);
printf("========================\n");
}
}

void change(struct JY *p)
{
int i = 0;

for(i = 0; i < 3; i++)
{
scanf("%s", p[i].name);
getchar();
scanf("%c", &p[i].sex);
scanf("%d", &p[i].wuli);
scanf("%d", &p[i].zhili);
scanf("%d", &p[i].minjie);
}
}

int main(int argc, char *argv[])
{
struct JY s[3] = {{"yangguo", 'M', 99, 90, 80},
{"guojing", 'M', 95, 59, 70},
{"zhangwuji", 'M', 98, 95, 90}};

print(s);//通过print函数打印玩家信息

change(s);//通过change函数改变玩家信息

print(s);//通过print函数打印玩家信息

return 0;
}

共用体(联合)

    union是C语言中的关键字
1.共用体的含义
    共用体的成员共用同一块儿存储空间
    (使用一块儿存储空间管理不同的数据类型)
2.共用体的定义
    union tag				//tag是标签
    {
        共用体成员的定义;
    };						//最后需要加 ; 结尾
3.和结构体不同的地方
    1>结构体中每个成员都会占用独立的存储空间
    共用体中每个成员都会占用相同的存储空间
    2>结构体的大小是每个成员大小的和的基础上加入字节对齐
    共用体的大小以共用体成员中最大的成员大小为准
4.大端格式和小端格式
    在硬件的平台中是分为大端格式和小端格式的
    X86/英特尔架构		小端格式
    高字节的数据存放在高地址上,低字节的数据存放在低地址上
    (高对高,低对低)
    例子 : 在小端格式上存储一个十六进制的数据0x12345678
    地址		数据
    0x1000		0x78
    0x1001		0x56
    0x1002		0x34
    0x1003		0x12
    ARM架构->ARM系列大端格式(现在既支持大端也支持小端)
    高字节的数据存放在低地址上,低字节的数据存放在高地址上
    (高对低,低对高)
    例子 : 在大端格式上存储一个十六进制的数据0x12345678
    地址		数据
    0x1000		0x12
    0x1001		0x34
    0x1002		0x56
    0x1003		0x78
可以使用共用体测试当前的机器是大端格式还是小端格式
=========================
笔试题
1.什么是大端格式?什么是小端格式?
2.设计一个程序测试当前的机器是大端格式还是小端格式
=========================
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <stdio.h>
#include <stdlib.h>

struct S
{
char c;
short s;
int i;
long l;
float f;
double d;
};

union U
{
char c;
short s;
int i;
long l;
float f;
double d;
};

int main(int argc, char *argv[])
{
struct S s;
union U u;

printf("sizeof(struct S) = %ld\n", sizeof(struct S));
printf("sizeof(union U) = %ld\n", sizeof(union U));

printf("&s = %p\n", &s);
printf("&s.c = %p\n", &s.c);
printf("&s.s = %p\n", &s.s);
printf("&s.i = %p\n", &s.i);
printf("&s.l = %p\n", &s.l);
printf("&s.f = %p\n", &s.f);

printf("&u = %p\n", &u);
printf("&u.c = %p\n", &u.c);
printf("&u.s = %p\n", &u.s);
printf("&u.i = %p\n", &u.i);
printf("&u.l = %p\n", &u.l);
printf("&u.f = %p\n", &u.f);
printf("&u.d = %p\n", &u.d);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

union U
{
int i;
char c[4];
};

int main(int argc, char *argv[])
{
union U u;

u.i = 13;

printf("u.i = %d\n", u.i);
printf("u.c[0] = %d-%p\n", u.c[0], &u.c[0]);
printf("u.c[1] = %d-%p\n", u.c[1], &u.c[1]);
printf("u.c[2] = %d-%p\n", u.c[2], &u.c[2]);
printf("u.c[3] = %d-%p\n", u.c[3], &u.c[3]);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <stdio.h>
#include <stdlib.h>

union score
{
int i;
float f;
};

struct stu
{
int id;
char name[20];
union score math;
};

int main(int argc, char *argv[])
{
struct stu s;
int flag = 0;

printf("请输入学生的ID : ");
scanf("%d", &s.id);
printf("请输入学生姓名 : ");
scanf("%s", s.name);
printf("请输入学生的成绩 : \n");
printf("(1.整型 2.浮点型)\n");
printf("> ");
scanf("%d", &flag);
switch(flag)
{
case 1 : scanf("%d", &s.math.i); break;
case 2 : scanf("%f", &s.math.f); break;
}

printf("ID : %d\n", s.id);
printf("姓名 : %s\n", s.name);
switch(flag)
{
case 1 : printf("分数 : %d\n", s.math.i); break;
case 2 : printf("分数 : %.1f\n", s.math.f); break;
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>
//需要在无符号的环境中进行使用位域
struct S//位域/位段/位字段(在底层内核驱动时会使用的内容)
{
unsigned int x : 1;
unsigned int y : 2;
unsigned int z : 1;
};

union U
{
char c;
struct S s;
};

int main(int argc, char *argv[])
{
union U u;

u.c = 13;

printf("u.s.x = %d\n", u.s.x);
printf("u.s.y = %d\n", u.s.y);
printf("u.s.z = %d\n", u.s.z);

return 0;
}

位域

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <stdlib.h>

struct GPIO
{
unsigned char PA0 : 2;
unsigned char PA1 : 2;
unsigned char PA2 : 2;
unsigned char PA3 : 2;
unsigned char PA4 : 2;
unsigned char PA5 : 2;
unsigned char PA6 : 2;
unsigned char PA7 : 2;
unsigned char PA8 : 2;
unsigned char PA9 : 2;
unsigned char PA10: 2;
unsigned char PA11: 2;
unsigned char PA12: 2;
unsigned char PA13: 2;
unsigned char PA14: 2;
unsigned char PA15: 2;
};
/*
输入功能 0->00
输出功能 1->01
复用功能 2->10
模拟功能 3->11
*/
int main(int argc, char *argv[])
{
struct GPIO P;

P.PA0 = 1;

P.PA10 = 2;

return 0;
}

============================
2022-7-26
============================

一.温故知新

    多维数组
        二维数组
            可以存储多个相同类型的数据
            可以抽象成有行有列的形式
            数据类型 数组名[行数][列数];
            int arr[2][3];
            一共有6个成员
            arr[0][0] arr[0][1] arr[0][2]
            arr[1][0] arr[1][1] arr[1][2]
int arr[3][5] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
现在想要找出数值为7的成员
            数组名 + 下标	arr[1][1]
            数组名 + 偏移量	*(*(arr + 1) + 1)
            通过sizeof运算符计算
            int arr[3][5];
            sizeof(arr[0][0]) = 4 = 一个成员的大小
            sizeof(arr[0]) = 20 = 一行的大小
            sizeof(arr) = 60 = 整个数组的大小
            sizeof(arr)/sizeof(arr[0][0]) = 15 = 成员个数
            sizeof(arr)/sizeof(arr[0]) = 3 = 行数
            sizeof(arr[0])/sizeof(arr[0][0]) = 5 = 列数
            通过打印每个成员的地址发现
            二维数组的成员也是占用了连续的存储空间
            二维数组的数组名相当于是数组首行的地址
            (一维数组的数组名相当于是数组首成员的地址)
字符数组和字符指针
    在C语言中没有字符串类型
    在C语言中需要使用字符数组来存储字符串数据
    字符数组
1.字符数组的含义
    是用来存储一组字符数据的
2.字符数组的定义
    数据类型	数组名	[成员个数]	;
    char		arr		[10]		;
3.字符数组的初始化
    数据类型	数组名	[成员个数] = {数值,数值};
    char arr[10] = {0};//给数组的10个成员都初始化成0
    char arr[5] = {1,2,3,4,5};
    char arr[5] = {'1','2','3','4','5'};
    char arr[5] = {'\0'};
    char arr[5] = {'0'};
    char arr[10] = "easthome";
    arr[0] = 'e' arr[1] = 'a' ... arr[7] = 'e' 
    arr[8] = '\0'
    char arr[10] = {"easthome"};

注意 : 
    当通过scanf("%c")的方式给字符数组录入字符数据时
    需要注意回车('\n'),因为回车会被当作换行符保存到
    字符数组中
    当通过scanf("%s")的方式给字符数组录入字符数据时
    需要注意空格,因为空格会被当作字符串的截断标志
    当通过scanf("%s")的方式给字符数组录入字符数据时
    需要注意数组越界的问题

字符指针

1.字符指针的含义
    字符指针也是指针,是用来保存地址的
2.字符数组和字符指针
    char arr[10] = "easthome";
    char *p = "easthome";
    字符数组在初始化的时候,是把"easthome"字符串中的字符
    一个一个的保存到字符数组的成员中
    字符指针在初始化的时候,指针p是保存了"easthome"
    这个字符串常量的首地址
    C语言会把内存分为5大区域
    栈区
    堆区
    常量区(read only)
    全局区
    代码区	
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char arr[10] = {0};//初始化10个成员的字符数组
int i = 0;
int size = sizeof(arr) / sizeof(*arr);

for(i = 0; i < size; i++)
{
scanf("%c", &arr[i]);
}

for(i = 0; i < size; i++)
{
printf("arr[%d] = %c\n", i, arr[i]);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char arr[10] = {0};//初始化10个成员的字符数组
int i = 0;
int size = sizeof(arr) / sizeof(*arr);

for(i = 0; i < size; i++)
{
scanf("%c", &arr[i]);
}

for(i = 0; i < size; i++)
{
printf("arr[%d] = %d\n", i, arr[i]);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char arr[10] = {0};//初始化10个成员的字符数组
int i = 0;
int size = sizeof(arr) / sizeof(*arr);

scanf("%s", arr);

for(i = 0; i < size; i++)
{
printf("arr[%d] = %c-%d\n", i, arr[i], arr[i]);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char arr[10] = {0};//初始化10个成员的字符数组
int i = 0;
int size = sizeof(arr) / sizeof(*arr);

scanf("%s", arr);

printf("arr = %s\n", arr);
/*
%d->int
%f->double
%c->int
%s->char *
*/

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char arr[10] = "easthome";
char brr[10] = {"easthome"};

printf("arr = %s\n", arr);
printf("brr = %s\n", brr);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char arr[10] = "easthome";
char *p = "easthome";

printf("arr = %s\n", arr);
printf("p = %s\n", p);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char arr[10] = "easthome";//可以,数组成员保存每个字符
char *p = "easthome";//可以,指针保存字符串常量的首地址
printf("arr = %s\n", arr);//可以打印
printf("p = %s\n", p);//可以打印
p++;//可以,指针保存的是字符串常量首地址
printf("p = %s\n", p);//自增之后保存下一个成员的地址
arr++;//不可以,数组名不能做自增或者赋值运算
printf("arr = %s\n", arr);
//arr[0] = 'E';//可以,实际上是修改了数组首成员的数据
//printf("arr = %s\n", arr);
//p[0] = 'E';//不可以,指针保存的是常量区地址(readonly)
//printf("p = %s\n", p);

return 0;
}

常量区

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
char src[10] = "easthome";
char dest[10] = {0};

printf("strcpy = %s\n", strcpy(dest, src));

//printf("dest = %s\n", dest);

//strcpy(dest, src);

//printf("dest = %s\n", dest);

return 0;
}

/*
调用strcpy函数时,第一个参数必须是一个字符数组,
    第二个参数可以是字符数组\字符指针\字符串常量
调用strcat函数时,第一个参数必须是一个字符数组,
    第二个参数可以是字符数组\字符指针\字符串常量
*/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
//char src[10] = "easthome";
char *src = "embsky!";
char dest[32] = "embsky";
int ret = 0;

//ret = strcmp(src, dest);
ret = strcmp("embsky", "easthome");
printf("ret = %d\n", ret);
//printf("strlen = %ld\n", strlen("Hello World!"));
//printf("strlen = %ld\n", strlen(dest));
//printf("strlen = %ld\n", strlen(src));
//printf("dest = %s\n", dest);
//strcpy(dest, "embsky");
//strcat(dest, src);
//printf("dest = %s\n", dest);

return 0;
}
/* 重新实现strcpy strcat strcmp strlen 字符串的结束标志是'\0'
1
2
3
4
5
6
7
strncpy strncat strncmp

char dest[5] = "abc";
char *src = "Hello World!";

strcpy(dest, src);
strncpy(dest, src, 5);
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <stdio.h>
#include <stdlib.h>

char *mystrcpy(char *dest, const char *src);
char *mystrcat(char *dest, const char *src);
int mystrcmp(const char *s1, const char *s2);
size_t mystrlen(const char *s);

char *mystrcpy(char *dest, const char *src)
{
int i = 0;

for(i = 0; src[i] != '\0'; i++)//可以把有效的字符遍历
dest[i] = src[i];
dest[i] = src[i];//为了拷贝'\0'

return dest;
}

char *mystrcat(char *dest, const char *src)
{
int i = 0, j = 0;

for(i = 0; dest[i] != '\0'; i++)//用来找字符串的'\0'
;
for(j = 0; src[j] != '\0'; i++,j++)
dest[i] = src[j];
dest[i] = src[j];//这次赋值是为了拷贝'\0'

return dest;
}

int mystrcmp(const char *s1, const char *s2)
{
int i = 0;

for(i = 0; s1[i] == s2[i] && s1[i] != '\0'; i++)
;
/////如果程序可以执行到此处
return s1[i] - s2[i];
}

size_t mystrlen(const char *s)
{
int i = 0;

for(i = 0; s[i] != '\0'; i++)
;
return i;
}

int main(int argc, char *argv[])
{
char *src = "Hello";
char dest[32] = "World";

printf("mystrcpy = %s\n", mystrcpy(dest, src));
printf("mystrcat = %s\n", mystrcat(dest, src));
printf("mystrcmp = %d\n", mystrcmp(dest, src));
printf("mystrlen = %ld\n", mystrlen(dest));
//printf("dest = %s\n", dest);
//mystrcpy(dest, src);
//printf("dest = %s\n", dest);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char *p = "root:x:0:0:root:/root:/bin/bash";
//需要大家把/etc/passwd文件读出的内容进行解析
int i = 0;

for(i = 0; p[i] != '\0'; i++)
{
if(p[i] != ':')
printf("%c", p[i]);
else
printf("\n");
}


return 0;
}

========================
2022-7-27
========================

一.温故知新

1.求10000-11000中所有的质数
2.冒泡法排序
3.实现strcat
    char *mystrcat(char *dest, const char *src); 
字符数组和字符指针
    在C语言中是没有字符串类型的,
    想要存储字符串数据可以使用字符数组
    数组越界的问题
    ----------------------------------
    char *p = "Hello";

    字符串的操作
    strcpy strcat strcmp strlen
    mystrcpy mystrcat mystrcmp mystrlen
    ----------------------------------
    mystrncpy mystrncat mystrncmp
    ----------------------------------
    char dest[2];
    char *src = "Hello World";

    strcpy(dest, src);

    strncpy

用多文件的形式实现一下冒泡法排序

main.c sort.c sort.h makefile

vim * -p(以标签页的形式打开多文件)
切换文件 ctrl + alt + pgup/pgdn

1
2
3
4
5
6
7
#ifndef __SORT_H
#define __SORT_H




#endif
========================== 作业: 1.把今天讲过的内容完完整整的复习一遍 2.把之前讲过的功能函数都拆成多文件的形式写一遍 1)子弹的程序 2)排序(冒泡法 选择法 ...) 3)字符串操作函数(mystrcpy mystrcat mystrcmp mystrlen) 4)atoi函数功能 3.把五子棋游戏拆成多文件的形式实现 ==========================

一.写C语言程序的四个阶段

第一阶段	只写一个.c文件,在.c文件中只写一个main函数
            把所有功能都堆到main函数中
第二阶段	只写一个.c文件,在.c文件中除了main函数以外
            封装了其他的功能函数
    函数的声明
函数的返回值类型 函数名(参数列表);//该处的参数指的是形参
    函数的实现
函数的返回值类型 函数名(参数列表)//该处的参数指的是形参
{
    功能实现的代码
}
    函数的调用
函数名(参数);//该处的参数指的是实际参数
第三阶段	以项目工程的形式编写程序,以多文件的形式
            main.c		存放主函数和功能函数的调用
            func.c		存放功能函数的实现
            func.h		存放功能函数的声明以及各种定义
            makefile	存放的是编译规则
第四阶段	把功能函数的实现封装成库文件
            静态库和动态库

二.makefile文件的简介

    一个项目工程中源文件不计其数,这些源文件按照文件类型,
    功能,模块,分别存放在不同的目录中,makefile文件定义了
    一系列的规则来指定哪些文件需要先编译,
    哪些文件需要后编译,哪些文件需要重新编译
    包括更复杂的一些编译操作

三.内存空间的分布

    C语言会把内存分为5个区域
    (栈区 堆区 常量区 全局区 代码区)
    ./a.out执行程序时,是由一个进程来执行程序的
    进程的空间大小是4Gb,是由真实内存4Kb虚拟出来的

    1Gb			Kernel(内核)
    ----------------------------
    3Gb			命令行参数以及环境变量
                栈区(从上往下分配空间)

                空白

                堆区(从下往上分配空间)
                静态区(全局区)未初始化的(系统会初始化成0)
                静态区(全局区)已初始化的
                常量区(只读区)Read Only
                代码区
    栈区		不需要程序员管理,自己开辟,自己释放
    堆区		需要程序员管理,需要手动开辟,需要手动释放
                (malloc calloc realloc free)
    全局区		全局变量/static关键字修饰的变量
    常量区		Read Only
                char *p = "Hello";
                字符串常量"Hello"存储在常量区中
    代码区		程序员没有办法管理

四.全局变量和局部变量

    局部变量
        在某个函数内部定义或者块内定义的变量叫做局部变量
        局部变量只能在函数内部或者块内使用
        局部变量在函数之外是不可知的

    全局变量
        全局变量是定义在函数之外的,通常写到程序开头的位置
        全局变量在整个程序中生命周期都是有效的
        在任意函数内部都可以访问全局变量

    注意:
    全局变量和局部变量可以同名
    如果出现了同名情况,那么在执行该函数时,优先使用局部变量
    在该函数中全局变量被屏蔽

    全局变量和局部变量在定义时的区别
        当定义局部变量时,系统不会帮助初始化,
            局部变量存储的是垃圾值
        当定义全局变量时,系统会帮助初始化
            int -> 0;
            char -> '\0';
            pointer -> NULL;
    全局变量和局部变量存储空间的区别
        全局变量 : 保存在内存的静态全局区,
            占用的是静态的存储单元
            当程序运行时就被创建了,
            当程序结束时才被销毁
        局部变量 : 保存在栈区中,
            只有在局部变量所在的函数被调用时,
            系统才会帮助分配存储单元,
            当局部变量所在的函数调用结束时被释放
/*
    全局变量和局部变量
    其实在查看一个变量可以通过3个属性进行查看
    存储区域 生命周期 作用域
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <stdio.h>
#include <stdlib.h>

int a = 9527;
/*
全局变量
存储区域 全局区
作用域 整个文件内有效
生命周期 从定义开始到程序结束
*/

void func(void)
{
int a = 100;
/*
局部变量
存储区域 栈区
作用域 函数内有效
生命周期 从函数内定义开始到功能函数结束
*/
printf("func().a = %d\n", a);
}

void func1(void)
{
printf("func1().a = %d\n", a);
}

int main(int argc, char *argv[])
{
int a = 200;

func();

printf("main.a = %d\n", a);

func1();

{
int a = -900;
/*
块级变量
存储区域 栈区
作用域 块内有效
生命周期 从定义开始到块结束
*/
printf("块.a = %d\n", a);
}

printf("main.a = %d\n", a);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdlib.h>

int myatoi(const char *nptr);
/*
1.把字符串的有效字符遍历一遍 //循环遍历
2.想办法把数字的字符转换成整型数字 //数字字符 - '0'
3.想办法把整型数字存储起来
====================================================
4.处理空格的问题
5.处理正负号的问题
*/
int myatoi(const char *nptr)//"9527"
{
int i = 0;
int num = 0;
int flag = 1;//用来标记正负

for(i = 0; nptr[i] == ' '; i++); //为了跳过空格

if(nptr[i] == '-' || nptr[i] == '+')//正负号的判断
{
flag = nptr[i] == '+' ? 1 : -1;
i++;
}
for(; nptr[i] != '\0'; i++)
{
switch(nptr[i])
{
case '0' ... '9' :
num *= 10;
num += nptr[i] - '0';
break;
default : return num * flag;
}
}
return num * flag;
}

int main(int argc, char *argv[])
{
int num = 0;

num = myatoi("- 9527");

printf("myatoi = %d\n", num);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#ifndef __ADD_H//为了防止头文件被重复包含
#define __ADD_H

int add(int , int );//函数的声明

#endif

----------------------------------------------------------

#include "add.h"

int add(int a, int b)
{
return a + b;
}

----------------------------------------------------------

#include <stdio.h>
#include "add.h"

int main(void)
{
int a = 0, b = 0;
int sum = 0;

scanf("%d-%d", &a, &b);

sum = add(a, b);

printf("sum = %d\n", sum);

return 0;
}

----------------------------------------------------------

# 注意:makefile文件是脚本文件,所以想要注释需要用 #
# 注意:makefile文件是非常注重格式的,空格是空格,tab是tab
# 生成的文件 : 依赖的文件(:以左是生成文件,:以右是依赖文件)
main : main.o add.o
gcc main.o add.o -o main
# 注意:使用的工具和上边生成文件的内容要差一个tab
main.o : main.s
gcc -c main.s -o main.o
main.s : main.i
gcc -S main.i -o main.s
main.i : main.c
gcc -E main.c -o main.i
add.o : add.s
gcc -c add.s -o add.o
add.s : add.i
gcc -S add.i -o add.s
add.i : add.c
gcc -E add.c -o add.i
clean :
rm -rf *.i *.s *.o main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#ifndef __MAX_H
#define __MAX_H

int max(int a, int b);

#endif

----------------------------------------------------------

#include "max.h"

int max(int a, int b)
{
return a > b ? a : b;
}

----------------------------------------------------------

#include <stdio.h>
#include "max.h"

int main(void)
{
int a = 0, b = 0;
int sum = 0;

scanf("%d-%d", &a, &b);

sum = max(a, b);

printf("sum = %d\n", sum);

return 0;
}

----------------------------------------------------------

main : main.o max.o
gcc -o main main.o max.o
clean :
rm -rf *.o main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#ifndef __SWAP_H
#define __SWAP_H

void swap(int *, int *);

#endif

----------------------------------------------------------

#include "swap.h"

void swap(int *a, int *b)
{
int tmp = 0;

tmp = *a;
*a = *b;
*b = tmp;
}

----------------------------------------------------------

#include <stdio.h>
#include "swap.h"

int main(void)
{
int a = 0, b = 0;

scanf("%d-%d", &a, &b);

printf("a = %d b = %d\n", a, b);

swap(&a, &b);

printf("a = %d b = %d\n", a, b);

return 0;
}

----------------------------------------------------------

# 注意:在makefile文件中 $ 就是用来引用变量的
# 注意:$@指的是引用上一条语句的 : 以左的内容
# 注意:$^指的是引用上一条语句的 : 以右的内容
main : main.o swap.o
gcc -o $@ $^
#等价于 gcc -o main main.o swap.o
clean :
rm -rf *.o main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#ifndef __INC_H
#define __INC_H

int arr[15][15];//棋盘
int x, y;//坐标
int flag;//标记玩家

void init(void);//初始化棋盘
void draw(void);//画棋盘
void print_infor(void);//打印玩家信息
void check(void);//检测坐标是否合理
int win(void);//判断是否可以赢

#endif

----------------------------------------------------------

#include "inc.h"

void init(void)
{
int i = 0, j = 0;

for(i = 0; i < 15; i++)
{
for(j = 0; j < 15; j++)
{
arr[i][j] = 0;//棋盘清零
}
}
x = y = 0;//坐标清零
flag = 1;//标记置为1
}

----------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include "inc.h"

void draw(void)//画棋盘
{
int i = 0, j = 0;

system("clear");
printf(" 1 2 3 4 5 6 7 8 9 A B C D E F\n");
for(i = 0; i < 15; i++)
{
printf("%X ", i + 1);
for(j = 0; j < 15; j++)
{
switch(arr[i][j])
{
case 0 : printf("+ "); break;
case 1 : printf("O "); break;
case 2 : printf("X "); break;
}
}
printf("\n");
}
}

----------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include "inc.h"

void print_infor(void)//打印玩家信息
{
printf("Player %d > ", flag);
}

#include <stdio.h>
#include "inc.h"

void check(void)//检测坐标是否合理
{
if(x < 1 || x > 15 || y < 1 || y > 15)
printf("下出了棋盘边界,请重新下棋!\n");
else if(arr[x - 1][y - 1] != 0)
printf("下到了重复位置,请重新下棋!\n");
else
{
arr[x-1][y-1] = flag;
flag ^= 3;
}
}

----------------------------------------------------------

#include "inc.h"

int win(void)//判断是否可以赢
{
return 1;
}

----------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include "inc.h"

int main(int argc, char *argv[])
{
init();//初始化棋盘

do
{
draw();//画棋盘
print_infor();//打印玩家信息
scanf("%d-%d", &x, &y);
check();//检测录入的坐标是否合理
}while(win());//如果赢了win()返回0,否则返回1

return 0;
}

----------------------------------------------------------

five : main.o init.o draw.o print.o check.o win.o
gcc -o $@ $^
clean :
rm -rf *.o five

===========================
2022-7-25
===========================

一.温故知新

    数组
线性的存储
    线性表
        顺序表
            数组
                一维数组
1.数组
    1)数组的含义
        数组就是用来存储一组相同类型的数据
    2)数组的定义
        数据类型 数组名[成员个数];
        int		 arr	[10]	 ;
        定义了10个int类型成员的数组
        arr[0] - arr[9]
    3)访问数组的成员
        数组名 + 下标		arr[3];
        数组名 + 偏移量		*(arr+3);
        指针 + 偏移量		*(p + 3);
        指针 + 下标			p[3];
    4)数组的特性
        sizeof(数组名) = 整个数组的大小
        sizeof(成员) = 一个成员的大小
        sizeof(数组名) / sizeof(成员) = 成员个数
        打印地址
            数组的成员占用了连续的存储空间
        一维数组的数组名相当于是数组首成员的地址
    C语言的第一阶段过渡到第二阶段
                多维数组
                    二维数组

二维数组

        链表
            有头/无头(头节点<无实质数据>)
            循环/不循环
            单向/双向
    两种特殊的存储形式
        栈式存储结构	先进后出
        队列式存储结构	先进先出

小项目:五子棋
    1.player VS player
要求
    1>最少完成p v p版本
    2>用多函数实现
    3>完成五子棋最基本功能即可
    4>要有适当的提示信息
    5>代码中要有注释
    6>以组为单位交付
提示
    1>棋盘存储
    2>坐标的打印
    3>玩家的替换
    4>下到重复位置/下出边界以后的处理
    5>判断赢的条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdio.h>
#include <stdlib.h>

void draw(int arr[15][15])
{
int i = 0, j = 0;

printf(" 1 2 3 4 5 6 7 8 9 A B C D E F\n");
for(i = 0; i < 15; i++)
{
printf("%X ", i + 1);
for(j = 0; j < 15; j++)
{
switch(arr[i][j])
{
case 0 : printf("+ "); break;
case 1 : printf("O "); break;
case 2 : printf("X "); break;
}
}
printf("\n");
}
}

int main(int argc, char *argv[])
{
//二维数组中 成员=0 该处无棋子
//成员=1 玩家1下棋
//成员=2 玩家2下棋
int arr[15][15] = {0};//定义二维数组初始化成0
int x = 0, y = 0;//下棋子的坐标
int flag = 1;//标记玩家

do
{
draw(arr);//通过draw函数画棋盘
printf("Player %d > ", flag);
scanf("%d-%d", &x, &y);
//做判断x 和 y的位置是否合理
arr[x - 1][y - 1] = flag;
flag ^= 3;
}while(win());
return 0;
}

多维数组和二维数组的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
int arr[7][4][4][4] = {
{
{
{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{1,1,1,1}
},
{
{0,0,0,1},
{0,0,0,1},
{0,0,0,1},
{0,0,0,1}
},
{
{0,0,0,0},
{0,0,0,0},
{0,0,0,0},
{1,1,1,1}
},
{
{0,0,0,1},
{0,0,0,1},
{0,0,0,1},
{0,0,0,1}
}
},
{
{
{0,0,0,0},
{0,0,0,0},
{0,1,0,0},
{0,1,1,1}
},
{
{0,0,0,0},
{0,0,1,1},
{0,0,1,0},
{0,0,1,0}
},
{
{0,0,0,0},
{0,0,0,0},
{0,1,1,1},
{0,0,0,1}
},
{
{0,0,0,0},
{0,0,0,1},
{0,0,0,1},
{0,0,1,1}
}
},
{
{
{0,0,0,0},
{0,0,0,0},
{0,0,1,0},
{0,1,1,1}
},
{
{0,0,0,0},
{0,0,1,0},
{0,0,1,1},
{0,0,1,0}
},
{
{0,0,0,0},
{0,0,0,0},
{0,1,1,1},
{0,0,1,0}
},
{
{0,0,0,0},
{0,0,0,1},
{0,0,1,1},
{0,0,0,1}
}
},
{
{
{0,0,0,0},
{0,0,0,0},
{0,0,0,1},
{0,1,1,1}
},
{
{0,0,0,0},
{0,0,1,0},
{0,0,1,0},
{0,0,1,1}
},
{
{0,0,0,0},
{0,0,0,0},
{0,1,1,1},
{0,1,0,0}
},
{
{0,0,0,0},
{0,0,1,1},
{0,0,0,1},
{0,0,0,1}
}
},
{
{
{0,0,0,0},
{0,0,0,0},
{0,1,1,0},
{0,0,1,1}
},
{
{0,0,0,0},
{0,0,0,1},
{0,0,1,1},
{0,0,1,0}
},
{
{0,0,0,0},
{0,0,0,0},
{0,1,1,0},
{0,0,1,1}
},
{
{0,0,0,0},
{0,0,0,1},
{0,0,1,1},
{0,0,1,0}
}
},
{
{
{0,0,0,0},
{0,0,0,0},
{0,0,1,1},
{0,0,1,1}
},
{
{0,0,0,0},
{0,0,0,0},
{0,0,1,1},
{0,0,1,1}
},
{
{0,0,0,0},
{0,0,0,0},
{0,0,1,1},
{0,0,1,1}
},
{
{0,0,0,0},
{0,0,0,0},
{0,0,1,1},
{0,0,1,1}
}
},
{
{
{0,0,0,0},
{0,0,0,0},
{0,0,1,1},
{0,1,1,0}
},
{
{0,0,0,0},
{0,0,1,0},
{0,0,1,1},
{0,0,0,1}
},
{
{0,0,0,0},
{0,0,0,0},
{0,0,1,1},
{0,1,1,0}
},
{
{0,0,0,0},
{0,0,1,0},
{0,0,1,1},
{0,0,0,1}
}
}
};
int i = 0, j = 0, x = 0, y = 0;

srand(getpid());

while(1)
{
x = rand() % 7;
y = rand() % 4;
system("clear");
for(i = 0; i < 4; i++)
{
for(j = 0; j < 4; j++)
{
if(arr[x][y][i][j] == 1)
printf("[]");
else
printf(" ");
}
printf("\n");
}
sleep(1);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 二维数组
1.二维数组的含义
是用来存储相同类型的数据的
可以抽象出行和列的存储形式,适合存储矩阵的数据
2.二维数组的定义
数据类型 数组名 [行数] [列数] ;
char arr [2] [3] ;
int arr [3] [4] ;
float arr [4] [2] ;
无论是行数还是列数都是从0开始计数的
arr[0][0] arr[0][1] arr[0][2] arr[0][3]
arr[1][0] arr[1][1] arr[1][2] arr[1][3]
arr[2][0] arr[2][1] arr[2][2] arr[2][3]
3.访问二维数组成员的方法
1)数组名 + 下标
2)数组名 + 偏移量
二级指针不能和二维数组产生关联(类型不匹配)
需要使用数组指针和二维数组产生关联
4.二维数组的特性
1)通过sizeof运算符进行测试
int arr[2][3];
sizeof(arr) = 24 = 整个数组的大小
sizeof(arr[0]) = 12 = 一行的大小
sizeof(arr[0][0]) = 4 = 一个成员的大小
sizeof(arr) / sizeof(arr[0][0]) = 6 = 成员个数
sizeof(arr) / sizeof(arr[0]) = 2 = 行数
sizeof(arr[0] / sizeof(arr[0][0])) = 3 = 列数
2)虽然二维数组可以抽象出行和列的形式,
但是二维数组中的每个成员的地址都是连续的
3)二维数组的数组名表示的数组首行的地址
5.二维数组的初始化
数据类型 数组名 [行数][列数] = {数值,数值};
int arr[2][3] = {0};//所有成员都初始化为0
int arr[2][3] = {11,22,33,44,55,66};
//每个成员获取一个数值
int arr[2][3] = {{11,22,33},{44,55,66}};
===============================================
int arr[][3] = {11,22,33,44,55,66};
注意 : 二维数组特殊的初始化方式,可以不指定行数
但是列数必须要指定
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <stdio.h>
#include <stdlib.h>

void print(int arr[2][3]);
void change(int arr[][3]);

void print(int arr[2][3])
{
int i = 0, j = 0;

//for(i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)

for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
{
printf("arr[%d][%d] = %d\n", i, j, arr[i][j]);
}
}
}
void change(int arr[][3])
{
int i = 0, j = 0;

for(i = 0; i < 2; i++)
{
for(j = 0; j < 3; j++)
{
scanf("%d", &arr[i][j]);
}
}
}

int main(int argc, char *argv[])
{
int arr[2][3] = {11,22,33,44,55,66};

print(arr);//通过print函数遍历二维数组
change(arr);//通过change函数改变二维数组的数据
print(arr);//通过print函数遍历二维数组

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0, j = 0;
int num = 1;
int arr[3][4];
int **pp = arr;

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
arr[i][j] = num++;
}
}

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
printf("*(*(arr+%d)+%d) = %d\n", i, j, *(*(arr+i)+j));
}
}

return 0;
}

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0, j = 0;
int num = 1;
int arr[3][4];
int **pp = arr;

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
arr[i][j] = num++;
}
}

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
printf("*(*(arr+%d)+%d) = %d\n", i, j, *(*(arr+i)+j));
}
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0, j = 0;
int num = 1;
int arr[3][4];

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
arr[i][j] = num++;
}
}

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
printf("*(*(arr+%d)+%d) = %d\n", i, j, *(*(arr+i)+j));
}
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[3][4];

printf("&arr[0][0] = %p\n", &arr[0][0]);
//表示的是数组首成员的地址
printf("&arr[0] = %p\n", &arr[0]);
//表示的是数组首行的地址
printf("arr[0] = %p\n", arr[0]);
//表示的是数组首成员的地址
printf("arr = %p\n", arr);
//表示的是数组首行的地址
printf("&arr = %p\n", &arr);
//表示的是整个数组的地址
printf("*arr = %p\n", *arr);
printf("========================\n");
printf("&arr[0][0] + 1 = %p\n", &arr[0][0] + 1);
printf("&arr[0] + 1 = %p\n", &arr[0] + 1);
printf("arr[0] + 1 = %p\n", arr[0] + 1);
printf("arr + 1 = %p\n", arr + 1);
printf("&arr + 1 = %p\n", &arr + 1);
printf("*arr + 1 = %p\n", *arr + 1);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[3][4];
int i = 0, j = 0;

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
printf("&arr[%d][%d] = %p\n", i,j,&arr[i][j]);
}
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[3][4];

printf("sizeof(arr[0][0]) = %ld\n",sizeof(arr[0][0]));
printf("sizeof(arr[0]) = %ld\n",sizeof(arr[0]));
printf("sizeof(arr) = %ld\n",sizeof(arr));


return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0, j = 0;
int num = 1;
int arr[3][4];

for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
arr[i][j] = num++;
}
}
for(i = 0; i < 3; i++)
{
for(j = 0; j < 4; j++)
{
printf("arr[%d][%d] = %d\n", i, j, arr[i][j]);
}
}

return 0;
}
输入十个人的身高,要求排序,从低到高打印 排序 (选择法排序) 首先从未排序的成员中找到最值 然后和数组开头的成员进行交换 然后再从剩余的未排序的成员中找到最值 然后再和剩余成员的开头位置进行交换 以此类推 ============================= 把冒泡法排序和选择法排序给组长讲一遍 (以讲懂了为标准) =============================
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <stdlib.h>

void print(const int *arr, int size);
void sort(int *arr, int size);

void print(const int *arr, int size)
{
int i = 0;

for(i = 0; i < size; i++)
printf("%d\t", arr[i]);
printf("\n");
getchar();
}

void sort(int *arr, int size)
{
int i = 0, j = 0, k = 0;
int tmp = 0;

for(i = 0; i < size - 1; i++)
{
k = i;//让k和i标记相同的位置
for(j = i + 1; j < size; j++)
{
if(arr[k] > arr[j])
k = j;//让k标记j的位置
}
if(i != k)//判断i和k是否标记相同的位置
{
tmp = arr[i];
arr[i] = arr[k];
arr[k] = tmp;
print(arr, size);
}
}
}

int main(int argc, char *argv[])
{
int arr[5] = {100, 201, 50, 70, 30};
int size = sizeof(arr) / sizeof(*arr);

print(arr, size);//通过print函数遍历数组

sort(arr, size);//通过sort函数进行从小到大的排序

print(arr, size);//通过print函数遍历数组

return 0;
}
输入十个人的身高,要求排序,从低到高打印 排序 (冒泡法排序) 比较两个相邻的成员,如果不满足大小顺序,当即完成交换操作 按照遍历顺序把第一对相邻的成员到 最后一对相邻的成员都完成对比 这样数组中的最值就冒到了最后的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <stdlib.h>

void print(const int *arr, int size);
void sort(int *arr, int size);

void print(const int *arr, int size)
{
int i = 0;

for(i = 0; i < size; i++)
printf("%d\t", arr[i]);
printf("\n");
getchar();
}

void sort(int *arr, int size)
{
int i = 0, j = 0;
int tmp = 0;

for(i = 0; i < size - 1; i++)
{
for(j = 0; j < size - 1 - i; j++)
{
if(arr[j] > arr[j + 1])
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
print(arr, size);
}
}
}
}

int main(int argc, char *argv[])
{
int arr[5] = {100, 201, 50, 70, 30};
int size = sizeof(arr) / sizeof(*arr);

print(arr, size);//通过print函数遍历数组

sort(arr, size);//通过sort函数进行从小到大的排序

print(arr, size);//通过print函数遍历数组

return 0;
}
有1000发子弹,要提前装到10个箱子里面,接收键盘输入,要取多少颗子弹数,只能显示整箱的个数,问这10个箱子怎么装,编程实现 二进制 100 逐次趋近式的方法 arr[0] = 1 arr[1] = 2 arr[2] = 4 arr[3] = 8 arr[4] = 16 arr[5] = 32 arr[6] = 64 arr[7] = 128 arr[8] = 256 arr[9] = 489
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include <stdlib.h>

void init(int *, int);
void print(int *, int);
void take_out(int , int *, int);

void init(int *arr, int size)
{
int i = 0;
int sum = 1;
int x = 0;

for(i = 0; i < size - 1; i++)
{
arr[i] = sum;
x += sum;
sum *= 2;
}
arr[i] = 1000 - x;
}

void print(int *arr, int size)
{
int i = 0;

for(i = 0; i < size; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}

void take_out(int num, int *arr, int size)
{
int i = 0;

for(i = size - 1; i >= 0; i--)
{
if(arr[i] <= num)
{
printf("arr[%d] = %d\n", i, arr[i]);
num -= arr[i];
}
}
}

int main(int argc, char *argv[])
{
int arr[10] = {0};//存子弹
int size = sizeof(arr) / sizeof(arr[0]);//存储成员个数
int num = 0;//存储要取走的子弹数

init(arr, size);//通过init函数存储子弹

print(arr, size);//通过print函数遍历数组

scanf("%d", &num);//录入取走的子弹数

take_out(num, arr, size);//取子弹

return 0;
}
进制转换的计算器 完成itob函数(十->八) 十 -> 二 void itob(int num, int arr[8], int size);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdlib.h>

void itob(int num, int *arr, int size);
void print(int *arr, int size);

void itob(int num, int *arr, int size)
{
int i = 0;

for(i = size - 1; i >= 0; i--)
{
arr[i] = num % 2;
num /= 2;
}
}
void print(int *arr, int size)
{
int i = 0;

for(i = 0; i < size; i++)
printf("%d ", arr[i]);
printf("\n");
}

int main(int argc, char *argv[])
{
int num = 0;//保存录入的十进制数字
int arr[32] = {0};//初始化8个成员的数组
int size = sizeof(arr) / sizeof(*arr);//保存成员个数

scanf("%d", &num);//录入十进制数字

itob(num, arr, size);//调用itob把十进制转换成二进制

print(arr, size);//通过print函数遍历数组

return 0;
}
口算卡
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

void Rand(int *, int *, int *);
int print(int , int , int);
int cmp(int , int);

void Rand(int *s1, int *s2, int *c)
{
*s1 = rand() %10;
*s2 = rand() % 9 + 1;
*c = rand() % 5;
}

int print(int s1, int s2, int c)
{
switch(c)
{
case 0:printf("%d + %d = ", s1, s2);return s1+s2;
case 1:printf("%d - %d = ", s1, s2);return s1-s2;
case 2:printf("%d * %d = ", s1, s2);return s1*s2;
case 3:printf("%d / %d = ", s1, s2);return s1/s2;
case 4:printf("%d %% %d = ", s1, s2);return s1%s2;
}
}

int cmp(int T, int guess)
{
if(T == guess)
{
printf("Bingo! +10分!\n");
return 10;
}
else
{
printf("Error! === %d\n", T);
return 0;
}
}

int main(int argc, char *argv[])
{
int s1 = 0, s2 = 0, c = 0;//2个操作数以及1个运算符
int T = 0, guess = 0;//1个正确结果和一个客户填写的答案
int score = 0;//记录总分
int i = 0;

srand(getpid());//随机种子

for(i = 0; i < 10; i++)
{
Rand(&s1, &s2, &c);//产生随机数
T = print(s1, s2, c);//打印表达式并把正确结果返回
scanf("%d", &guess);//录入正确的结果
score += cmp(T, guess);//对比结果
}

printf("总分 : %d\n", score);

return 0;
}

===========================
2022-7-20
===========================

指针(point/pointer)
1.指针的含义
嵌入式开发工程师需要遵守的规则
谁打开,谁关闭	文件IO
谁开辟,谁释放	堆区空间 malloc
谁创建,谁销毁	链表
谁加锁,谁解锁	线程

    指针就是用来存储地址的
    变量是用来存储数据的(会存在内存中<以二进制补码形式>)
    指针是用来存储地址的(会存在内存中<以二进制补码形式>)
    虽然在内存中以二进制补码的形式存储地址,
    但是会以十六进制的方式展示给程序员
    注意 :地址是没有负的(最小的地址0x00000000)
2.指针的定义
    定义变量	type name;
    定义指针	type * name;
    数据类型	指针名	;
1
2
3
4
5
6
7
8
9
char 		c		;
int i ;
floar f ;
char * pc ;
int * pi ;
float * pf ;

int * p //定义了一个 int* 类型的指针p
int * p = &i; //把int类型变量i的地址保存到指针p当中
3.通过sizeof了解指针的数据类型的特点 无论何种类型的指针,在我当前的机器上 打印出来的字节大小都是8个字节 我当前机器的CPU是64bit的CPU,安装的是64bit的Ubuntu系统 代表在我当前的机器中一个地址长度就是64bit 指针是用来存储地址的,所以指针应当能够存储64bit的地址 所以无论体积类型在我当前机器上都是8个字节 4.指针的命名规则和变量的命名规则一致 5.指针运算符 & 取地址的运算符(用来取地址的) 可以使用在变量\指针\数组成员 * 取值的运算符(用来取值的) 可以使用在地址 6.指针的赋值 变量名 = 数值 ;
1
2
3
4
5
6
c		=	'A'		;
i = 9527 ;
f = 3.14 ;
pc = &c ;
pi = &i ;
pf = &f ;
7.指针的初始化
1
2
3
4
5
6
7
8
9
char	c	=	'A'	;
int i = 100 ;
float f = 3.14;

数据类型 指针名 = 地址 ;
char * pc = &c ;
int * pi = &i ;
float * pf = &f ;
int * p = NULL ;
注意 :如果初始化指针时,不确定指针指向哪个空间, 可以先将指针指向NULL(空) NULL代表的就是空的意思,在ASCII表中表示的就是0x0 (其实就相当于给指针赋了一个地址0x0) 8.指针的定义和指针的初始化的区别(和变量的内容一样) 9.指针的使用 1>指针在使用时应当先定义再使用 2>指针应该保存相应数据类型变量的地址 int *类型的指针保存 int类型变量地址 char *类型的指针 保存 char类型变量地址 float *类型的指针 保存 float类型变量地址 3>指针可以做加减法运算(自增自减运算) int *P; p + 1; p += 1; P++; ++p; 注意 :不同类型的指针,在做加减法运算时, 所偏移的字节数是不同的, 和指针本身要保存的变量的数据类型有关 例子: int *pi;//指针保存的地址是0x1000 pi++; printf ("pi");//0x11004 10.特殊的指针 1>空类型的指针(万能指针) void *p; 空类型的指针可以保存任意类型的地址,所以叫做万能指针 会在指针的强制类型转换时使用 2>空指针(指向NULL的指针) 在C语言中,不允许访问地址为NULL的存储空间, 因为该空间是系统保留的,所以不允许访问 操作空指针,程序在执行时会报段错误 3>野指针(没有对象的指针) 野指针指向一个已经删除或者已释放的空间 与空指针不同,没有办法通过简单的判断避免使用野指针 只能通过养成良好的编程习惯来尽力减少 可以主动的把已删除或者已释放的指针变为空指针 再去判断空指针即可 -------------------------------------------------------- 在操作指针时,可以使用一些方法,避免使用空指针 可以加入判断 if(p == NULL)//if(NULL == p) printf ("指针p是一个空指针!\n");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 1;
int sum = 0;

loop:
if(i <= 100)
{
sum += i++;
goto loop;
}

printf("sum = %d\n", sum);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
printf("sizeof(char *) = %ld\n", sizeof(char *));
printf("sizeof(short *) = %ld\n", sizeof(short *));
printf("sizeof(int *) = %ld\n", sizeof(int *));
printf("sizeof(long *) = %ld\n", sizeof(long *));
printf("sizeof(float *) = %ld\n", sizeof(float *));
printf("sizeof(double *) = %ld\n", sizeof(double *));

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0;

printf("i = %d &i = %p\n", i, &i);

i = 13;

printf("i = %d &i = %p\n", i, &i);

return 0;
}
![06point](https://s2.loli.net/2022/08/16/7unBeUcYKkm3zog.png)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0;

printf("i = %d &i = %p\n", i, &i);

i = 13;

printf("i = %d &i = %p\n", i, &i);

//printf("*(&i) = %d\n", *(&i));//可以使用
printf("&(*i) = %d\n", &(*i));//不能使用

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0;
int *p = &i;//把int类型变量i的地址保存到指针p当中
printf("i = %d &i = %p\n", i, &i);
printf("*p = %d p = %p\n", *p, p);
i = 13;
printf("i = %d &i = %p\n", i, &i);
printf("*p = %d p = %p\n", *p, p);

//printf("*(&i) = %d\n", *(&i));//可以使用
//printf("&(*i) = %d\n", &(*i));//不能使用

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0;
int *p = &i;//把int类型变量i的地址保存到指针p当中

printf("i = %d &i = %p\n", i, &i);
printf("*p = %d p = %p\n", *p, p);

//*p = 9527;
scanf("%d", p);

printf("i = %d &i = %p\n", i, &i);
printf("*p = %d p = %p\n", *p, p);

//printf("*(&i) = %d\n", *(&i));//可以使用
//printf("&(*i) = %d\n", &(*i));//不能使用

return 0;
}
![09point](https://s2.loli.net/2022/08/16/CasOxBrYX1wSuvm.png)
1
2
3
4
5
6
7
8
9
10
11
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int *p = 0x12345678;

printf("p = %p *p = %d\n", p, *p);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char *pc;
short *ps;
int *pi;
long *pl;
float *pf;
double *pd;

printf("sizeof(pc) = %ld sizeof(*pc) = %ld\n", sizeof(pc), sizeof(*pc));
printf("sizeof(ps) = %ld sizeof(*ps) = %ld\n", sizeof(ps), sizeof(*ps));
printf("sizeof(pi) = %ld sizeof(*pi) = %ld\n", sizeof(pi), sizeof(*pi));
printf("sizeof(pl) = %ld sizeof(*pl) = %ld\n", sizeof(pl), sizeof(*pl));
printf("sizeof(pf) = %ld sizeof(*pf) = %ld\n", sizeof(pf), sizeof(*pf));
printf("sizeof(pd) = %ld sizeof(*pd) = %ld\n", sizeof(pd), sizeof(*pd));

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 9527;
char *p = &i;

printf("i = %d &i = %p\n", i, &i);
printf("*p = %d p = %p\n", *p, p);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
short i = 100;
short *p = &i;

printf("i = %d &i = %p\n", i, &i);
printf("*p = %d p = %p\n", *p, p);

++p;//p++;//p+=1;

printf("i = %d &i = %p\n", i, &i);
printf("*p = %d p = %p\n", *p, p);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
char c = 0;
short s = 0;
int i = 0;
long l = 0;
float f = 0;
double d = 0;
void *p;

p = &c;
p = &s;
p = &i;
p = &l;
p = &f;
p = &d;

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 100;
void *p = &i;

printf("p = %p\n", p);
printf("*p = %d\n", *p);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int *p = NULL;

*p = 100;

printf("*p = %d\n", *p);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int *p = NULL;

p = malloc(sizeof(int));
*p = 100;
printf("free之前*p = %d\n", *p);
printf("free之前p = %p\n", p);
free(p);
p = NULL;//把野指针变为了空指针
printf("free之后*p = %d\n", *p);
printf("free之后p = %p\n", p);

*p = 9527;//使用野指针

printf("free之后*p = %d\n", *p);
printf("free之后p = %p\n", p);

return 0;
}

===========================
2022-7-21
===========================

一.温故知新	
    指针
        malloc
    指针的含义 : 指针就是用来存储地址
    NULL
    指针的定义 : 数据类型	指针名	;
                 char *		pc		;
                 char *		pi		;
                 float *	pf		;

指针

    通过sizeof测试
    指针的命名规则和变量的命名规则一致
    int i = 0;
    int *p;

    p = &i;
    *p
    指针运算符
    数据类型	指针名	=	地址	;
    int *		p		=	NULL	;
    int *p;	->	int
    char *p;	->	char
    指针可以做加减法运算
        p+1;	p+=1;	p++;	++p;
    特殊指针
        空类型指针(万能指针)
        空指针
        野指针

    容易混淆的声明
        const	指针
        指针常量
        常量指针
        数组	函数
        指针数组
        数组指针
        指针函数
        函数指针
        函数指针数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

int add(int a, int b);//函数的声明

int add(int a, int b)//函数的实现
{
return a + b;
}

int main(int argc, char *argv[])
{
int a = 0, b = 0;
int sum = 0;

scanf("%d-%d", &a, &b);

sum = add(a, b);//函数的调用

printf("sum = %d\n", sum);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

int max(int , int );//函数的声明

int max(int x, int y)//函数的实现
{
//x > y ? return x : return y;
return x > y ? x : y;
}

int main(int argc, char *argv[])
{
int a = 0, b = 0;
int sum = 0;

scanf("%d-%d", &a, &b);

sum = max(a, b);//函数的调用

printf("sum = %d\n", sum);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

int isleapyear(int );//函数的声明

int isleapyear(int abc)//函数的实现
{
return (!(abc%4)&&(abc%100)) || (!(abc%400));
}

int main(int argc, char *argv[])
{
int year = 0;
int sum = 0;

scanf("%d", &year);

sum = isleapyear(year);//函数的调用
//如果sum = 1代表是闰年 如果sum = 0代表是平年

sum == 1 ? printf("%d年是闰年!\n", year) : printf("%d年是平年!\n", year);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <stdio.h>
#include <stdlib.h>

void func(int ,int);

void func(int a, int b)
{
int min = 0, max = 0;
int i = 0, j = 0;

min = a < b ? a : b;
max = a > b ? a : b;

for(i = min; i <= max; i++)
{
for(j = 2; j < i; j++)
{
if(i % j == 0)
break;
}
if(i == j)
printf("%d\t", i);
}
}

int main(int argc, char *argv[])
{
int a = 0, b = 0;

scanf("%d-%d", &a, &b);

func(a, b);//函数的调用
//func()的功能是寻找最小值 - 最大值之间的素数

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdio.h>
#include <stdlib.h>

void swap(int ,int);

void swap(int a, int b)
{
int tmp = 0;

printf("swap.交换之前a = %d b = %d\n", a, b);
printf("swap.交换之前a = %p b = %p\n", &a, &b);
tmp = a;
a = b;
b = tmp;
printf("swap.交换之后a = %d b = %d\n", a, b);
printf("swap.交换之后a = %p b = %p\n", &a, &b);
}

int main(int argc, char *argv[])
{
int a = 0, b = 0;

scanf("%d-%d", &a, &b);

printf("main.交换之前a = %d b = %d\n", a, b);
printf("main.交换之前a = %p b = %p\n", &a, &b);

swap(a, b);//通过swap()交换变量a b的值

printf("main.交换之后a = %d b = %d\n", a, b);
printf("main.交换之后a = %p b = %p\n", &a, &b);

return 0;
}

交换变量值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

void swap(int *, int*);

void swap(int *a, int *b)
{
int tmp = 0;

tmp = *a;
*a = *b;
*b = tmp;
}

int main(int argc, char *argv[])
{
int a = 0, b = 0;

scanf("%d-%d", &a, &b);

printf("main.交换之前a = %d b = %d\n", a, b);

swap(&a, &b);//通过swap()交换变量a b的值

printf("main.交换之后a = %d b = %d\n", a, b);

return 0;
}

程序员写C语言程序会经历几个阶段
第一阶段	只写一个.c文件,在.c文件中只写一个main函数
            把所有的功能都放到main函数中
第二阶段	只写一个.c文件,在.c文件中除了有main函数以外
            还会有其他的功能函数的封装
第三阶段	在一个项目工程中存在多个.c或者.h文件
            main.c		存放main函数以及功能函数的调用
            func.c		存放功能函数的实现
            func.h		存放功能函数的声明以及各种定义
            makefile	是一个脚本文件(存放的是编译规则)
第四阶段	把func.c制作成库文件
            动态库和静态库

函数
当写一个C语言程序时,首先需要写的一个函数就是main函数
1.函数的含义
    函数就是一个功能,当调用函数时,会使用这个功能
    printf()	->	打印输出
    scanf()		->	录入
    rand()		->	用来产生随机数
    srand()		->	随机种子
    time()		->	获取时间的描述
    getpid()	->	获取进程ID号
    sleep()		->	秒级延时
    usleep()	->	微秒级延时
    system()	->	调用shell命令
    函数的出现是为了减少代码量,减少代码的冗余
    例子 : printf函数的代码量有100行左右
    如何把一部分的代码封装成一个函数,其实是由程序员决定的
    通过是根据每个函数执行一个特定的功能来进行划分的
2.函数组成部分
    一般来说函数是由三个部分组成的
    1)函数的声明(函数的定义)
        其实就是告诉编译器,函数的名称,函数的返回值
            以及函数的参数
函数的返回值类型 函数名(参数列表);//该处参数指的是形式参数
函数的返回值类型 函数名	(参数列表);
char *			 strcpy (char *dest, const char *src);
void *			 malloc (size_t size);
int				 rand	(void);
void			 srand	(unsigned int seed);
注意 : 
        1>函数名的命名规则和变量的命名规则一致
        2>在写函数的声明时,可以不指定形式参数的名字,
            但是必须要有形式参数的数据类型
    2)函数的实现
        其实就是把函数所代表的功能通过代码进行实现
函数的返回值类型 函数名(参数列表)//该处参数指的是形式参数
{
    功能实现的代码
}
    注意 : 
        1>在写函数的实现时,必须要指定形式参数的名字
    3)函数的调用
        其实就是在代码中使用函数实现的功能
函数名(参数);//该处参数指的是实际参数
形式参数和实际参数是两个不同的内容,可以同名
其实是形式参数来接收实际参数的值的
3.函数的传参
    值传递	: 是把实际参数的值传递给形式参数
            在这种情况下修改形式参数的值不会影响实际参数
        总结: 如果只是想要访问实际参数的值,
                不想修改实际参数的值,那么就使用值传递
        add(); max(); isleapyear(); printf();

    地址传递: 是把实际参数的地址传递给形式参数(指针)
            如果通过形式参数修改指向空间的数据
            会改变实际参数的值
        总结: 如果想要通过形式参数修改实际参数的值
            那么就用地址传递
        swap(); scanf();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <stdio.h>
#include <stdlib.h>

void print(int);
void change(int *);

void print(int a)
{
printf("a = %d\n", a);
}

void change(int *a)
{
scanf("%d", a);
}

int main(int argc, char *argv[])
{
int a = 9527;

print(a);//通过print函数打印变量a的值

change(&a);//通过change函数改变a的值

print(a);//通过print函数打印变量a的值

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 9527;
int *p = &i;

printf("i = %d &i = %p\n", i, &i);
printf("*p = %d p = %p\n", *p, p);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
printf("sizeof(char **) = %ld\n",sizeof(char **));
printf("sizeof(short **) = %ld\n",sizeof(short **));
printf("sizeof(int **) = %ld\n",sizeof(int **));
printf("sizeof(long **) = %ld\n",sizeof(long **));
printf("sizeof(float **) = %ld\n",sizeof(float **));
printf("sizeof(double **) = %ld\n",sizeof(double **));

return 0;
}
/* =========================== 作业: 1.把今天讲过的内容完完整整的复习一遍 2.最少把<猜数游戏>和<口算卡>改为多函数的版本 =========================== 二级指针 1.二级指针的含义 只要是指针,那就是用来存储地址的 一级指针是用来保存变量的地址的 (可以使用一级指针操控变量) 二级指针是用来保存一级指针的地址的 (可以使用二级指针操控一级指针) 2.二级指针的定义 int i ; 数据类型 指针名 ; int * p ; int ** pp ; 3.通过sizeof测试占用存储空间的大小 无论何种类型的二级指针在我当前的机器环境中, 占用空间大小都是8个字节 4.二级指针的命名规则和变量的命名规则一致 5.使用二级指针
1
2
3
4
5
6
7
8
9
10
名	i		p		pp

100 0x1000 0x2000
i p pp
*p *pp
**pp
0x1000 0x2000 0x3000
&i &p &pp
p pp
*pp
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 100;
int *p = &i;
int **pp = &p;

printf("i = %d\n", i);
printf("*p = %d\n", *p);
printf("**pp = %d\n", **pp);
printf("====================\n");
printf("&i = %p\n", &i);
printf("p = %p\n", p);
printf("*pp = %p\n", *pp);
printf("====================\n");
printf("&p = %p\n", &p);
printf("pp = %p\n", pp);
printf("====================\n");
printf("&pp = %p\n", &pp);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 100;
int *p = &i;
int **pp = &p;

printf("&p = %p\n", &p);
printf("pp = %p\n", pp);

pp++;

printf("&p = %p\n", &p);
printf("pp = %p\n", pp);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>

void change(int **, int *);

void change(int **p, int *b)
{
*p = b;
}

int main(int argc, char *argv[])
{
int a = 100, b = 200;
int *p = &a;

printf("*p = %d p = %p\n", *p, p);

//change(p, &b);
change(&p, &b);//通过change函数把指针p指向b变量

printf("*p = %d p = %p\n", *p, p);

return 0;
}
随机函数 rand(); //功能:就是用来产生随机数的 rand() % 1000; //[0 ~ 999] rand() % 100 - 50;//[-50 ~ 49] srand(); //功能:可以保证每一次产生的随机数可变 srand函数在调用时需要有可变的因子 time(NULL); //时间种子 getpid(); //进程种子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <unistd.h>

void func(void);

void func(void)
{
int T = 0;//存储随机出的数字
int guess = 0;//存储玩家猜的数字
int num = 1;

srand(getpid());//随机种子

T = rand() % 10;

for(num = 1; ; num++)
{
scanf("%d", &guess);
if(T > guess)
printf("[%d] Small!\n", num);
else if(T < guess)
printf("[%d] Big!\n", num);
else
{
printf("[%d] Bingo!\n", num);
break;
}
}
}

int main(int argc, char *argv[])
{
func();

return 0;
}

==========================
2022-7-22
==========================

一.温故知新

    函数 : 就是一个功能
    函数的声明
函数的返回值类型 函数名(参数列表);//形式参数
    函数的实现
函数的返回值类型 函数名(参数列表)//形式参数
{
    功能的实现
}
    函数的调用
函数名(参数);//实际参数

值传递	:

地址传递:

    二级指针
    用来保存一级指针的地址
    可以通过二级指针操控一级指针
    type **	pointer_name;
    int **pp;->pp++->偏移量和int *
    char **pp;->pp++->偏移量和char *

数组(arr/array)

1.什么是数组?
    相同类型变量的集合
    数组是由一个一个成员组成,每个成员的数据类型都是相同的
2.数组的含义
    数组就是用来存储一组相同类型的数据的
3.数组的定义
    数据类型	数组名	[成员个数]	;
    int			arr		[5]			;
    char		brr		[20]		;
    float		crr		[3]			;
    1)数组的定义中数据类型->指的是数组的每个成员的类型
    2)数组名的命名规则和变量的命名规则一致
    3)成员个数->就是数组的成员个数
    注意 : 成员个数必须是一个非负的整数
4.访问数组成员的方法
    例子 : int arr[5];//定义了有5个int类型成员的数组arr
            int *p = arr;
    1)数组名 + 下标//在访问成员时下标从0开始计数
        arr[0] arr[1] arr[2] arr[3] arr[4]
        注意 : 没有arr[5]这个成员,如果强行使用arr[5]成员,
            会造成数组越界
    2)数组名 + 偏移量
        *(arr+0) *(arr+1) *(arr+2) *(arr+3) *(arr+4)
    3)指针 + 偏移量
        *(p + 0) *(p + 1) *(p + 2) *(p + 3) *(p + 4)
    4)指针 + 下标
        p[0] p[1] p[2] p[3] p[4]
5.数组的特性
    1)通过sizeof运算符查看数组特性
        sizeof(数组名) = 整个数组的大小
        sizeof(成员) = 一个成员的大小
        sizeof(数组名) / sizeof(成员) = 成员个数
    2)数组中成员的地址都是连续的
        可以通过某个成员的地址进行地址偏移
            找到其他成员的位置
    3)一维数组的数组名表示的是数组首成员的地址
6.数组的初始化
    数据类型	数组名	[成员个数]	=	{数值,数值};
    int			arr		[3]			=	{0,1,2];
    int			arr		[3]			=	{0};
            //会把数组的所有成员初始化成0
    int			arr		[3]			=	{11};
            //会把首成员初始化成11,其他成员初始化成0
    ================================================
    int			arr		[]			=	{11,22,33,44};
            //会把后边数值的个数当作成员个数使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5];

arr[0] = 100;
arr[1] = 9527;
arr[2] = -1;
arr[3] = 123;
arr[4] = 10086;

printf("arr[0] = %d\n", arr[0]);
printf("arr[1] = %d\n", arr[1]);
printf("arr[2] = %d\n", arr[2]);
printf("arr[3] = %d\n", arr[3]);
printf("arr[4] = %d\n", arr[4]);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
//int arr[5];可以
//int arr[0];可以
//int arr[-1];不可以
//int arr[3.14];不可以
//int arr['A'];可以
int i = 5;
int arr[i];

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5];
int i = 0;

for(i = 0; i < 10; i++)
{
arr[i] = i * 10;
printf("arr[%d] = %d\n", i, arr[i]);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5];
int i = 0;

for(i = 0; i < 5; i++)
{
scanf("%d", &arr[i]);
}

for(i = 0; i < 5; i++)
{
printf("arr[%d] = %d\n", i, arr[i]);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5];
int i = 0;

printf("sizeof(arr[0]) = %ld\n", sizeof(arr[0]));
printf("sizeof(arr) = %ld\n", sizeof(arr));


/*for(i = 0; i < 5; i++)
{
printf("sizeof(arr[%d]) = %ld\n", i, sizeof(arr[i]));
}*/

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[10];
int i = 0;

for(i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
scanf("%d", &arr[i]);
}

for(i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("arr[%d] = %d\n", i, arr[i]);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 10;
int arr[5] = {i};

printf("arr[0] = %d\n", arr[0]);
printf("arr[1] = %d\n", arr[1]);
printf("arr[2] = %d\n", arr[2]);
printf("arr[3] = %d\n", arr[3]);
printf("arr[4] = %d\n", arr[4]);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int i = 0;
int arr[5] = {0};

for(i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("arr[%d] = %p\n", i, &arr[i]);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5] = {0};

printf("arr[0] = %p\n", &arr[0]);
//表示的是数组首成员的地址
printf("arr = %p\n", arr);
//表示的是数组首成员的地址
printf("&arr = %p\n", &arr);
//表示的是整个数组的地址
printf("===========================\n");
printf("arr[0] + 1 = %p\n", &arr[0] + 1);
printf("arr + 1 = %p\n", arr + 1);
printf("&arr + 1 = %p\n", &arr + 1);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5] = {11,22,33,44,55};
int i = 0;

for(i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
//printf("arr[%d] = %d\n", i, arr[i]);
printf("*(arr + %d) = %d\n", i, *(arr + i));
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5] = {11,22,33,44,55};

printf("arr = %p\n", arr);

arr += 1;
//arr++;

printf("arr = %p\n", arr);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5] = {11,22,33,44,55};
int i = 0;
int *p = arr;

//p = &arr[0];//把数组首成员的地址赋值给指针p
p = arr;//把数组首成员的地址赋值给指针p

for(i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
//printf("arr[%d] = %d\n", i, arr[i]);
//printf("*(arr + %d) = %d\n", i, *(arr + i));
//printf("*(p + %d) = %d\n", i, *(p + i));
//printf("p[%d] = %d\n", i, p[i]);
printf("%d[arr] = %d\n", i, i[arr]);
}

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
int arr[5] = {11,22,33,44,55};
int *p = arr;

/*
sizeof(arr) / sizeof(arr[0]) = 5
sizeof(arr) / sizeof(*arr) = 5
sizeof(p) / sizeof(*p) = 2
sizeof(p) / sizeof(p[0]) = 2
*/

return 0;
}

数组和函数

1.当多函数程序需要使用数组传参时,
    无论是否需要修改数组成员的值,在函数调用时都传数组名
2.如果功能函数并不想修改实参数组成员的值,
    可以在形参前加const关键字修饰一下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdlib.h>

void print(const int *p, int size);
void change(int *p, int size);

void print(const int *p, int size)
{
int i = 0;

for(i = 0; i < size; i++)
printf("p[%d] = %d\n", i, p[i]);
}

void change(int *p, int size)
{
int i = 0;

for(i = 0; i < size; i++)
scanf("%d", p + i);//&p[i]
}

int main(int argc, char *argv[])
{
int arr[5] = {11,22,33,44,55};
int size = sizeof(arr) / sizeof(arr[0]);

print(arr, size);//通过print函数遍历数组的成员

change(arr, size);//通过change函数改变数组成员的值

print(arr, size);//通过print函数遍历数组的成员

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>
#include <stdlib.h>

int *func(void);

int *func(void)
{
int arr[5] = {11,22,33,44,55};//局部数组

return arr;
}

int main(int argc, char *argv[])
{
int *p = NULL;
int i = 0;

p = func();

for(i = 0; i < 5; i++)
{
printf("p[%d] = %d\n", i, p[i]);
}

return 0;
}