嵌入式基础:C语言和Linux系统基础内容梳理

操作系统

1.操作系统是一个软件
2.管理硬件资源
3.为上层的应用层序提供简单易用的接口。

linux----------------------------------------

1.开源、免费
2.内核可裁剪
3.linux一切皆文件
4.linxu文件系统像是一颗倒置树
5.支持多用户、多任务

一、linux基础指令

1.打开终端

ctrl  + alt + t --打开终端,默认为用户目录
hqyj@ubuntu:~$ 
用户名@主机名:~
~:用户目录  /home/hqyj
/:根目录
    
sudo --临时获取root权限

2.导航命令— pwd ls cd

pwd --打印当前的绝对路径 ---从根目录到当前目录 --- print work dirctory

ls --打印当前路径的所有内容(隐藏文件->以.开头的文件)
ls -a --打印当前路径的所有内容(包含隐藏文件)
ls -l  --打印当前路径的所有内容+详细信息
ls -la 上面两种的组合效果
ls -R --递归显示所有文件
ls -i --文件名前显示索引

linux中有7大文件类型 -- lsp -  bcd
l -- 链接文件
s -- 套接字文件
p -- 管道文件
-  :普通文件
b --块设备文件
c -- 字符设备文件
d -- 目录文件

ls [选项]  路径 --查看指定路径的所有内容


cd  -- change dirctory --跳转到指定的路径
cd  + 路径
cd -  :返回终端上一次操作的路径
cd / :返回根目录

..:代表上一级目录
. :代表当前目录

3.目录文件

mkdir 文件名 --在当前路径创建一个目录文件

mkdir -p  /文件名1/文件名2/...  --同时创建多级子目录

rmdir 目录文件名 --删除指定的空目录文件

rm -r 目录文件名 --删除非空目录文件

练习1:在用户目录下创建一个Class目录文件 ,然后再在Class文件中创建一个230101目录文件。

4.普通文件

touch 文件名  ---创建普通文件

rm 普通文件名  --删除普通文件

cat 普通文件名 --查看文件的内容

5.文件的复制 和剪切 --cp mv

cp 原普通文件名 目标普通文件名 ---如果目标普通文件存在,直接把原文件的内容复制过去,如果不存在则先创建
cp -r 源目录文件名 目标目录文件名

mv 源文件名 目标文件名 
备注:如果原文件和目标文件在同一路径下,则相当于改名(类似于重命名)


6.编辑器 -vi/vim geidt

  • vi/vim
vi + 文件名    
vim + 文件名
注意:如果该文件已经存在则打开该文件,如果该文件不存在则创建

命令行模式:
	j --光标向下移动一行
	k --光标向上移动一行
	h --光标向左移动一列
	l --光标向右移动一列
	
	yy --复制当前光标所在的行
	nyy --复制当前光标之后的n行 ---n是一个整数,代表要复制多少行
	
	dd --剪切当前光标所在的行
	ndd --剪切当前光标之后的n行 ---n是一个整数,代表要复制多少行
	u --撤销上一步操作
	ctrl + r --反撤销
	p  --在当前光标之后开始粘贴
	
	gg=G --自动调整缩进
	
底行模式
    esc键回到命令行模式,然后shift+:回到底行模式
	:w --保存
	:q --退出
	:wq --保存并退出
	:x -- 保存并退出
	:q! --强制退出,不保存修改内容
	:n --将光标跳转到第n行
	:%s/old/new/g --用new将文件中所有的old替换
	/string --在当前文件查找string -按n键来查找下一个
	
命令模式->插入模式:
	i --从当前光标之前开始插入
	a --从当前光标之后开始插入
	A --从当前行末开始插入
	o --从当前行下一行开始插入
	
插入模式->命令模式:Esc

!!!如需更改配置文件,在家目录下:hqyj@ubuntu:~$ vi .vimrc更改。
  • gedit //类似于windows的编辑器

    gedit + 文件名    
    注意:如果该文件已经存在则打开该文件,如果该文件不存在则创建
    

7.编译器–gcc

gcc test.c         //默认生成一个a.out可执行文件
./a.out            //执行a.out

gcc test.c -o test //生成test可执行文件
./可执行文件名       //运行可执行文件

编译的步骤:
	1.预处理 --头文件的展开 宏替换
	2.编译 --将语言转换成汇编语言
	3.汇编 --汇编语言转换成机器语言 .o
	4.链接 --间所有的.o链接在一起生成可执行文件

二、计算机的组成

输入设备 --键盘、麦克风
输出设备 --显示器、扬声器
存储设备 --磁盘、内存、寄存器
程序控制器 --PC
逻辑\算术运算器 -ALU

1个字节 = 8位
1byte  = 8bit

三、计算中数据的表现形式

十进制:0-9 --满十进一
123 = 1 * 10^2 + 2*10^1 + 3*10^0

二进制:0-1 --满二进一 前缀:0b
94 ->0b01011110 = 1 * 2 ^ 6 + 0 * 2^5 + 1 * 2^4 + 1 * 2^3 + 1* 2^2 + 1* 2^1 + 0*2^0
74 ->0b01001010
84 ->0b0101 0100
277 ->100010101

八进制:0 - 7 --满8进一 前缀:0
94 ->0b001 011 110 ->0136
74 ->0b001 001 010->0112
84 ->0b001 010 100->0124
十六进制:0 - 9 、A、B、C、D、E、F --满16进一 前缀:0x
94 ->0b 0101 1110->0x5E
74 ->0b0100 1010->0x4A
84 ->0b0101 0100->0x54


字符型:ASCII

在linuax查询ascii码表:man ascii


C语言---------------------------------------

一、编程规范

#include <stdio.h> //c语言本身并不提供输入、输出,所有的输入输出都是通过调用标准库函数实现的

//int main(int argc,char *argv[])
int main()//所有的c程序都是从主函数开始,从主函数结束
{
    printf("hello wrold\n");//分号是英文的分号,代表一条语句结束。
    return 0;//当程序执行过程中遇到return关键字代表程序结束。
}
int a;
printf("a\n",a);
//在c语言中{}代表一个模块,模块与模块之间注意缩进。

二、词法符号

1.标识符–程序员按照命名规则自定的词法符号—名字

命名规则:
    1.由数字、字母、下滑线组成
    2.不能以数字开头
    3.不能与关键字重名
关键字:32个
	数据类型:char short int long float double signed unsigned  --8个
	存储类型: atuo  static extern register const volatile  --6个
	结构体类型:struct union enum -3个
	语句:if else switch case break default while do for continue goto return --12个
    空类型:void 
	取别名: typedef
    求字节: sizeof
注意:c语言严格区分大小写。
    a A
    int a = 10;
	printf("%d",A);//error

练习1:下列可用于C语言用户标识符的一组是【 B】

A)void, define, WORD

B)a3_b3, _123,Car

C)For, -abc, IF Case

D)2a, DO, sizeof

2.运算符

1.算术运算符 + - * / % ++ --
    %:取余运算符
        10 % 2 = 0;
		10 % 3 = 1;
	注意:左右两边的操作数必须是整数。
    
   ++:自增运算符
      单独使用:自增1,++和后++没有区别,自增1
      int a = 10;
	  a++;//a = a + 1;a = 11
	  ++a;//a = a + 1;a = 11
	  非单独使用:
          前++:先自增再取值
          	int a = 10;
		  	int b = ++a;//a = a + 1; a = 11; b = a = 11;++:先取值,再自增
              int a = 10;
		 	  int b = a++;//b = a = 10;a = a +1 = 11;
		int a = 10;
		int b = a++ + ++a;//

2.关系运算符 > < >= <= == !=
   	int a = 1 < 2;
	如果关系为真,则运算结果为1
    如果关系为假,则运算结构为0

3.逻辑运算符 && || ! 注意:在c语言中用来0代表逻辑上的假,用非0代表真。
        &&:逻辑与
            (表达式1)&&(表达式2)
        运算规则: 如果表达式1和表达式2都为真则运算结果为1
            	如果表达式1和表达式2其中一个为假则运算结果为0
            	全真为真1
            	有假则假0
          int a = (1 < 2)&&(2 > 0);
		  int b = -1 && -2;
	  ||:逻辑或
            (表达式1)||(表达式2)
          运算规则 :有真则真1,
				   全假为假0.
          int a = (1 > 2)||(1 == 1);
	 ! :逻辑非
         !(表达式)
         运算规则:如果表达式为真,则运算结果为0
         		  如果表达式为假,则运算结果为1
    短路法则: 
        逻辑与:当表达1为假时,运算结果为0,表达式2不会被执行
        逻辑或:当表达1为真时,预算结构为1,表达式2不会被执行
4.位运算符 & | ^ ~ << >>
         &:位与
             (表达式1)&(表达式2)
             运算规则:全11,有00
             10&11
             1010
            &1011
             1010 ->10
        |:位或
             (表达式1)|(表达式2)
             运算规则:全00,有11
              10|11
             1010
            |1011
             1011 ->11
	   ^:异或 
            (表达式1)^(表达式2)
           运算规则:相异为1,相同为0
           10 ^ 11
           1010
          ^1011
           0001 ->1
       ~:位非
           ~(表达式)
       	   运算规则:按位取反,1001
           ~10
           ~1010
           	0101>5
           		->-11
机器码:对于一个有符号数来说,最高位用来表示符号位,1表示负数,0表示正数  
原码:机器码
反码:正数:原码
     负数:符号位不变,其余位按位取反。
补码:正数:原码
     负数:反码+1
           注意:计算中存储数据都是存的数据的补码
           char a = 10;
		   char b = 11;
		   原码:0000 1011
           反码:0000 1011
           补码:~0000 1011
               
               	补码:1111 0100
               	反码:1111 0011
               	原码:1000 1100
                
          << :左移
              (表达式1)<<(表达式2)
              正数:高位舍去,低位补0
              负数:高位舍去,低位补0,最后最高位置1
              10 << 2
              -10 << 2
         >> :右移
             (表达式1)>>(表达式2)
             正数:低位舍去,高位补0
             右移:低位舍去,高位补1(用补码来运算)
             10 >> 2;
			 -10 >>2;
			练习:
			char b  = -11;
			b = b >> 2;
5.求字节 sizeof  
    求数据类型或变量的字节。
    int  a ;
	sizeof(int) == sizeof(a);
	sizeof(char);
	sizeof(float);
注意:sizeof的运算结果是一个长整型的数据
6.赋值运算符 = += -= *= /= &=
    int a = 10;	
	a+=10; //a = a + 10; a = 20;
	int b = 10;
	b *= 10;//b = b * 10; b = 100;
7.逗号运算符 ,
	运算规则:以逗号最右边的值作为运算结果。
        int a = (10,11);
		int b = (10,11,12,13);
8.三目运算符 ?:
	(表达式1)?(表达式2):(表达式3)
	运算规则:判断表达式1,如果表达式1的结果为真,则表达式2作为运算结果,如果表达式1的结果的假,则表达式3作为运算结果

​ 位运算:a第n位的清0和置1

​ 一般表达式:

​ 清0:a&~(1<<n)

​ 置1:a|(1<<n)

3.unsigned和signed

  7 #include <stdio.h>
  8 
  9 int main(int argc, char *argv[])
 10 {
 11     unsigned int a=-3;
      //补码:1000 0000 00000000 00000000 0000 0011
      //反码:1000 0000 00000000 00000000 0000 0010
      //原码:1111 1111 11111111 11111111 1111 1101
      //把有符号的赋给无符号,先按照有符号找原码,最后直接忽视符号位
 12     if(a>2)
 13     {
 14         printf("hello\n");
 15     }
 16     else{
 17         printf("world\n");
 18     }
 19     printf("a=%x\n",a);
 20     
 21     return 0;
 22 }
结果:
    hello
a=fffffffd

三、c语言中数据的表现形式

1.常量

​ 程序运行过程不能被改变的数据–只读–不能做等号的左值

整型常量:123,-11
实型常量:
	十进制小数形式:3.14-4.156
	指数形式:0.314e131.4e-1
	规范化指数形式:小数点前第一位为0,小数点后第一位不为0

字符型常量:''括起来的单个字符,'a'
	转义字符 \n -换行  \t --tab
		'\n' '\t'
字符串常量: ""括起来的一串字符
	"hello world"
	
符号常量:
	#define PI 3.14 //放在程序的开头
   	#define N  100

2.变量

​ 程序运行过程能被改变的数据-保存数据

定义变量的一般形式:
<存储类型> <数据类型> 变量名;

存储类型:
    auto:自动类型
        在定义局部变量时,用auto显式声明代表该变量在栈区(先入后出的一种存储方式)随机分配一片存储空间。
        如果在定义变量时缺省存储类型,改变变量默认为auto类型
        
	register:寄存器类型
        register声明的变量,在程序运行时直接加载到寄存器上,但是计算机上寄存器有限,可能会加载失败,失败之后自动转为auto类型。
        register int a = 10;//可能失败->auto int a = 0; int a = 0;

	extern:引用类型 --该变量不能初始化,代表该变量在别处定义,要在此处使用。
        extern int a;
        1.在一个文件中扩张全局变量的作用范围。
        2.在一个源程序中扩展一个文件的全局变量的作用范围。
		
	static:静态类型
        1.修饰全局变量,限制全局变量只能在本文件中使用,该变量不能被其他文件引用。
        2.修饰局部变量,将局部变量的生命周期,延长到整个程序结束,并且该变量只会被定义一次。
	const:修饰只读
        const 修饰变量,只能读,不能写。
        该变量不能做为等号左值。
        const int a = 10;
		a = 11;//报错

数据类型:
	整型:
	char :字符型 一个字节
        unsigned:0 - 2^8 -1
        signed :-2^7 - 2^7 -1
        注意:在定义变量时,如果缺省符号,默认为有符号
    short :短整型 2个字节
       	unsigned:0 - 2^16 -1
        signed  :-2^15 - 2^15 -1
    int :整型  4个字节
    long : 长整型 8个字节(64位) 4个字节(32位)

       
    
  
	浮点型:
		float:单精度浮点型 4个字节
        精确度:6-7
        
        double:双精度浮点型 8个字节
        精确度:15-16
        
变量名:标识符
     命名规则:由字母、数字、下滑线组成
             不能以数字开头
             不能与关键字重名
	
            
 
int a  = 10;
float f = 10.1;
double d = 10.1;

char c;//如果不说明符号,默认有符号数
unsigned char c;
c = 97;
c = 'a';

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

3.变量的初始化 --定义时赋值

int a = 10;
int a,b,c,d =10;

4.变量的赋值

int a,b,c,d;
char c;
a = 10;
c = 'a';

5.全局变量 和 局部变量

  • 全局变量

    定义位置:定义在所有模块({})之外的变量
    生命周期:从定义开始,到程序结束
    作用范围:整个源文件
    没有初始化:默认为0
    
  • 局部变量

    定义位置:定义在模块({})之内的变量
    生命周期:从定开始到模块结束
    作用范围:在定义它的模块之内。
    没有初始化:随机值。
    

练习:定义两个整型变量,并交换他们值

int a = 10,b = 11;
交换后,a = 11,b = 10;

四、输入输出

  • c语言本身并不提供输入输出,所有的输入输出都是通过调用标准库函数实现的->stdio.h
  • ->#include <stdio.h>

1.标准输出函数 -printf(“格式控制字符串”,输出列表)

格式控制字符串:格式控制符 + 其他字符
格式控制符:
	%d --按十进制有符号整数格式输出
	%u --按十进制无符号整数格式输出
	%md --按十进制有符号整数格式输出,指定占m位域宽,默认右对齐.否则按照原样输出
	%-md --按十进制有符号整数格式输出,指定占m位域宽,左对齐.否则按照原样输出

	%ld --按十进制有符号整数格式输出长整型的数据
	%o --按八进制无符号整数格式输出
	%#o --按八进制无符号整数格式输出-带前缀
	%x --按十六进制无符号整数格式输出
	%#x --按十六进制无符号整数格式输出
	%p --输出一个地址
	%f  --输出单精度浮点型数据--隐含六位小数
	%.nf --输出单精度浮点型数据--指定保留两位有效数据
	%lf --输出双精度浮点型数据
	%c --输出单个字符
	%s --输出一个字符串
	%.ns --输出一个字符串的前n个字符
	%%  --输出一个%
其他字符:按照原样输出
输出列表:从左到右与控制符一一对应,每一个用,隔开

int a  =  10;
printf("%d %d %d %d",a++,++a,a++,++a);
函数在调用时,参数会从右到左依次入栈(先入后出的存储结构),++会备份,出栈时,有备份打印备份,无备份打印真值。
    
    
单纯输入空格的方式:
    1.printf(" ");//打印1个,""中间有空格
	2.printf("%*s",n,"");//打印n个空格,""中间没有空格
	3.printf("%c",32);//空格的assic码是32

真值备份
a++1413
++a13
a++1211
++a11

练习:

int a = 10;
printf("%d %d %d %d",++a,a++,++a,a++);
备份真值
++a14
a++1213
++a12
a++1011

2.标准输入函数 --scanf(“格式控制字符串”,地址列表)

scanf("格式控制符",指定对象的地址);

格式控制字符串:格式控制符 + 其他字符
格式控制符:
    %ld --输入长整型的数据
    %lf --输入双精度浮点型数据
其它字符:按照原样输入
地址列表:->&变量  ->变量的类型必须与格式控制符从左到有一一对应
    int a =0;
	char b= 0;
	float c = 0;
	scanf("%d%c%f",&a,&b,&c);
	long l = 0;
	sancf("%ld",&l);
注意1:在输入数值型数据时,空格、回车、tab都算非法字符,相当于输入结束。
注意2:在输入字符型数据时,空格、回车、tab都算是的有效字符->脏字符
注意3:在输入时,格式控制字符串后面不要加\n
 
返回值:是按照格式正确输入的个数。

3.字符输入函数–gechar()–输入单个字符

char ch = 0;
ch = getchar();
getchar();

4.字符输出函数–putchar()

char ch = 'x';
putchar(ch);
putchar(97);
putchar('x');
putchar('\n');

练习:循环在终端上输入一个字符,如果是大写,小写输出,小写就大写输出,如果是数字,改成数值数字输出

 7 #include <stdio.h>
  8 
  9 int main(int argc, char *argv[])
 10 {
 11     char a,i;
 12     puts("请输入字符:");
 13     for(i=0;a!='\n';i++)
 14     {   
 15        a=getchar();
 16        if(a>='A'&&a<='Z')
 17            putchar(a+32);
 18        else if(a>='a'&&a<='z')
 19            putchar(a-32);
 20        else if(a>='0'&&a<='9')
 21            printf("%d",a-48);
 22     }
 23     putchar('\n');
 24     
 25     return 0;
 26 }

5.字符串输入函数-gets()

gets(字符数组的首地址);
char str[20];
gets(str);
scanf("%s",str);

6.字符串输出函数-puts()

puts(字符串的首地址);
char str[20] = "hello world";
puts(str);


puts("hello world");
注意:puts在打印完字符串之后会自动换行。

五、数据类型的转换

1.自动类型转换

1.当用一种数据类型的数据,给另一种数据类型的变量赋值时会发生自动类型转换.
int a = 3.14;
int b = 'a';
2.c语言中两个整型的数据相除,结果还是一个整数
float  f = 5/2;
3.当多种数据类型的数据发生混合运算
  转换规则:数据会向精确度增加的方向转换。
  float f = 5/2 + 3.14;//f =5.14
  float f2 = 5.0/2 + 3.14;//f2 = 5.64

2.强制类型转换

(数据类型)数据;//将数据转换成()里指定的数据类型

int a = 5;
int b = 2;
float f =(float)a / b;

练习:输入两个整数,打印第一个整数除以第二个整数的值(精确到小数点后第三位)。

六、c语言三大语句结构

1.顺序语句

c语言按照顺序执行
所有的c程序都是从主函数开始从主函数结束。

2.选择语句–分支

1.单分支
if(表达式)
{
    语句块1;
}
else
{
    语句块2;
}
step:判断表达式,如果表达为真,则执行语句块1,如果表达式为则执行语句块2。
注意1:如果if或者else后面之后一条语句,{}可以省略
    int a = 0if(a > 0)
        printf("a");
	else
        printf("xxx");
注意2:else 可以没有
注意3:else 会与离他最近的if结合。

2.多分支
    if(表达式1)
    {
        语句块1;
    }
	else if(表达式2)
    {
        语句块2;
    }
	......
    else
    {
        语句块3;
    }
step:判断表达式1,如果表达式1为真,则执行语句块1;
				如果表达式1为假,则判断表达式2,
				...
                如果表达式1,2都为假,则执行语句块
3.多分支
   switch(表达式)
   {
       case 表达式1:
           语句块1;
           break;
       case 表达式2:
           语句块2;
           break;
           ....
      case 表达式n:
           语句块n;
           break;
       default:
           语句块n+1
   }
表达式:整型表达式
表达式1-n:整型的常量表达式
step:判断表达式1 - n哪一个与表达式相等,如果相等则执行表达式n后面语句块。
    如果遇到break关键字则结束switch语句,如果没有遇到break关键字则顺序执行后面的语句,直到遇到break结束switch语句.
    如果表达式1-n都不等于表达式则执行default后面的语句块。

​ 练习:写一个10个随机点名册出来

7 #include <stdio.h>
  8 #include <stdlib.h>
  9 #include <time.h>
 10 #include <unistd.h>
 11 
 12 int main(int argc, char *argv[])
 13 {
 14     srand(time(NULL));
 15     int a=rand()%10;
 16     puts("幸运儿是谁呢?......");
 17     sleep(1);
 18     switch(a)
 19     {
 20         case 0:printf("别看了,就是你,0号同学\n");break;
 21         case 1:printf("别看了,就是你,1号同学\n");break;
 22         case 2:printf("别看了,就是你,2号同学\n");break;
 23         case 3:printf("别看了,就是你,3号同学\n");break;
 24         case 4:printf("别看了,就是你,4号同学\n");break;
 25         case 5:printf("别看了,就是你,5号同学\n");break;
 26         case 6:printf("别看了,就是你,6号同学\n");break;
 27         case 7:printf("别看了,就是你,7号同学\n");break;
 28         case 8:printf("别看了,就是你,8号同学\n");break;
 29         case 9:printf("别看了,就是你,9号同学\n");break;
 30     }
 31 
 32     return 0;
 33 }

man --查询命令或者函数的使用方法(函数原型、参数说明、功能描述、返回值)
man手册分为31--命令
    2--系统调用函数
    3-- 库函数
    man 1/2/3 命令名/函数名
    
srand(time(NULL));//播下一个随机数种子,该随机数随着时间改变。头文件:#include<time.h>
int ret = rand() % 3;//限制随机数0-2
rand()//产生一个非负随机数   加头文件:include <stdlib.h>                          

3.循环语句

1.while循环--当型循环
    while(表达式)
    {
        语句块;
    }
step1:判断表达式,如果表达式为假则结束循环,如果表达式为真,则执行语句块。(非0为真,0为假)
step2:继续step1的操作。
2.do{}while()循环 --直到型循环
    do    //下面的{可以与do在一行。
    {
        语句块;
    }while(表达式);  //这一个;不能省略
step:执行语句块,判断表达式如果表达式为真,则继续执行语句块,否则结束循环。

break:
	   1.在swich语句中遇到break关键字,结束switch语句。
       2.结束当前循环。
continue:跳过本次循环,执行下一次循环。

3.for循环
   for(表达式1;表达式2;表达式3)//三个表达式之间用英文;隔开
   {
       语句块;
   }
表达式1:一般是循环变量的初始化,只会在第一次进入循环时执行一次。
表达式2:循环的结束条件
表达式3:一般是循环变量自增或自减。
step1:执行表达式1
step2:判断表达式2,如果表达式2为真,则执行语句块,执行表达式3.
step3:step2,直到表达式2为假则结束循环。

!!!在for循环的表达式1中定义变量和赋值也是可以的,只不过是for循环模块的一个局部变量,循环结束后所定义的变量就被释放了
    
4.goto --无条件跳转语句
    goto 标签;
标签:标识符
step:当程序执行过程中,遇到goto关键字,程序会无条件跳转到标签处,执行标签后的语句。
标签的设置方法->标签:
goto label;//goto 标签名;
...
lable://标签名:

注意:1.goto 不能跨函数。
	 2.goto的标签不能放在定义语句前

练习:以空格为界限裁剪一段英文,分别放到数组里面

hello world nihao shijie

hello

world

nihao

shijie

 int main(int argc, char *argv[])
 {
     char a[32]={0};
     gets(a);
     char *p[32];
     int i,j=0;
     for(i=0;a[i];i++)
     {
         if(a[i]!=' '&&i==0)//判断第一个不为空格就指向
         {
            p[j]=&a[i];
            j++;
         }
         else if(a[i]!=' '&&a[i-1]==' ')//找后面的每个单词的首字母地址,并且指向它
         {
             p[j]=&a[i];
             j++;
         }
     }
     for(i=0;a[i];i++)//改空格为'\0'
     {
         if(a[i]==' ')
         {
             a[i]='\0';
         }
     }
     for(i=0;i<j;i++)
     {
         puts(p[i]);
     }
 
     return 0;
 }
-- 插入

练习:输入一个数,统计这个数的二进制1的个数

​ 输入8–一个1

​ 输入15—4个1

 int main(int argc, char *argv[])
 {
     int num;
     scanf("%d",&num);
     int i,count=0;
     for(i=0;num;i++)
     {
         if(num&1)
         {
             count++;
         }
         num=num>>1;
     }
      printf("count=%d\n",count);

七、数组–同种数据类型数据的有序集合。

1.数组定义的一般形式

<存储类型> <数据类型> 数组名[元素个数//整形的常量表达式];

存储类型:auto register static extern  const
    
数据类型:char short int long float double 
int a[3];

数组名意义:
	1.标识符
    2.是整片数组空间的起始地址(即:首地址)
    3.代表了整片数组空间(即:地址)
    注意:数组名是一个地址常量。
     // &a[0]==a为真;整片数组空间的起始地址(即:首地址) 
     // 代表整个数组----sizeof(a)
     // 代表数组首地址---strcpy(a,b) 
     
                  
!!!注意:
     拿数组而言,地址和首地址在数值上是一样的,但是数据类型不一样。

例子eg:                 
#include <stdio.h>
int main(int argc, char *argv[])
{
    int a[6] = {1,2,3,4,5,6};
    //int (*p)[6] = a;
    //编译报警告,因为左边是一个数组指针变量,数据类型是这一行数组的地址,右边是该数组整个的地址
    //两边的数据类型部匹配   
    int (*p)[6] = &a;//&a==&a[0];
   
    printf("a=%p\n",a);
    printf("&a=%p\n",&a);
    printf("%d\n",*(*(p+1)-1));
    //取出数组指针下一行的地址,该地址再向后偏移前一个int空间地址
    //最后取出这个地址中存放的地址,即:a[5]元素
    return 0;
} 

                  
数组名意义例子:
#include <stdio.h>
int main(int argc, char *argv[])
{ 
    int arr1[6] = {3,15,2,3,10,131};
    printf("sizeof(arr1)=%d\n",sizeof(arr1));//数组名arr1可以表示整个数组

    int i = 0;
    for(i = 0;i < 6;i++)
    {
        printf("%d,",*(arr1+i)); //数组名arr1表示数组的首地址
    }

    printf("\n");
    //arr1++;   
    //编译error,因为arr1 = arr1+1中,arr1是一个地址常量,不能进行赋值操作
    return 0;
} 
                  
                  
    
[]意义:
   1.在定义变量[]代表定义是一个数组。
   2.对地址来说,偏移并引用。
       
取数组元素:数组名[下标] --下标从0开始
int  a[5];//a[0] -- a[4]
int a[5];//sizeof(a) = 20
char b[5];//sizeof(b) = 5
float c[5];
double d[5];

2.数组的初始化

  • 完全初始化

    int a[5] = {11,22,33,44,55};
    
    float b[5]  ={1.0,2.0,3.0,4.0,5.0};
    
    int a[32]={0};//定义及其清零初始化
    
  • 部分初始化

    int a[6] = {11,22,33};//给前三个元素初始化,没有初始的元素默认为0
    float f[10] = {0};
    
  • 省略初始化

    int a[] = {1,3,5,7,9};
    
    float f[];//error
    

    数组的清0:三种方式

    1.赋值清0,写一个循环

    2.局部初始化赋值为0:int a [5]={0};

    3.memset( ):可用于批量复制操作,清零

 #include <stdio.h>
 #include <string.h>
 int main(int argc, char *argv[])
 {
     auto int a[5]={1,2,3,4,5};
     memset(a,0,20);//(数组名,赋值,数组大小)
     printf("a[2]=%d\n",a[2]);
     
    return 0;
 } 
  

3.数组元素的赋值–只能单个赋值

int a[10] = {1,2,3};
int b[10];
b = a;//error

--------------
int a[5];
a[5] = {1,2,3,4,5};//error
-------------------
!!!注意:在数组定义之后,只能给一个一个的给数组每个元素赋值
int a[3] = {0};
a[0] = 11;
a[1] = 22;
a[2] = 33;

八、字符数组

​ 具有数组的普遍性,又有自己的特殊性:

​ 普遍性:定义,初始化,赋值,遍历,清0等

​ 特殊性:当我们是字符串的时候,必定会以‘\0’结尾

 #include <stdio.h>
 #include <string.h>
 int main(int argc, char *argv[])
 {
     char a[]={'h','e','l','l','o'};//普遍性
     char b[]="hello";//特殊性
     printf("size(a)=%ld\n",sizeof(a));
     printf("size(b)=%ld\n",sizeof(b));
     
     return 0;
 } 

1.字符数组初始化

  • 完全初始化

    char str[6] = {'h','e','l','l','o','\0'};
    			= {hello};//error
    printf("%s\n",str);
    
    字符串常量:""括起来的一串字符,所有的""括起来的字符串,后都有一个隐藏的'\0'
        "hello world\0"
        
    注意:在c语言当中可以用字符串常量来给字符数组初始化。
        
    char str[6] = "hello";
    			={"hello"};
    			={'h','e','l','l','0','\0'};
    
    char str[5] = "hello";
    			={'h','e','l','l','o'};//没有把字符串末尾的\0存下来。
    
  • 部分初始化

    char str[10] = {'h','e'};
                ="he";
    			={"he"};
    //未初始化的部分赋值'\0',assic码为0,本质上也是赋值0
    
  • 省略初始化

    char  str[] = "hello world";
    char  str1[] ="fdafdasfdfasdfasdfasdfadsfasdfasd";
    

2.字符数组赋值

char str[10] = {0};
str[0] = 'h';

3.字符串操作函数

  • strcpy()----复制

    头文件:#include <string.h>
    函数的原型:char *strcpy(char *dest, const char *src);
    描述:将src所指向的字符串,复制到dest所指向的数组。
    参数:dest:接收被复制字符的数组。
         src:字符串的首地址。
     char str[10];
     strcpy(str,"hello");
    返回值:返回dest的首地址
    
  • strlen()-----有效长度

    头文件:#include <string.h>
    函数的原型:size_t strlen(const char *s);
    描述:计算s所指向字符串的有效长度。不包括'\0'
    参数:s->字符串的首地址
    返回值:s所指向字符串的有效字节数。
        !!!注意:strlen 计算字符串有效长度,只要遇到'\0'就停止计算。
     
    eg:char str[32] = "hello";
       int len = strlen(str);
    
  • strcat()-----追加

    头文件:#include <string.h>
    函数的原型: char *strcat(char *dest, const char *src);
    
    描述:将src所指向的字符串拼接到dest所指向的数组后面。
    参数:src->字符串的首地址
         dest->字符数组名
    返回值:dest
    
  • strcmp()------比较

    头文件:#include <string.h>
    函数的原型:int strcmp(const char *s1, const char *s2);
    
    描述:比较两个字符串的大小,比较规则为对位比较单个字符的assic码值。只要出现第一个不一致就出结果
    参数:s1:字符串
         s2:字符串
    返回值:
    	如果s1 > s2 ,返回大于0的整数
           s1 < s2 ,返回小于0的整数
           s1 == s2 ,返回值为0
            
    eg:
    	char str[32]="hello";
    	char str1[32]={0};
    	printf("input string:");
    	gets(str1);
    	int ret = strcmp(str1,str);
    
    
  • strstr()

    头文件:#include <string.h>
    函数的原型: char *strstr(const char *haystack, const char *needle);
    
    描述:在haystack所指向的字符串中,找needle所指向的字符串第一次出现的位置
    参数:haystack ->字符串
         needle->字符串
    返回值:如果找到则返回第一次出现位置(地址),如果没有找到则返回NULL
        
    

练习:int a[5] = {11,5,12,13,2};将数组里的元素按照从大到小排序。(在一个数组中实现)

练习:输入一个字符串,将字符串中的小写字符改变为大写字符,大写字符转换为小写字符。

练习:写一个strcat()功能函数

 #include <stdio.h>
 #include <string.h> 
 char *mystrcat(char *str1,char *str2)
 {
      int len=strlen(str1);//找字符串1的‘\0’的下标
      int i;
      for(i=0;str2[i];i++)
      {
          str1[len]=str2[i];
          len++;
      }
      return str1;
 }
 
 int main(int argc, char *argv[])
 {
     char a[128]={0};
     char b[32]={0};
     gets(a);
     gets(b);
     puts(mystrcat(a,b));
 
     return 0;
 }

八、二维数组

1.如何定义一个二维数组

存储类型 数据类型 二维数组名[行号//常量表达式][列号//常量表达式];
int a[3];
int b[3][4];

二维数组名:是整个二维数组第一行的地址,代表了整片数组空间。
    long len = sizeof(b);//48

行号,列号:整型的常量。
    
[] :对地址来说,偏移并引用
    注意:二维数组名还是一个地址常量。
    
取二维数组的元素:a[行号][列号] --行号列号从0开始
    char b[3][4];
	b[0-2][0-3];

2.二维数组的初始化

  • 完全初始化

    int a[2][3] = {1,2,3,4,5,6};
    			={{1,2,3},{4,5,6}}//用{}降维赋值
    
  • 部分初始化

    int a[2][3] = {1,2,3};//给第一行的三个元素初始化,剩下的默认为0
    int a[2][3] = {{1},{2}};
    
  • 省略初始化 --行号可以省略,但是列号不能省略。

    int a[][2] = {1,2,3,4,5,6};
    

3.二维数组的引用

​ 只能单个引用

练习1,定义一个10行十列的二维数组,里面存放0 - 100之间的随机数(rand),找里面第一个最大值的下标。

九、指针–地址–内存单元的编号(一般用十六进制的整数表示)

  • 指针、地址、指针变量(存地址)。
  • 指针变量用来保存地址(指针)

1.指针变量的定义

<存储类型>  <数据类型>  * 指针变量名;

<数据类型> + * ->构成了一个新的数据类型->指针类型 //注意:定义指针变量的时候,*号是与前面的数据类型结合
    
int   a  = 10;
int *p = &a; 
printf("%p\n",p);//查看指针变量p指向的地址

char ch = 'a';
char *q = &ch;64位操作系统,所有的指针变量都占8个字节;32位是4个字节。

2.指针的运算符

&:取地址符->取变量的地址。
    int a[10];
    
[]: 1.在定义变量时代表定义的是一个数组
    2.偏移并引用
        
 *: 1.(前面+数据类型)定义变量时代表定义的是一个指针变量。
    2.(引用)地址下的内容。//指向 指针变量中存放的地址中的 具体内容,*后面跟的是地址
    
&*是互为逆运算!
   
int a  = 10;
int *p = &a;
p[0] = 123;//通过指针间接访问a所占的内存空间
printf("%d\n",a);
*p = 666;//通过指针间接访问a所占的内存空间

3.指针变量的初始化

int a = 0x11223344;
int *p =&a;

char ch = 'x';
char *q = &ch;

int a[5] = {1,2,3,4,5};
int *p = a;
注意:在给指针变量初始化时,一定要注意类型相匹配。

小端存储:数据的低位,存储到低地址。

练习1.通过指针来遍历数组。

int a[5] = {1,2,3,4,5};

练习2.通过指针来遍历数组,并且找到最大值下标。

4.指针变量的赋值

方式一:
int a = 123;
int b = 456;
int *p;
p = &a;
p = &b;

方式二:
int a = 123;
int b = 456;
int *p = &a;
int *q = &b;
p =q;

5.指针的偏移(指针变量的运算)

int a[5];
a++;//a = a + 1;error
int *p = a;
p++;

P++  ---指针向高地址偏移一个数据类型单位。
P--  ---指针向低地址偏移一个数据类型单位。
    
P + N ---指针向高地址偏移N个数据类型单位。 -N是一个整数
P - N ---针向低地址偏移N个数据类型单位
    
P + 1;//P 没有被改变
P++;//P  = P + 1;

P - Q  ---两个指针变量之间相差多少个数据类型单位。
注意:在c语言同种数据类型的指针才能相加减

练习1:编写程序,实现strlen。

作业1:编写程序,实现strcpy.

作业2:编写程序,实现strcat.

作业3:编写程序,实现strcmp.

作业4:编写程序,实现字符串的压缩。

压缩前:AAAABBBCCCDD
压缩后:A4B3C3D2

6.特殊指针

  • 空指针–NULL–代表0地址空间,不允许访问,一旦访问直接段错误。(非法访问内存)

    int *p = NULL;
    char *q = NULL;
    
  • 野指针 --在定义指针变量时没有初始化,里面保存的是随机的地址。

    int *p;
    char *q;
    int *p = NULL;
    注意:尽量避免野指针的出现,如果暂时不知道,指针指向,尽量初始化为NULL
  • 空类型的指针–!!!使用空类型指针时必须强制类型转换。

    void * p;//p为空类型的指针
    
    e.g.1:
    int a = 10;
    void *p = &a;
    *((char *)p)==*char *)p//强制类型转换
    

7.多级指针

  • 二级指针 --指向指针变量的指针。

    int a = 10;
    int *p = &a;
    
    int **q = &p;//二级指针
    int ***r = &pp;//三级指针
    

8.const修饰指针 --重点

  • const char *p; char const *p;//const在前,星号在后

    const 修饰*p,使得*p只读,即:不能通过指针(间接)修改所指向空间的内容,但是可以修改P指针变量内部指向的地址,也可以直接修改原变量的值。
    
  • char * const p;//星号在前,const在后

    const 修饰p, 使得P只读,这种类型的指针指向不能被改变,即:不能修改P指针变量内部指向的地址。但是能通过指针(间接)修改所指向空间的内容,也可以直接修改原变量的值。
    int * const p ;
    int a;
    p = &a;
    
  • const char * const p; //前两种的结合

    这种类型的指针,使得p和*p只读,既不能修改指针的指向,也不能修改指针所指向的内存空间里面的内容,只能修改原变量。
    
    

9.字符指针

char *p  = "hello world";//p保存字符串常量"hello world"的首地址。

char str[] = "hello world";//在栈区或者静态区开辟足够空间将"hello world"从常量拷贝出来。

十、数组指针—行指针

​ //本质还是指针,只是把一行看作一个整体

  • 本质上是指针,指向数组的一行。

1.数组指针定义的一般形式

<存储类型>  <数据类型> (*数组指针变量名)[N]
N:所指向的数组一行有几个元素,是一个整型的常量。

int arr[3][4];
int (*p)[4] = arr; //不能写成: int *p[4],这样成为了指针数组,*号是右结合的
p++;

&:升维
*[]: 降维
数组指针:引用所指向数组的元素。
 p[i][j]
==*(*(p + i)+j)
==*(p[i]+j)
==(*(p + i))[j]
e.g.1
char arr1[3][4];
int arr2[3][4];
int (*p)[4] = arr2;


e.g.2
int arr1[3][4];
int arr2[3][5];
int arr3[5][4];

int (*p)[4] = arr1;
			= arr3;
//用数组指针遍历二维数组

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{ 
    int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
    printf("sizeof(a)=%ld\n",sizeof(a));

    //int *p = a;  
    //会警告:类型不一样,因为左边是一个int类型的地址,右边是二维数组一行的整个地址
    //int *p = &a[0][0];  
    //消除警告可以用这个,但遍历二维数组不用这种指针,用下面数组指针
    
    int (*p)[4] = a;    //定义 数组指针

    for(int i = 0;i < 3;i++)
    {
        for(int j = 0;j < 4;j++)
        {
            printf("%2d ",*(*(p+i)+j));
            //数组指针变量
            //指向数组第i行号的数组指针
            //*(p+i):取出指向数组第i行号的数组指针的地址
            //*(p+i)+j:地址偏移到数组i行号,j列号的元素的地址
            //*(*(p+i+j):根据上面提到的地址取出a[i][j]的数值
        }
        
        printf("\n");
    }

    printf("\n");
    return 0;
} 

练习1:定义一个10行10列的二维数组,里面存放小于100随机值(rand),使用数组指针,来遍历数组的每一个元素。

练习2:定义一个2行3列的二维数组,里面存放小于100随机值(rand),分别求每一行每一列元素之和。(用数组指针实现)

十一、指针数组

  • //本质上是一个数组,里面存放的元素都是指针
  • 同种类型指针的有序集合。
<存储类型> <数据类型> *指针数组名[元素个数]
    
int a1[3] = {1,2,3};
int a2[4] = {1,3,5,7};
int a3[1] = {2,4,6,8};

int *arr[3] = {a1,a2,a3};

1.指针数组的初始化

int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};

int *p[3] = {a[0],a[1],a[2]};

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


int a = 10;
int b = 20;

int *p = &a;
int **p = &p;

int *arr[2] = {&a,&b};

int **p = arr;
注意1:指针数组名,是整个数组空间的起始地址,代表了整片数组空间
     long len = sizeof(arr);//len  = 16;
注意2:指针数组名,是一个地址常量,不能做等号左值。
    char *p1[5];
	short *p2[5];
	int *p3[5];
long len1 = sizeof(p1);
long len2 = sizeof(p2);
long len3 = sizeof(p3);

练习1.编写程序,输入一个十进制数,转换成对应的二进制数。

练习2.编写程序,实现字符串的逆序。

char str[] = "hello";
"olleh"

练习3.编写程序

char *arr[5] = {"math","Chinese","English","Physic","Bioloay"};
将数组里的字符串按照从大到小排序。 

十二、函数

  • 能够实现特定功能的代码模块的封装。

1.函数的作用

  • 提高代码的复用率,提高开发效率
  • 增加了代码的可读性
  • 增加代码的逻辑性,结构清晰
  • 便于维护和修改

2.函数的定义

<返回值类型(函数类型)>  函数名(形式参数1,形式参数2,...)//形式参数不要初始化    注意后面没有分号。
{
    函数体;
    return 返回值;
        
}
返回值类型(函数类型):char short int long float double void

函数名:
	1.标识符
        由数字、字母、下划线组成
        不能以数字开头
        不能与关键字重名。
    2.整个函数的入口地址
        
形式参数:是该函数被调用时用于接受实参的接口。是各种类型变量的定义,可以有多个,可以没有,如果多个形式参数,每一个之间用逗号隔开。

函数体:
	代码模块->函数功能的具体实现过程。

return:函数调用过程,程序执行时,遇到return关键,代表整个函数结束,后面可以跟一个返回值,作为整个函数的运行结果。
注意:如果是void类型的函数,则没有返回值。

返回值:函数只能有一个返回值,该返回值必须与函数的返回值类型对应。
    
函数的三要素:返回值、形式参数、函数体

e.g.1:封装一个函数,实现打印n行hello world.

void Print(int n)
{
    while(n)
    {
    	printf("hello world");
        n--;
    }
   return ;
}

3.函数的声明

  • 告诉编译器,该函数是在别处定义,要在后续的程序中使用。

  • 声明方式

    <返回值类型>  函数名(形式参数);//这个分号不能省略
    void Print(int n);
    //void Print(int);
    注意:在声明时形式参数可以省略变量名,但是类型不能省略
        eg:  int fun(int ,char);
    
  • 声明位置

    1.放在调用之前 --头文件之后,主函数之前。
    2.放在.h文件之中
    

4.函数的调用

在需要实现某些特定功能时去主动使用函数

  • 函数调用一般形式
函数名(实际参数1,实际参数2);
实际参数:与函数的形式参数一一对应的数据,每一个实际参数之间用逗号隔开。

!!!注意:
    如果是带返回值的函数,可以用数据类型相同的变量接受返回值;也可以直接使用返回值,甚至是参与表达式运算。
    viod类型的函数是没有返回值的:可以不用写return;也可以写return,但是后面不要加任何数据。s
    实参的个数和类型必须要与形参的个数和类型一致。
   Print(10);
  • 作为语句调用

    int n  = 0;
    scanf("%d",&n);
    Print(n);//函数单独作为一条语句使用
    
  • 作为表达式调用

    char *p = "hello world";
    int len = strlen(p);
    

5.函数的传参方式

  • 值传递

    //复制传递:只是单纯把值复制给了函数的参数,被调用函数处理的数据是复制到其形参的数据,操作不会影响原来的值

//交换了复制版的a,b;原来的a,b是不会交换的。
int main(int argc,char *argv[])
{
    int a = 666,b = 888;
    Swap(a,b);
    printf("a= %d,b = %d",a,b);
    return 0;
}
void Swap(int a,int b)
{
    int temp;
    temp = a;
    a = b;
    b =temp;
    return;
}
  • 地址传递–变量的空间地址是唯一的

    //通过给出地址操作原数据

#include <stdio.h>
  void swap1(int, int);
  void swap2(int *,int *);
  
  int main(int argc, char *argv[])
  { 
      int a = 666,b = 888;
      swap1(a,b);
      printf("a=%d----b=%d\n",a,b);
  
      swap2(&a,&b);
      printf("a=%d----b=%d\n",a,b);
  
      return 0;
  } 
  
  void swap1(int a,int b)
  {
      int temp;
      temp = a;
      a = b;
      b = temp;
  }
  
  void swap2(int *p,int *q)
  {
      int temp = *p;
      *p = *q;
      *q = temp;
  }
  //运行结果:
  a=666----b=888
  a=888----b=666

6.static修饰函数

  • 限制该函数只能在本文件中使用,其他文件不能调用该函数。
  • 同一源程序的其他文件可以定义同名的函数。

7.指针函数

  • 本质上还是一个函数,只不过返回值是一个指针(地址)。
<返回值类型>*  函数名(形式参数)//形式参数不要初始化
{
    函数体;
    return 返回值;
        
}
<返回值类型>*:char *,int *.....
    		void *:代表该函数返回值是一个空类型指针。
注意:在定义指针函数时,不能返回一个函数内部局部变量的地址。

十四、函数指针

一个指向函数的指针,函数名就表示函数的地址

十五、递归函数

递归函数:直接或间接调用函数本身
 #include <stdio.h>
 int sum=0;
 int digui(int n)
 {   
     sum += n;
     n++;
     if(n<=100)
     {
         digui(n);
     }
     return sum;
 }
 int main(int argc, char *argv[])
 {   
     int n=1;
     printf("sum=%d\n",digui(n));
 
     return 0;
 }

练习:求n!

 #include <stdio.h>
 int digui(int n)
 {
     if(n>1)
     {
       return n*digui(n-1);
     }   
 }
 int main(int argc, char *argv[])
 {
     int n=1;
     scanf("%d",&n);
     printf("sum=%d\n",digui(n));
     
     return 0;
 }  
回调函数:

以函数作为参数用函数指针接受传递

 #include <stdio.h>
 int callback()
 {   
     printf("hello world\n");
 }   
 int callback2()
 {   
     printf("zhangcheng\n");
 }
 int zhangcheng(int (*zc)())
 {   
     zc();
 }   
 int main(int argc, char *argv[])
 {
     zhangcheng(callback);
     
     return 0;
 } 

十六、结构体

可以存放任意数据类型的元素集合

struct(关键字) 结构体名{

数据类型 成员1;

数据类型 成员2;

数据类型 成员3;

。。。。。。

};//这里的分号不能省略

结构体的初始化,按照定义的结构体的成员顺序依次赋值

结构体的调用,变量名.成员

 #include <stdio.h>
 typedef struct student{
     int age;
     char name[32];
     int id;
     char sex[32];
 }STU;//结构体的定义
 int main(int argc, char *argv[])
 {   
     STU zhangcheng = {18,"zhangcheng",520,"man"};//结构体的初始化
     printf("name=%s----age=%d\n",zhangcheng.name,zhangcheng.age);//结构体的成员调return 0;
 }

练习:写一个喜欢的人的结构体,打印出来所有的属性

结构体的赋值

 #include <stdio.h>
 #include <string.h>
 typedef struct student{
     int age;
     char name[32];
     int id;
     char sex[32];
 }STU;//结构体的定义
 int main(int argc, char *argv[])
 {   
     STU zhangcheng;//结构体的初始化
     zhangcheng.age = 18;
     strcpy(zhangcheng.name,"zhancgheng");//不能直接用=
     zhangcheng.id=520;
     strcpy(zhangcheng.sex,"man");
     printf("name=%s----age=%d\n",zhangcheng.name,zhangcheng.age);//结构体的成员调return 0;
 }

结构体的嵌套

 #include <stdio.h>
 #include <string.h>
 typedef struct student{
     int age;
     char name[32];
     int id;
     char sex[32];
 }STU;//结构体的定义
 
 typedef struct teacher{
     int age;
     char name[32];
     char sex[32];
     STU zhangcheng;
 }TEA;
 int main(int argc, char *argv[])
 {
   /*  STU zhangcheng;//结构体的初始化
      zhangcheng.age = 18;
     strcpy(zhangcheng.name,"zhancgheng");
     zhangcheng.id=520;
     strcpy(zhangcheng.sex,"man");*/
      TEA zc = {20,"zc","man",{18,"zhangcheng",520,"man"}};
    // printf("name=%s----age=%d\n",zhangcheng.name,zhangcheng.age);//结构体的成员调用
     printf("name=%s----age=%d\n",zc.zhangcheng.name,zc.zhangcheng.age);//结构体的成员调用
 
     return 0;
 }
~     

结构体大小:32位系统默认字节对齐大小为4,64位的为8,字节序对齐,如果是成员最大的 大小大于等于8,按照8字节对齐,如果最大的大小小于8,按照最大的数据类型字节对齐偶数地址存储,char要放两个地址

验证方式:结构体大小必定是最大字节序倍数

结构体数组:

 #include <stdio.h>
 #include <string.h>
 typedef struct student{
     int age;
     int id;
     char sex[32];
 }STU;//结构体的定义
 
 int main(int argc, char *argv[])
 {   
     STU zc[3]={{18,520,"man"},{19,333,"man"},{20,444,"women"}};//结构体数组
    printf("age=%d\n",zc[0].age);
     
    printf("stu=%ld\n",sizeof(zc));
     
     return 0;
 } 

结构体指针:

 #include <stdio.h>
 #include <string.h>
 typedef struct student{
     int age;
     int id;
     char sex[32];
 }STU;//结构体的定义

 int main(int argc, char *argv[])
 {   
     STU zc={19,333,"man"};//结构体数组
     STU *p=&zc;
     printf("*p.id=%d\n",(*p).id);
     printf("*p.id=%d\n",p->id);
     
     return 0;
  } 

十七、联合体

共用体:union

和结构体的区别:

1、大小不一样,结构体计算每一个成员,按照字节序对齐方式存储,共用体计算最大成员的大小,按照字节序对齐方式存储

2、存储方式不同,结构体所有成员分别存储不同的空间地址,共用体共用一片空间地址,只能一个一个存储

 #include <stdio.h>
 #include <string.h>
 typedef union student{
     int age;
     int id;
     char sex[28];
 }STU;//结构体的定义
 
 int main(int argc, char *argv[])
 {   
     STU zc;
     zc.age=18;
     zc.id=520;
    // strcpy(zc.sex,"man");
     printf("age=%d\n",zc.id);
     
     return 0;
 } 

十八、枚举类型

enum:

 #include <stdio.h>
 
 int main(int argc, char *argv[])
 {
     enum week {
         monday=3,tuesday,wednesday,thursday,friday,saturday,sunday};
     printf("wesday=%d\n",wednesday);
     
    
     return 0;
 } 

十九、typedef

typedef–重命名已知的数据类型

 #include <stdio.h>
 typedef int a;
 int main(int argc, char *argv[])
 {
     a A=23;
     printf("A=%d\n",A);
     return 0;
 } 

define----宏定义 定义的是一个常量,不能够修改

 #include <stdio.h>
 #define A char *
 typedef char* a;
 int main(int argc, char *argv[])
 {
     char  b=12,c=34;
     char d=56,e=78;
     a p=&b,q=&c;
     A m=&d,n=e;
     printf("b=%d---c=%d\n",*p,*q);
     printf("d=%d----e=%d\n",*m,n);
     
     return 0;
 } 

二十、malloc函数—开辟空间

  • 在堆区申请一片空间
堆区:内存上的一片区域,是程序员手动开辟,手动释放的区域。
void *malloc(size_t size);
size:传一个整型的数据,代表申请多少个字节的空间。
    
返回值:如果申请成功,则返回一个空类型的指针,指向这片空间的起始地址。
    	失败返回NULL;

  • 释放堆区申请的空间

    void free(void *ptr);
    ptr:在堆区申请的空间的首地址。
    
    
    
         #include <stdlib.h>
         void *malloc(size_t size);
         void free(void *ptr);
         形参:size:开辟的空间地址大小
         返回值:成功返回开辟的空间的首地址,失败返回NULL
         
        #include <stdio.h>
        #include <stdlib.h>
        int main(int argc, char *argv[])
       {
           char *p = malloc(sizeof(int));
           if(NULL == p)
           {
               printf("malloc fail\n");
           }
           *p=34;
           printf("*p=%d\n",*p);
           free(p);
           p=NULL;
           return 0;
       }
    
    

练习1:输入一个整数n,定义一个可以保存n个整数的可变数组,然后输入n个整数,最后遍历该数组。

二十一、函数多文件封装

头文件:

​ 调用的库头文件和函数的声明

#ifndef _MYSTRCPY_
#define _MYSTRCPY_
#include <stdio.h>
char *mystrcpy(char *p,char *q);
#endif

功能函数:

#include "mystrcpy.h"
char *mystrcpy(char *p,char *q)
{
	int i=0;
	for(i=0;q[i];i++)
	{
		p[i]=q[i];
	}
	return p;
}

主函数:

​ 函数的调用和自定义头文件

#include "mystrcpy.h"
int main(int argc, char *argv[])
{
	char a[6]="hello";
	char b[6]="world";
	puts(mystrcpy(a,b));
	return 0;
}

编译:gcc mystrcpy.c main.c
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值