manyouren 发表于 2024-11-22 15:56

我的C语言笔记分享

# 恐秋的C语言学习笔记
》我的网名叫恐竹丶叶凌秋,简称恐秋
# EWVM注音法

》在笔记中使用了大量的拼音来作为变量和函数名(因为不懂英语),如果不给注音,dajia可以是大家又或者是打架,这样很苦恼   
》》所以我就从视力表上找到了灵感,W上M下3左E右
》》由于汉语中3无法看上去是什么音,所以我用V把他替换成了三音   
》》注音用大写拼音用小写   

》以下是使用方法   
》》niVhaoVshiMjieM你好世界   
》》zaoVshangMhaoV早上好   

》没来得及学英语,先用这个方法代替。   
# 第一个程序

》代码

```c
#include<stdio.h>

int amin()
{
    printf("hello world"\n);
    return 0;
}
```
》》#include是预处理命令   
》》》<>使用尖括号会在系统头文件所在目录中查找      
》》》""使用双引号会优先在当前文件目录中进行查找   
》》int main()      
》》》是一个固定的格式入口         
》》》int表示返回值是一个整型      
》》return         
》》》执行成功终止或返回      
》》printf("hello world"\n);   
》》》输出打印到屏幕上         
》》》\n是换行的意思操作符      

# 输出

## printf

》代码
```c
printf("%d",5);
```
》》printf分为两个部分         
》》》双引号代表控制字符串      
》》》外面的5是输出列表   
》》》》可以是变量也可以是常量      
》》》》》`printf("%d",a);`   

### 占位符
》带百分号(%)的都是占位符         
》》占位符需要输出列表填充      
》》有多少个占位符就要有多少个列表填充

》常用的占位符
》》%c字符类型
》》%d十进制的int整型      
》》%ld十进制的long类型         
》》%f浮点类型(float)   
》》%lf浮点类型(double)      
》》%s字符串类型      
》》%u十进制无符号类型
》》%p指针类型

### 格式输出

》右对齐   
```c
printf("右对齐:%5d"a);
```

》左对齐   
```c
printf("右对齐:-%5d"a);
```
》显示+号   
```c
printf("显示加号%+d"a);
```
》限定小数的位数   
```c
printf("右对齐:%.2f"12.3);
```
》》仅显示后两位   


# 关键字

## 关键字检索

### 控制语句关键字
》控制语句
》》break   
》》case   
》》continue   
》》default
》》do
》》else   
》》for   
》》goto      
》》if
》》return
》》switch
》》while   

### 数据类型关键字

》数据类型所有的关键字
》》char   
》》enum   
》》double
》》long   
》》float   
》》int   
》》short   
》》signed
》》struct
》》unsigned      
》》union   
》》void   



### 存储类型关键字

》存储类型所有的关键字
》》auto   
》》extern
》》register   
》》static



### 其他类型关键字

》其它类型的关键字
》》const   
》》sizeof
》》typedef   
》》volatile   


# 变量

》内存中的一块区域,可以在同一范围内不断地变化      
》可以通过变量名访问这块区域   

》变量的三个要素    <br>
》》数据类型    <br>
》》变量名<br>
》》存储的值    <br>

》变量声明的格式    <br>
》》`数据类型 变量名 =变量值;`<br>
》》》`int nianWlingW1=10;`    <br>   

》变量的初始化      
```c
int nianWlingW2=5;
int nianWlingW3=nianWlingW1;   
```

》变量必须初始化   <br>   
》》不初始化会出现其它软件荣过得变量没有被清理的垃圾    <br>

》变量的作用域<br>
》》定义在一对花括号里{}    <br>
》》在花括号里声明的变量只能在花括号里使用   <br>   

》按照数据类型进行分类<br>
》》基本数据类型    <br>
》》》整型<br>
》》》》短整型:short   <br>
》》》》整型:int   <br>
》》》》长整型:long    <br>
》》》》长整型:long long   <br>
》》》浮点型    <br>
》》》》单精度型:float <br>
》》》》双精度型:doublie   <br>
》》》》双精度型:long double   <br>
》》》字符型:char<br>
》》结构类型    <br>
》》》数组类型<br>
》》》结构体类型:struct    <br>
》》》共用体类型:union   <br>
》》》枚举类型:enum    <br>
》》指针类型    <br>
》》空类型:void    <br>
》》布尔类型:_Bool   <br>
》》》记住C语言里的布尔型有下划线且B是大写的    <br>
》》C语言没有字符串类型<br>   

# 基本数据类型

## 数据类型

### 数据类型的介绍

》C语言有几种类型   
》》短整型:short   
》》整型:int   
》》长整型:long   
》》超长整型:long long

》每种类型可以被signed与unsigned关键字所修饰   
》》signed表示带符号有正负号电费,这是默认的   
》》unsingned表示无符号没有负数,并且把负数都让给了正数的长度   

》后缀
》》在创建long类型的时候不加l或L就会发生隐式转换城int   
》》》代码
```c
long l1=123456;//隐式转换成int类型
long l2=12345L;//这才是long类型
```
》其他后缀    <br>
》》ll(longlong)<br>
》》u无符号 <br>
》》ul(无符号long)    <br>
》》ull(无符号long long)<br>

## 字符类型
》字符类型关键字:char<br>
》》只能存储单一的字符<br>
》》每个字符类型都占据1字节(8位)<br>
》》使用单引号括起来('')<br>
》》输出使用%c<br>
》代码<br>
```c
int main()
{
    char c1 ='a';
    printf("c1=%c",c1\n);
    printf("c1=%d",c1\n);
   
    return 0;
}
```
》》使用%d输出数字,输出的ascii码   
》》》也可以直接给char类型赋值数字,这里的数字也对应着ascii码   

### 转义字符
》转义字符
》》换行符`\n`      
》》制表符`\t`      
》》单引号`\'`
》》双引号`\"`
》》反斜杠`\\`
》》回车符`\r`


## 布尔类型

》在处理程序的时候经常有真、假、是、否、0、1这样的判断      
》》真假就是boom的两个值   
》》》true真   
》》》false假   
》》布尔的bool类型是从C99开始才有的   
》》C语言的bool类型的格式:`_Bool`
》》》代码
```c
_Bool panMduanM =true//或者1
if(panMduanM)
{
    printf("真的");
}
```
》》C99提供了一个头文件`stdbool.h`中提供了一个bool类型代替了_Boll   



# 变量的运算
》不同类型的数据类型会转换为同一类型然后再进行运算

## 隐式转换
》窄类型自动转为宽类型   
》》系统自动将字节宽度较小的类型转换为字节宽度较大的数据类型,系统自动完成转换   
》》》float小于double
》》》short和char小于int小于unsigned小于long小于double

## 强行类型转换
》宽类型赋值给窄类型
》》强行从宽转换为窄会发生警告或报错

》强制转换语法
》》代码   
```c

double a=11.2;
int b=(int)a;
```
》》强制转换会有损失,会丢失精度   


## 运算的溢出问题

》存储的数字超过了最小或最大值,就会出现溢出   
》》如果超过了最大值就会从0开始   
》》如果超过了最小值就会编程这个类型的倒数第一位      



# 常量

## 常量分类

》被定义之后不能被修改的就是常量   
》》数字是常量
》》》字面常量
》》>》代码   
```c
1.23
1000
```
》》#define标识符常量   
》》》宏定义   
》》const常量   
》》》在C99之后才出现的心愿   
》》枚举常量   

## 定义常量的方式

### #define
》开头使用#define定义常量   
》》也叫宏定义      
》》》用标识符来表示一个常量值,在代码中如果出现这个标识符,就会在变异的时候替换成常量,简称宏替换

》语法:`#define 常量名 常量值`
```c
#define LINGW 0
```


### const常量

》代码
```c
const int a =1 00;
```


### 枚举常量(eenum)

》关键字
》》`enum`

》代码
```c
enum xingMbieW
{
    nanW,
    nvV,
    baoVmiM
};
printf(%d\n,nanW);//男
printf(%d\n,nvV);//女
printf(%d\n,baoVmiM);//保密
```
》》过个枚举需要用逗号隔开
》》花括号的最后要跟着一个分号
》》枚举不需要赋值,会自动赋值


# 输入、输出函数
》输出
》》打印到显示器上文字
》》使用打印机打印文字

》输入
》》由外部硬件设备输入到计算机当中

》输入函数
》》scanf()
》》》可以接收任何类型
》》getchar()   
》》》字符输入函数,只能输入单个字符   
》》gets()
》》》输入字符串的函数

》输出函数
》》printf()   
》》》可以按指定的格式,显示任意的数据
》》putchar()   
》》》字符显示函数,只显示单个字符
》》puts()
》》》字符串输出函数   

## scanf
》从键盘上输入数据存入对应的内存中,给变量赋值

》语法`scanf(“格式控制字符串”,&参数地址列表);`
》》参数地址列表要加上取地址符:&   

》代码
```c
int a;
printf("请输入:");
scanf("%d",&a);
```


## getchar()与putchar()的使用
》putchar代码<br>
```c
char a='A';
putchar(a);
```

》getchar代码
```c
char b=getchar();
putchar(b);
```


## gets()与puts()的使用

》puts是输出一个字符串<br>

》gets是输入一个字符串(将数据输读入一个字符串当中) <br>   

》输出字符串代码<br>
```c
char st[]=["niVhaoV"];//你好
puts(str);
```

》输入字符串代码<br>
``` c
char str2;
printf("请输入");
gets(str2);
```

# 变量按生命位置的分类
》变量的生命的位置可以有两种<br>
》》局部变量<br>
》》》在函数体内
》》全局变量<br>
》》》在函数体外<br>
》》代码<br>
```c
#include<stdio.h>
int quanWjvWbianMliangM;
int main()
{
    int jvWbuMbianMliangM;

    return 0;
}
```


# 运算符与流程控制

》算数运算符   
》》加+   
》》减-   
》》乘*   
》》除/   
》》取模%   
》》自增++
》》自减--

》赋值运算符   
》》等于=   
》》加等于+=
》》见等于-=
》》乘等于*=
》》除等于/=
》》取模等于%=

》关系运算符   
》》大于>   
》》大于等于>=
》》小于<   
》》小于等于<=
》》等于==
》》不等于!=

》逻辑运算符   
》》&&
》》||
》》!   

》位运算符
》》&   
》》|   
》》^   
》》-   
》》<<
》》>>

》条件运算符   
》》()? 结果1:结果2

》sizeof运算符
》》sizeof()   


## 赋值运算符

》符号:=      
》》两侧数据类型不一致的时候,可以使用自动类型转换和强行类型转换   
》》》代码
```c   
int a =11;
double b =a;//自动类型转换

short c=(short)a;//强制类型转换
```

》支持连续赋值
》》代码   
```c
int a,b;
a=b=15;
```
》》》从右往左,10先赋给b15然后b又赋值给了a   

》扩展运算符   
》》+=      
》》-+
》》*=
》》/=
》》%=


## 关系运算符

》关系运算符    <br>
》》>大于   <br>
》》>=大于等于<br>
》》<小于   <br>
》》<=小于等于<br>
》》==等于<br>
》》!=不等于    <br>

》关系运算符的解释      <br>
》》比较的结果是bool类型<br>
》》关系运算符有两个值<br>
》》》真1   <br>
》》》假0   <br>
》》》比如`10>1`返回1,`1>10`返回0<br>


## 逻辑运算符

》符号<br>
》》&&逻辑与<br>
》》》两个条件同时满足<br>
》》》`num1>10 && num2>=20 `<br>
》》||逻辑或<br>
》》》两个其中满足一个<br>
》》》`num1>=10||num2>=20`<br>
》》!逻辑非   <br>
》》》否定的条件<br>
》》》`!(num1>=10)`<br>



## 条件运算符

》格式<br>
》》`(条件表达式)? 表达式1:表达式2`<br>
》》》判断条件表达式的真假,返回其中一个表达式<br>
》》》》代码<br>
```c
int a = 10;
int b = 20;
int c = (a>b)? a:b;
```


## sizeof

》用法:`sizeof(参数)`   
》》参数可以是数据类型的关键字,也可以是变量名后某个具体的值    <br>
》》返回某种数据类型或某个值占用的字节数量<br>

》代码
```c
int a = sizeof(int);//返回int占用的字节数的大小

float b = 12.34l
sizeof(b);//返回变量所占据的字节数大小

int c =sizeof(1234);
```



# 流程控制结构

》顺序结构<br>
》》冲上往下执行,中间没有任何的判断和跳转<br>

》分支结构
》》根据条件,悬着执行某段代码
》》有两种语句
》》》if……else
》》》switch-case   

》循环结构      
》》根据循环条件,重复性的执行某段代码      
》》有三种语句
》》》fou   
》》》while   
》》》do-while


## is-else

### if-else的语法
》基础语法代码
```
if(条件表达式)
{
    语句块;
}
```

》双分支语法
```c
if(条件表达式)
{
    语句块1;
}else{
    语句块2;
}
```
》》条件判断是否为真就执行代码,若是假就执行esle中的代码    <br>

》多重分支语法<br>
```c
if(条件表达式1)
{
    语句块1;
}else if(条件表达式2){
    语句块2;
}else if(条件表达式3){
    语句块3;
}else{
    语句块n;
}
```

》单if代码<br>
```c
int nianWlingW=16;
if(nianWlingW=>18)
{
    printf("已满18岁可以注册");
}
```

》分支if代码<br>
```c
printf("您的性别是:");
int xingMbieW =1;
if(xingMbieW==0)
{
    printf("好的先生");
}else{
    printf("好的女士");
}
```

》多重分支if代码
```c
printf("1:开始游戏 2游戏读档 3游戏设置 4退出游戏");
int xuanVzeW=scanf("%d",&xuanVzeW);
if(xuanVze=1)
{
    printf("游戏开始");
}else if(xuanVzeW=2){
    printf("游戏正在读档");
}else if(xuanVzeW=3){
    printf("游戏设置");
}else{
    printf("游戏退出");
}
```



## switch-case

### switch-case的语法

》switch的语法
```c
switch(表达式)
{
    case 常量值1:
    break;
    case 常量值2:
    break;
    case 常量值3:
    break;
    default:
    break;
}
```
》》每个case语句的结束都会紧跟着一个冒号:   <br>
》》switch如有出现break关键字就会停止,如果没有就会便利所有的case   <br>

》switch代码   
```c
int a=2;

switch(a)
{
    case 0:
      printf("0\n");
    case 1:
      printf("1\n");
    case 2:
      printf("2\n");
    case 3:
      printf("3\n");

    case 4:
      printf("4\n");
    default:
      printf("n\n");
               
}
```
》》满足条件之后就会连续的遍历出以满足条件下面的所有遍历   
》》》除非带一个break   
》》default语句是条件不满足的时候执行的
》》表达式可以是整型或字符类型
》》》不能使用浮点类型      
》》case后面的常量,与表达式等值的判断      


## for循环

》循环四要素   
》》初始化条件
》》》只执行一次   
》》循环条件   
》》》是布尔类型结果   
》》循环体
》》》反复执行的代码   
》》迭代条件   
》》》相关变量会做更新、迭代   

》for循环语法   
`fou(1初始化条件;2循环条件;4代码更新迭代){3循环体}`

》for循环代码   
```c
for(int i=1;i<=5;i++)
{
    printf("%d",i);
}
```
》》第四部分要求多个变量或其他语句同时迭代需要使用逗号不能使用分号<br>
》》》`for(int i=1;i<=5;i++,a)` <br>   


## while循环

》while循环的语法<br>
```c
1初始化部分
while(2循环条件部分)
{
    3循环体部分
    4迭代部分
}
```

》while循环代码<br>
```c
int i=1;
while(i<=5;)
{
    printf("%d",i);
    i++;
}
```


## do-while循环

》使用的较少

》do-while语法
```c
1初始化
do{
    3循环体部分
    4迭代部分
}while(2循环条件部分)
```
》》先执行一次循环体,再进行判断<br>
》》》就像是先吃饭再看钱包<br>

》do-while代码<br>
```c
int i=1;
do{
    printf("%d",i);
    i++;
}while(i<=5);
```


## 死循环

》死循环语法<br>
```c
while(1)
{

}
for(;;)
{

}
```

》代码:不知道要循环多少次退出循环<br>
```c
int a;
int zhengMshuM=0;
int fuMshuM=0;
for(;;)
{
    printf("输入0的时候结束:");
    scanf("%d",&a);
    if(a>0)
    {
      zhengMshuM++
    }else if(a<0){
      fuMshuM++;
    }else{
      break;
    }
}

```



## 嵌套循环

》循环里面可以嵌套另一个循环,可以无限嵌套<br>
》》for、while、do-while都可以作为嵌套循环<br>


》代码<br>
```c
for(int m=1;m<=9;m++)
{
    for(int n=1;n<=m;n++)
    {
      printf("%d*%d=%d",m,n,m*n);
    }
    printf("\n");
}
```
》》外层循环控制行数,内层循环控制列数<br>


## break与continue

》break可以使用在switch-case和循环结构中<br>
》》执行完就直接跳出结束<br>

》continue<br>
》》一旦执行,就跳过本次循环,继续下一次循环<br>
》》代码<br>
```c
for(int i = 1;i<=10;i++)
{
    if(i%4==0){
      continue;
    }
    printf("%d",i);
}
```


## goto关键字

》goto可以实现无条件的语句转移<br>

》goto语法<br>
`goto 标号`   <br>
》》可以随意地跳转到任意的代码语句上然后执行它<br>

》goto代码
```c
int main()
{
    int a=+;
    qiWzhiM:printf("过来%d次哦",a)
    a++;
    goto qiWzhiM;

    return 0;
}
```


# 数组

》数字是多个相同类型的数据的类型集合,通过编号来管理,并使用同一个名字命名<br>

》数组的概念<br>
》》数组名<br>
》》下表(引用)<br>
》》》下表从0开始<br>
》》元素<br>
》》数组的长度<br>

》数组的特点
》》数字的袁术在内存中是一次紧密有序排列的<br>
》》创建数组对象会在内存空间中开辟一块连续的空间,占据空间的大小,取决于元素的类型<br>
》》》比如int一个变量是4字节,double1个是8个字节<br>
》》数组一旦定义就不能再次修改大小<br>
》》数组名引用是这块连续内存空间的首地址<br>


》数组分类<br>
》》一维数组<br>
》》》存储一组数据<br>
》》二维数组<br>
》》》存储两行的数组<br>
》》多维数组<br>
》》》存储多行的数组<br>


## 一维数组

》数组在变量名右面加上方括号表示,方括号里表示的是数组的长度<br>

》调用语法<br>
`数组名[下标]`<br>
》》数组的第一位数是0<br>

》数组代码<br>
```c
#include<stdio.h>
#define NUM 10

int main()
{
    int arr;
    int arr1;

    arr=10;//第一个元素赋值为10
    arr=20;//第二个元素赋值为20
}
```


## 数组的长度

》数组的长度需要与sizeof运算符来使用<br>
》》代码<br>
```c
int arr3;
printf("数组的长度是:%zd",sizeof(arr3)/sizeof(int));
```
》》》sizeof的占位符   
》》》》`%zd`   
》》》》`%zu`   

》sizeof有自己的数据类型<br>
》》size_t来存储sizeof的返回值<br>

## 数组的遍历

》一维数组的遍历代码
```c
int arr;
int changWduM=sizeof(arr)/sizeof(int);
for(int i=0;i<changWduM;i++)
{
    arr=i;
}
```


## 数组的其它定义方式

》数组其它定义方式1
```c
int arr[]={11,22,33,44,55};
int arr={11,22,33,44,55};

int arr={11,22,33};//其余的都被设为了0

int arr={0};//全部都是0
```

》数组其它定义方式2:为指定索引位置赋值<br>
```c
int arr={=3,=7,=10};
```

## char类型数组与字符串

### char类型数组

》字符型数组用来存储字符类型的
》》字符型的数组可以用来存储字符串

### 字符串的使用

》字符串是用双引号
》》代码   
```c   
"123"
"abcd"
```

》没有专门的字符串类型的变量,字符串都是存储在char类型数组中的   
》》在字符串的结尾会用`\0`来作为字符串结束的标志   

》字符串标准的写法
```c
char str[]={'a','b','c','d','\0'};
```
》》字符串必须要用'\0'来做结尾

》字符串简化的写法
```c
str[]={"abcd"};
str[]="abcd";
```

》获得字符串的长度的函数(strlen)
》》`strlen(str)`
》》包含在string.h头文件里

# 多维数组

##二维数组的定义

》二维数组的定义语法:`int a`
》》三行四列的矩阵
》》三是行数,四是列数

》有几个中括号就是几个维度的数组    <br>

》内存上的理解<br>
》》在内存当中各元素是连续存放的,不是二维,而是线性的<br>
》》先存放的是a00>a01>a02>a03然后再存放a10>a11>a12>a13以此类推<br>

## 成员调用

》数组成员调用
```c
a= 10;
a= 20;
```

》二维数组的遍历
```c
for(int i=0;i<3;i++)
{
    for(int j=0;j <4;j++)
    {
      printf("%d",a);
    }
    printf("\n");
}
```

## 二维数组其它定义方式

》第二种定义方法<br>
```c
int a=
{{1,2,3,4}
{5,6,7,8}
{9,10,11,12}};
```

》第三种定义方法
```c
int a={1,2,3,4};
```

》第四种定义方法

```c
int a[]{1,2,3,4,5,6,7,8,9}
```



# 指针

# 指针的理解与定义

## 内存地址与指针

》小学教室楼里面的每一间教室可以理解为内存当中的一个个的数据
》》在教室的门口都有一个门牌号,每个内存它对应的位置都会有一个地址,这个地址就像等于教室门前额度门牌号<br>

》地址可以比喻为指针<br>
》》变量:命名内存空间,开辟四个字节的大小<br>
》》变量名:给内存空间取的一个容易记忆的名字<br>
》》变量值:在变量单元中存放的数据值<br>
》》变量的地址:变量所使用的内存空间的地址,既指针<br>
》》指针变量:指针变量是用来存储地址的<br>

## 指针的定义

》指针定义的格式:`数据类型 *指针变量名 = 初始地址值`<br>
》》*号是高速编译器这是一个指针变量<br>

》什么类型的指针,只能访问什么类型的变量地址<br>

》指针变量当中保存的是地址,不要将一个整数和非地址的数据赋值一个指针变量的代码<br>
```c
int *p1;
int *p2;
int *p3;

int a=10;
p1=a;
```
》》p1指针保存的是地址,而a变量保存的是数值,将p1所只想的内存数改成了10这个数值,而不是p1指向了a   

》同时创建两个指针
```c
int *p1,p2;
```
》》同时创建两个指针的时候其中一个没有加*号就会被认定为普通的变量   
》》》正确的写法   
```c
int *p1,*p2;
```

》指向指针的指针   
》》顾名思义就是用一个新的指针去指向老的指针   
》》语法   
```c
int **pp
```


# 指针的运算

》指针是一种特殊的数据类型,可以参与运算,但与其他数据类型不同,指针的运算都是针对内存中的地址来来实现的   

## 取地址运算符&

》取地址符的语法:`&变量`   
》》可以用于显示地址或是取得地址还可以修改地址
》》代码   
```c
int a = 10;
printf("a=%d",a);
printf("a=%p",&a);
```
》》%p占位符是以地址的方式打印
》》&是地址而不是变量   

》指针的赋值   
```c
int *p1;
p1=&a;
```

》指针没有赋值的时候指向的值是不可预料的,可能导致崩溃
》》可以给指针赋值为0或者是NULL   
》》》也被称之为空指针变量

## 取值运算符(* 或者叫解引用运算符)

》指针运算符的语法:`*指针表达式`

》&是获取地址,*是获取指针指向变量的值。

》实例代码1
```c
int a = 1000;
int *p;
p=&a;

printf("%p",p);
printf("%p",&a)
printf("%d",*p);
int *p1=*&p1;
```

》实例:通过指针变量修改指向的内存地址位置上的值<br>
```c
int a =10;
int *p=&a;

printf("%d",a);
*p=20;
printf("%d",a);
```

## 指针的常用运算

》指针的加减运算是移动内存地址<br>
》》每次移动都会增加或减去两个字节<br>

### 指针与整数值的加减运算

```c
#include<stdio.h>

int main()
{
    int arr={1,2,3,4,5}
    int *p=arr;
    printf("%p",p);
    printf("%d",*p);//2

    printf("%d",p+1)//4

    return 0;
}
```

》指针的加减会增加两个数或者减去两个数<br>

### 指针的自增、自减运算

》指针自增和自减
》》p++:先显示后自增<br>
》》p--:先显示后自减<br>
》》++p:先自增再显示<br>
》》--p:先自减再显示<br>


# 野指针

》野指针是只像不可知的位置<br>
》》随机<br>
》》不确定<br>
》》没有明确的限制<br>


## 野指针的问题

》指针在定以前为初始化,其值是随机的,此时操作指针就是去访问一个不确定的地址,结果不可知就被判为野指针 <br>

### 使用前未初始化

》随机的位置有可能被另外的变量所占用了   
```c
int* p1;
printf("%d\n",*p1);
*p1 =100;
```
》》p1有可能被其它程序所使用着,操作*p1是违法的   

### 数组角标越界
》代码
```c

//数组是从0开始,它的最后一位是9
int arr1 ={0};
//如果判断是小于或等于10,那么会直接让数值越过最后一位9调到第10位
for(int i=0;i<=10;i++)
{
    arr1=i;
}
```
》》指针版本   
```c
int* p
for(int i=0;i<=10;i++,p++)
{
    *p=i;
}
```

### 指针指向已释放的控件

```c
int* test()
{
    int a=10;
    return &a;
}
/**main()**/
int *p2=test();
printf("%d\n",*p2);
/*****/
```
》》当函数体执行完了之后,对应的栈区也被清除掉了<br>
》》》如果再把a赋值给p2的时候,对应的地址可能被回收了<br>
》》》因此p2指向了一个可能被回收掉的内存地址,进而是一个野指针<br>

## 避免野指针

### 指针初始化

》指针初始化的时候为指针变量赋值为NULL<br>
`int* p=NULL;`

### 指针释放设置为NULL

```c
int a = 10;
int* p=&a;

pa=NULL;
```


# 指针与数组

》取地址符号与解引用符号
```c
int* p;

int num=1;

p =&num;//得到num的地址

printf("%d的值",*p);//打印指针指向内存变量的值
```

## 一维数组与指针

### 指向一维数组的指针变量

》所谓数组元素的指针就是数组元素的地址,可以用一个指针指向一个数组元素<br>
```c
#include<stdio.h>

int main()
{
    int a={,1,2,3,4,5,6,7,8,9,10};
    int* p;
    //取首地址犹两种写法
    //第一种
    p = &a;

    //第二种将:将a0的地址给pp
    int* pp;
    pp=a;


    printf("%p",p);
    printf("%p",pp);

    return 0;
}
```

### 使用指针访问数组的元素

》如果指针变量p的初始值为&a
》》p+1和a+1就是数组元素a[i}的地址。或者说,他们指向a数组序号为i的元素<br>
》》*(p+1)或*(a+i)是p+1或a+1所指向的数组元素的值,既a的值    <br>
```c
int a={1,2,3,4,5,6,7,8,9,10};
int* p=&a;

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

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

printf("%d\n",&a);
printf("%d\n",p+2);
printf("%d\n",a+2);
```


### 指针带下表的使用

》指向数组元素的指针变量也可以带下表,如p.p被处理成*(p+i),如果p是指向一个整型数组元素a,则p,代表a。但是必须弄清楚p的当前值是什么?如果当前p指向a,则p并不代表a,而是a,即a<br>
```c
int main()
{
    int a={1,2,3,4,5};

    int* p;
    p =a+1

    printf("%d\n",*(p+2));

    return 0;
}
```
》》带角标的使用<br>
```c
/**main**/
//p =a+1
//以当前数组的头在哪里,角标就会从那个位置上加上2,所以等于4
printf("%d\n",p);
/******/
```
》》》建议使用`*(p+2)`<br>
》》》不建议使用`p`<br>


### &数组名

》代码
```c
//表示a元素的首地址
printf("%p\n",a);
//&a表示数组的地址
printf("%p\n",&a);


printf("%p\n",a+1);
//这个数组多大,那么就会跨越数组的末尾,从数组的下一个位置坐数组首地址
printf("%p\n",&a+1);

```

# 指针数组

## 数组指针与指针数组

》数组指针
》》当指针变量里存一个数组的首地址时,此指针变量成为指向数组的指针变量,简称数组指针。<br>

》指针数组
》》数组是用来存放一系列相同类型的数据,当然数组也可以用来存放指针,这种用来存放指针的数组被称为指针数组,它要求存放在数组中指针的类型必须一致。<br>


## 指针数组的使用

》指针数组的格式:`数据类型* 指针数组名[大小];`<br>

》指针数组举例代码<br>
```c
int* arr;
```

# 字符数组与字符指针变量

》字符数组是由若干个元素组成,每个元素放一个字符    <br>

》字符指针变量中存放额度是地址(字符串/字符数组的首地址),绝不是将字符串放进字符指针变量中   <br>

》对比举例
```c
//字符数组
char str[] = "hello";
//指针变量
char* pstr[] ="heloo";
```

》字符数组在定义好数组的长度之后,长度不能在修改,字符可以被修改<br>

》指针变量可以修改长度<br>


# 字符串数组的表示

》字符串可以使用一堆字符数组或字符指针变量等两种方式表示<br>

》如果一个数组的每个成员都是一个字符串,则构成了字符串数组,字符串数组有两种表示方式<br>
》》二位字符数组<br>
》》字符指针数组<br>

》二位数组的表示<br>
```c
char fruit[]={"apple","orange","grape","pear","peach"};
```

》字符指针数组表示<br>
```c
char* riMqiE={
    "星期一看电影",
    "星期二逛街小吃",
    "星期三去见Tony",
    "星期四疯狂星期四",
    "星期五黑色星期五超市大甩卖",
    "星期六在家享受",
    "星期天吃饭睡觉打豆豆"
}
```

# 指向固定长度数组的指针变量

》定义格式:`(*标识符)[一维数组元素个数];`<br>
```c
int a{
            
            {10,20},
            {30,40},
            {50,60}};

int *p =a;//将a的首地址赋值给p


//int *q = a;赋值操作错误的写法,这样就理解成指针数组了
int (*q) =a;//赋值操作正确的写法
```


# 函数

## 函数的定义

》函数的定义格式<br>
```c
返回类型 函数名(数据类型1 形参1,数据类型2 形参2,……数据类型n 形参n)<br>
{
    函数体;
}
```

》函数的简单举例1<br>
```c
int xiangEjiaE(int a,int b)
{
    int c;
    c = a + b ;

    return 0;
}
```

》没有返回值函数void<br>
```c
void daMxiaoV(int a,int b)
{
    int c;
    c=(a>=b)? a:b;
    printf("%d\n",c);
}
```
》》没有返回值可以使用void作为返回类型,这里只是作为查看并非返回给调用的函数数值<br>

》如果函数有返回值必须要搭配return的使用,什么类型的函数用return返回什么样的类型<br>

》 函数名
》》函数名,属于标识符。要遵循标识符的命名规则,同时要见名知意,以增强程序的可读性。<br>

### 参数列表

》在函数名圆括号里面的,可以生命参数的类型和参数名。表示完成函数体功能时需要外部提供的数据列表<br>

》无参列表
```c
void a()
```
》》在调用无参函数时,主函数不向被<br>

》有参列表<br>
```c
int youVcanE(int a,int b)
```

### return 语句

》return语句的作用<br>
》》结束函数执行<br>
》》将函数运算的结果返回<br>

》return语句后面不能再写其它代码了,否则会报错。<br>

》返回类型不是void时,函数体中必须确保有一个return 返回值;的语句<br>
》》返回的类型必须要和函数体类型一致<br>

》如果函数体结束的时候只写一个return旁边不写返回值,那么就跟void一样的效果直接结束了<br>


## 声明注意事项

》C语言中所有的函数都是互相独立的,函数不能在函数里定义嵌套函数   <br>

》C语言不支持函数重载<br>
```c
int add(int a,int b)
{
    return a + b;
}

int add(int a,int b,int c)
{
    return a+b+c;
}
```
》》定义的函数名不能重名<br>
》》函数重载的意义是只要是参数列表不一样就行<br>


## 函数的调用

》实参与形参<br>
》》实参是在调用函数的时候传入的参数<br>
》》形参是定义好函数括号里的参数<br>
```c
int hanWshuM(int a,int b)//这里面的a和b是形参
{

}

int main()
{
   
    hanWshuM(1,2);//这里面的1和2就是实参

    retrun 0;
}
```

》有多少个形参就要放入多少个实参,不能多不能少<br>


# 进一步认识函数

## main函数

》main函数是程序入口函数<br>

》程序必须要一个main函数<br>

》main函数不可以调用main函数<br>
》》其它函数也不能调用main函数<br>

### 带参数的main函数

》语法
```c
int mian(int argc, char *argv[]
{

})
```
》》argc全程是 argument count<br>
》》*argv[]是数组<br>
》》》相当于传进了一个字符串<br>
》》》字符串的个数记录在argc当中<br>


## exit函数

》exit函数用来终止整个程序的运行<br>

》exit函数包含在stdlib.h头文件里面<br>

》exit函数有两个参数<br>
》》EXIT_SUCCESS相当于0,表示运行成功,正常结束<br>
》》EXIT_FAILURE相当于1,表示程序异常终止<br>

》在main函数结束的时候,main函数会隐式的方式来调用exit函数<br>
》》其它函数调用exit,就是终止整个程序的运行。<br>


## 函数原型

》函数需要先声明后使用<br>

》如果函数生命在main函数的下面,那么就找不到该函数了<br>

》可以在main函数上方声明该函数的存在即可<br>
```c
//声明函数的原型
void func1();
void func2();

int main()
{
    func1();
    func2();

    return 0;
}

void func1()
{

}

void func2()
{

}
```


## 形参、实参

》形参在定义函数的时候,函数的括号里声明的变量为形参<br>
```c
int add(int a,int b)//口号里的a和b是形参
{
    return a+b;
}
```

》实参是在调用函数的时候,函数名的花括号内使用值、变量、或者表达式就是实参<br>
```c
int main()
{
    add(1,2);//这个小括号里面就是实参

    return 0;
}
```

》函数空间在用的时候才会分配空间,闲着的时候不会分配空间<br>
》》函数空间使用完之后,里面的所有数据都会被销毁<br>


## 值传递

》值传递就是将主调函数的实参值赋值给函数的形参,使得形参获得初始值,接着在函数内对形参的修改,不影响实参值 <br>
》》属于单项传递<br>

》值传递的数据类型<br>
》》整形<br>
》》浮点型<br>
》》字符型<br>
》》结构体<br>
》》共用体<br>
》》枚举类型<br>


## 地址传递

》就是把实参的地址传递给函数,让两者的地址相同

》值传递类型
》》指针<br>
》》数组<br>

》通过地址传递,在函数形参内修改,那么实参也会跟着修改<br>

》形参是双向传递<br>

》变量指针作为形参<br>
```c
void jiaoEhuanM(int *a,int*b,)
{
    int c =*a;
    *a = *b;
    *b = c;
}

int main()
{
    int a =10;
    int b =20;
    jiaoEhuanM(&a,&b);

    return 0;
}
```

》如果函数的返回类型是指针,但是返回的是普通变量,是错误的,因为地址被释放了。
```c
int* f()
{
    int i;

    return &i;
}
```

### 数组最为参数

》除了数组指针,针对数组也是有效的,也叫做地址传递
```c
ceMshiM(int shuMzuV[],int n)
{
    for(int i=0; i<n;i++)
    {
      arr += 1;
    }
}


int main()
{
    int arr={1,2,3};
    printf("%d,%d,%d",arr,arr,arr)
    ceMshiM(arr,3);
    printf("%d,%d,%d",arr,arr,arr)

    return 0;
}
```
》》也可以用指针形参代替数组形参的
```c
ceMshiM(int* shuMzuV,int n)
{
    for(int i=0; i<n;i++)
    {
      arr += 1;
    }
}

/***main***/
ceMshiM(arr,3);
/******
```

》实参与形参的对应关系有四种   
》》形参与实参都用数组名   
```c
int f(intx[],int n)
{
    ……
}

int main()
{
    int a;
    ……
    f(a,10);
    ……
}
```
》》实参形参都用指针   
```c

int f(int *x,int n)
{
    ……
}

int main()
{
    int a;
    ……
    f(a,10);
    ……
}
```
》》实参用数组名,形参用指针变量   
```c

int f(int *x,int n)
{
    ……
}

int main()
{
    int a;
    int* p;
    *p=a;
    ……
    f(p,10);
    ……
}
```
》》实参为指针变量,形参为数组名   
```c
int f(int x[],int n)
{
    ……
}

int main()
{
    int a;
    int* p;
    *p=a;
    ……
    f(p,10);
    ……
}
```


## 指针函数

》c语言允许函数的返回值是一个指针(地址),这样的函数被称之为指针函数。   
```c
int* biVdaMxiaoV(int *c,int *d)
{
    int e;
    if(c>d)
    {
      printf("a:%d",a);
      return &a;
    }else{
      printf("b:%d",b);
      return &2;
    }
}
int main()
{
    int a;
    int b;
    scanf("%d",&a);
    scanf("%d",&b);
    biVdaMxiaoV(&a,&b);
}
```

》如果返回的是局部变量,因为是返回的是地址,局部变量已经被销毁,所以返回就会是错误的   
```c

int* fanVhuiW()
{
    int b =10;

    return &b;
}

int main()
{
    int* a=fanVhuiW();

    return 0;
}
```
》》b是个局部变量,在执函数执行完毕之后就会释放销毁函数里面的所有东西,用指针来获取局部变量的地址就会获得不好的东西   

》指针获取局部变量不被销毁的方法就是让局部变量使用static关键字,从而放进堆区   
```c
int* fanVhuiW()
{
    static int b =10;

    return &b;
}

int main()
{
    int* a=fanVhuiW();

    return 0;
}
```
》》堆区的内存会随着整个程序被关闭才会被销毁            


## 函数指针

》函数指针就是指向函数的指针   

》函数指针的声明格式   
》》`返回类型(*指针变量名)(参数列表);`
```c
void daVyinM(int b)
{
    printf("%d\n",b);
}

int liangVgeM(int a,int b)
{
    return
}

int main()
{
    void(*daVyinMchuElaiW)(int);
    daVyinMchuElaiW = &daVyinM;

    (*daVyinMchuElaiW)(10);

    int(*erMgeM)(int,int);
    erMgeM=&laingVgeM;
    int sheiWdaM=(*erMgeM)(20,30);

    return 0;
}
```

》C语言规定,函数名本身就是指向函数代码的指针,通过函数名就能获取函数地址。也就是说daVyinM和&daVyinM是一回事   
》》因此daVyinMchuElaiW就像等同于daVyinM   
```c
void (*daVyinMchuElaiW)(int) =&daVyinM;

void (*daVyinMchuElaiW)(int)=daVyinM;

if(*daVyinMchuElaiW == daVyinM);
```

## 回调函数

》指向函数a的指针变量的一个重要用途是吧函数a的入口地址地址作为参数传递到其它函数函数B里去,此时函数B就称为回调函数。   
```c
viod huiWdiaoMhaiWshuM(int(*x1)(int),int(*x2)(int,int))
{
    int a,b,i=10,j=20;
    a=(*x1)(i);
    b=(*x2)(i,j);


}
```

## 函数的说明符

》在声明函数的前面可以加入static或者extern关键字   

》定义函数指向在本源文件中使用可以直接使用static关键字
》》`static int hanWshuM(int a,int b){}`   

》让函数在其它源文件里也能使用就要加入extern关键之了   
》》`extern int hanWshuM(int a,int b){}`   
》》外部函数要避开重名的函数   
》》外部函数在本源文件中使用要做函数原型   
》》》`extern int hanWshuM(int a,int b)`   

# 其它修饰符

## 寄存器变量(register)

》register是用来把变量存储在寄存器当中
`register int f`

## extern

》extern是定义外部全局变量
`extern int a,b,c;`

》将全局变量的作用于扩展到其它文件


## const

》表示为常量   
`const int a =10;`

》定义好后就不能再次修改值

》指向常量的指针   
```c
int a=10;
const int *p = &a;
int b=20;
p =&b;
```
》》值不可以改,但是可以再次指向其它地址   

》常量指针
```c
int a=10;
int b=20;
int *const p =&a;
//无法再指向其它的变量地址了
p =&b;
```
》》可以修改值,但不可以修改指向的变量


》同时用const修饰指向和变量   
```c
int a = 10;
int b = 20;
const int * const p =&a;

//错误,无法修改值
*p=5;

//错误,无法修改指针的指向
p=&b;
```

# 结构体

》结构体,可以在它的内部来定义多个不同类型的变量作为其成员

》结构体用`struct`关键字来定义

## 结构体的定义

》结构体的定义语法
```c
struct xueWshengE
{
    int id;
    char name;
    char gender;
    int age;
    char address;
};
```
》》在结构体结束的地方要加上一个分号   

》声明结构体变量   
```c
int main()
{
    struct xueWshengE sx1;
    xs1.id =001;
    strcpy(xs1.name,"张三");
    xs1.gender ='男';
    xs1.age =13;
    strcpy(xs1.address,"地球村中国");

    return 0;
}
```
》》char字符串类型不能直接更改字符串内容,要使用strcpy函数进行修改才行

》结构体的其他写法1   
```c
struct xinMxiE
{
    char mingWziM;
    int nianWlingW;
    char xingMbieW;
};

int main{}
{
    struct xinMxiE xinxi1={"小明",12,'男'};

    return 0;
}
```
》》如果大口号内容没有填完,就会使用默认值

》结构体的其他写法2   
```c
/***main***/
struct xinxi2={.xingMmingW="张三".nianWlingW=13,.xingMbiW='男'};
/******/
```

》结构体的其他写法3   
```c
struct diMsanEgeM
{
    char *mingWziM;
    int nianWlingW;
}a,b;
```
》》声明结构体的同时创建结构体变量

》不指定类型名而直接定义结构体类型变量(匿名结构体)   
```c
struct
{
    char *mingWziM;
    int nianWlingW;
}a,b;
```
》》在创建结构体的时候就存在,在之后无法声明创建同样的结构体变量   

》使用typedef关键字为结构体起别名   
```c
typedef struct xueEshengE
{
    char *mingWziM;
    int nianWlingW;
}xs;


/***main()***/
xs xuesheng1;
/******/
```
》》起别名是不会有默认值的

## 结构体嵌套

》结构体也可以是成员的一部分   
```c

struct xingMmingW
{
    char xingMshaW;
    char jiaoMshenWmeE;
    int xiVhuanEdeEshuMziM;

};

struct xueWshengE
{
    int nianWlingW;
    struct xingMmingW xm;
    char gender;
}xs1;

int main()
{
    strcpy(xs.xingMmingW.xingMshaW,"张");
    strcpy(xs.xingMmingW.jiaoMshenWmeE,"三");
    xs.xingMmingW.xiVhuanEdeEshuMziM=3;
    struct xueWshengE xs2 = {.xm={.xiVhuanEdeEshuMziM=7}};
    return 0;
}
```

## 结构体占用空间

》C语言占用空间都必须是int类型得到存储空间的整数倍。如果int类型的存储是4字节,那么struct类型的存储空间就总是4的倍数   
》》如果占据的比int类型小就按照4个字节来存储   


## 结构体赋值操作

》结构体赋值操作的时候会生成一个权限的副本,系统会分配一块新的内存空间,大小与原来的变量相同,把每个属性都复制过去,即原样生成一份数据。      


# 结构体数组

》结构体数组,就是数组元素是结构体变量而构成的数组
```c
struct xueEshengE
{
    char xingMmingW;
    int nianWmiingliW;
};

int main()
{
    struct xueWshengE xs1={{.xingMmingW="张三",nianWlingW=13},{.xingWmingW="李四",14},{.xingMmingW="王五",15}};
    struct xueWshengE xs2[]={{.xingMmingW="张三",nianWlingW=13},{.xingWmingW="李四",14},{.xingMmingW="王五",15}};
    struct xueWshengE xs3={"张三",13,"李四",14,"王五",15};
}
```
》》调用   
```c
/***main***/
printf("%s",xs1.xingMmingW);
/******/
```

# 结构体指针

》结构体指针是指向结构体变量的指针

》应用场景
》》可以指向单一的结构体变量   
》》可以用作函数的参数
》》可以指向结构体数组

》结构体指针变量格式   
`struct 结构体名 *结构体指针变量名`

》代码
```c
struct xueWshengE
{
    char xingMmingW;
    int nianWlingW;

}

int main()
{
    struct xueWshengE xs1={.xingMmingW="张三",.nianWlingW=13};
    struct xueWshengE *xsp1 =&xs1;
    printf("%s\n",(*xsp1).xingMmingW)
    (xsp1).nianWlingW =13;
   printf("%d\n",(*xsp1).nianWlingW)

    return 0;
}
```

## 结构体传参

》结构体属于值传递

》结构体指针属于地址传递
```c

struct xueEshengE
{
    char *xingMmingW;
    int nianWlingW;
    char *mingWziM;
};

void zengEjiaEnianWlingW(struct xueWshengE meiVgeM)
{
    (*meiVgeM)meiVgeM.nianWlingW =(*meiVgeM).nianWLingW + 1;
}

int main()
{
    struct xueWshengE xs1={"张三",13,"芜湖"};
    zengEjiaEnianWlingW(&xs1);
    printf("年龄",xs1.nianWlingW);   
   
    return 0;
}
```

## 指针操作符(->)

》在没有使用率指针操作之前,使用的都是`(*p).age`这样的写法,很麻烦,使用了`->`操作符就容易了   

```c
struct xueWshengE
{
    char *xingMmingW;
    int nianWLingW;
    char *diMzhiV;
}

void jiaEnianWlingW(struct xueWshengE *xs)
{
    xueWshengE->nianWlingW =xs-nianWlingW + 1;
}

int main()
{
    struct xueWshengE xs1 ={"张三",20,"芜湖"};
    jiaEnianWlingW(&xs1);
    printf("年龄%d\n",xs1.nianWlingW);

    return 0;
}
```

## 指向结构体数组的指针

》代码
```c
struct xuWshengE
{
    int id;
    char name;

};

int main()
{
    struct xueWshengE xs1;
    struct xssz1;

    structxueWshengE *p,*q;

    p = &xs1;
    q = &xssz1;
}
```
》》数组本身就是地址可以不加取地址符`&`   


# 结构体在数据结构中的应用

## 声明节点的结构体

》链表是一种动态的数据存储结构(非固定长度),链表的基本单位是节点(node),同意链表的所有节点具有相同的数据类型。而节点使用结构体类型进行定义。   

》一个链表节点包含数据域和指针域两个部分   
》》数据存储需要的存储数据
》》指针域或存储下一个节点的位置   

》声明单项链表的代码   
```c
struct jieWdianV
{
    int shuMjvM;
    struct jiWdianM *xiaMyiEgeM;
};

int main()
{
    struct jieWdianV jd1;

    return 0;
}
```
》》方式2
```c
typedef struct jieWdianV
{
    int shuMjvM;
    struct jiWdianM *xiaMyiEgeM;
}_jiWdian;

int main()
{
    _jieWdianV jd

    return 0;
}
```
》》声明二叉树节点
```c
struct erMchaMshuMjieWdianV
{
    int shuMjvM;
    struct erMchaMshuMjieWdianV *zuoVziVshuM;
    struct erMchaMshuMjieWdianV *youMziVshuM;
};

int main()
{
    struct erMchaMshuMjieWdianV ecsjd;

    return 0;
}
```
》》带typedef的版本
```c
typedef struct erMchaMshuMjieWdianV
{
    int shuMjvM;
    struct erMchaMshuMjieWdianV *zuoVziVshuM;
    struct erMchaMshuMjieWdianV *youMziVshuM;
}_erMchaMshuMjieWdianV;

int main()
{
    _erMchaMshuMjieWdianV ecsjd;
   
    return 0;
}
```

## 声明节点变量

》动态分配内存空间
```c

typedef struct erMchaMshuMjieWdianV
{
    int shuMjvM;
    struct erMchaMshuMjieWdianV zuoVziVshuM;
    struct erMchaMshuMjieWdianV youMziVshuM;
}_erMchaMshuMjieWdianV;

int main()
{
    _erMchaMshuMjieWdianV *ecsjdp;
    ecsjdp = (_erMchaMshuMjieWdianV)malloc(sizeof(_erMchaMshuMjieWdianV))

    return 0;
}
```
》》使用malloc函数创建的都是在在堆空间中创建的
》》》虽然复杂,但是灵活   
》》使用malloc开辟数据结构的数据是可以更改的,普通的是固定的   
》》取内部元素的方法不一样
》》》普通的   
```c
typedef struct erMchaMshuMjieWdianV
{
    int shuMjvM;
    struct erMchaMshuMjieWdianV zuoVziVshuM;
    struct erMchaMshuMjieWdianV youMziVshuM;
}_erMchaMshuMjieWdianV;

int main()
{
    _erMchaMshuMjieWdianV ecsjd;

    ecsjd.shuMjvM;
   
    return 0;
}
```
》》》malloc()函数灵活的   
```c
typedef struct erMchaMshuMjieWdianV
{
    int shuMjvM;
    struct erMchaMshuMjieWdianV zuoVziVshuM;
    struct erMchaMshuMjieWdianV youMziVshuM;
}_erMchaMshuMjieWdianV;

int main()
{
    _erMchaMshuMjieWdianV *ecsjdp;
    ecsjdp = (_erMchaMshuMjieWdianV)malloc(sizeof(_erMchaMshuMjieWdianV))
    ecsjdp->shuMjvM;
    return 0;
}

```
》》》》ecsjdp在创建的时候是个指针(*esjdp)   

》释放创建在堆区里的数据free()函数
```c
free(esjdp);
```

# typedef

》c语言允许为一个数据类型起一个别名

》起别名的目的不是为了提高程序运行效率,而是为了编码方便   

## typedef使用的方法

》typedef语法:`typedef 类型名 别名;`   

》给基本类型起别名
```c
typedef int zhengVxingW;

/***man***/
zhengVxingW a=10,b=20;
/******/
```

》为结构体起别名
```c
struct xueWshengE
{
    char xingMmingW;
    int nianWlingW;
};

/***main***/
typedef struct xueWshengE xuesheng;
xuesheng xs1;
/******/
```

》第二种写法   
```c
typedef struct chongVwuMgouV
{

}xiaoVgouV;

/***main***/
xiaoVgouV xg;
/******/
```

》匿名结构体   
```c
typedef struct
{

}xiaoVgouV;

/***main***/
xiaoVgouV xg;
/******/

```

》为指针起别名      
```c
typedef int* zhengVxingWzhiVzhenE

/***main***/
zhengVxingWzhiVzhenE p;
/******/
```

》给数组类型起别名
```c
typedef int shuMzuVwuV;//数组大小为5

/******/
shuMzuVwuV sz ={1,2,3,4,5};
/******/
```

》指针数组
```c
typedef int* zhiVzhenEshuMzuV;
/***main***/
int a=10,b=20;
zhiVzhenEshuMzuV = {&a,&b};
/******/
```

》给函数指针起别名
```c
int zuiMdaM(int a,int b)
{
    int c=(a>b)? a:b;

    return c;
}

typedef int (hanWshuMzhiVzhenE)(int,int);

/***mian***/
hanWshuMzhiVzhenE hszz = &zuiMdaM;
int jieWguoV = (*hszz)(12,34);
/******/
```

# C语言的常用函数

## 字符串常用函数

》关于字符串的处理函数,都属于库函数   
》》库函数跟编译器有关系,不同的编译器或许有不同的库函数   

》字符串处理函数大多都在string.h头文件中   

### strlen()

》返回字符串中的长度,不包括结尾的\0   
```c
#include <string>

int main()
{
    char shuMzuV[]="123456789";
    printf("%d\n",strlen(shuMzuV));

    return 0;
}
```

### strcpy()
》strcpy包含在string.h头文件中      

》字符数组一旦被复制就不能被修改   

》strcpy函数可以让字符数组B直接复制字符数组A中的所有内容   
```c
#include<string.h>

int main()
{
    char a="123456789";
    char b[]="abcdefg";
    printf("%s\n",a);
    strcpy(a,b);
   printf("%s\n",a);


    return 0;
}
```
》strcpy也可以更改字符串数组中的内容   
```c
char a="no";
strcpu(a,"yes");

```

### strncpy()

》将B字符串中的前n个字符给A字符串   
```c
char a="01234567891"
char b="abcdefghijk"
strncpy(a,b,3);

```

### strcat()

》函数原型
`char* strcat(char* a,char* b);`   

》strcat是连接的意思,是将两个字符串连接在一起作为结果返回给左边的字符串   
```c
char a={"qazwsx"};
char b[]={"edcrfv"};
strcat(a,b)
printf("%s\n"),a;

printf()
```
》》要求前面的字符串要大   
》》也可以作为printf的粘合剂,粘合的地方去除\0只在最后添加\0   
```c
printf(strcat(a,b));
```
### strncat()

》将B字符串前N个字符存放在A字符串的结尾   
```c
char a="abcd";
char b="efgh0000";
strncat(a,b,4);
```

### strcmp

》用来比较两个字符串   
》》两个字符串相同,救认为相等,返回值为0   
》》如果不相等,就会返回值为正数,则字符串1大,反之,返回值为负数,则字符串2大

》函数原型`int strcmp(const char* a,const char* b);`      

代码
```c
char* a="abc";
char* b="xyz";

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

int duiMbiV=strcmp("yes","no");
printf("%d",duiMbiV);
```

### strlwr()与strupr()

》strlwr是将大写的字母换成小写的   

》struprs是将小写的字母换成大写的   


### 基础数据类型转换成字符串(sprintf)

》该函数放在stdio.h头文件中   

代码   
```c
int main()
{
    char str1;
    char str2;
    char str3;
    int a=123,b=233;
    char c=’a‘;
    double 233.33;

    sprintf(str1,"%d %d",a,b);
    sprintf(str2,"%d %c",a,c);
    sprintf(str3,"%.5",d;

    printf("str1%s\n",str1);
    printf("str2%s\n",str2);
    printf("str3%s\n",str3);

    return 0;
}
```

将字符串转换为基本数据类型(atoi和atof)   

》这两个函数都存存放在了stdlib.h头文件当中

》代码
```c
char str1[]="123456";
char str2[]="111";
char str3[]="12.34";

int a =atoi(str1);
int b =atoi(str2);
short c =atoi(str2);
double d =datof(std3);

```

## 内存动态分配函数

》内存动态分配函数有两个   
》》malloc与free   
》》他们俩都在stdlib.h头文件里面   


### malloc()

》包含在stdlib.h头文件中

》函数原型
```c
void *malloc(unsigned int size)
```
》》因为这是个指针函数

》在内存的动态存储区(堆区)中分配一个长度为size的连续空间。并将空间额度首地址作为函数返回值,既此函数是一个指针函数   

》有的时候返回的有可能是空的(NULL),原因是在内存堆空间里找不到适合的空间所以失败   

》malloc函数的使用代码
```c
<stdlib.h>

int main()
{
    int pty_int = (int*)malloc(sizeof(int));

*ptr-int = 10;
printf("%d\n",pty_int);

    return 0;
}
```
》》给int类型的数组分配空间   
```c
/***main()***/
int shuMzuV =10;
int* shuMzuVp1;
shuMzuVp(int)malloc(shuMzuV* sizeof(int));

for(int i,i<shuMzuVli++)
{
    shuMzhuV=i;
}
/******/
```
》》为结构体变量分配空间   
```c
struct jieWdianV
{
    int shuMjvM;
    struct jieWdianV* xiaMyiEgeM
};
/***main()***/
struct jiWdianV* _jieWdianVp;
_jieWdianVp=(struct jieWdianV*)malloc(sizeof(struct jieWdianV));
/******/
```
》》》指向结构体的指针曲成员   
```c
_jieWdianVp->shuMjvM;
struct jieWdianV a=(*struct jieWdianV).shuMjvM
```

### 释放内存空间(free)

》判断有没有成功分配动态内存如果没有就释放
```c
if(_jieWdianVp!=NULL)
{
    free(_jieWdianVp);
}else{

}
```
》》如果没有在程序最后执行free函数市分行动态飞陪的内存,则在当前函数调用后结束,次内存没有被释放,存在内存泄漏的风险   
```c
/***mian()***/
free(_jieWdianVp);
//return 0;
/******/
```

### calloc()
》在内存的动态存储区(堆区)中分配N个,单位长度为size的连续空间,这个空间一般比较大,总共占据n*size个字节。
》》并将改地址空间的首地址作为函数的返回值。如果函数没有成功执行,返回NULL。   

》函数原型:`void *calloc(unsigned int n,unsigned int size);`   

》用malloc开辟的数组是没有被初始化的   

》代码
```c
int *p;
p=(int*)calloc(10 * sizeof(int));
memeset(p,0,sizeof(int) * 10);
```
》》开辟空间同时初始化的版本   
```c
p=(int*)calloc(10,sizeof(int));
```

### realloc()

》realloc是作为充分分配malloc或calloc函数获得动态空间大小,既调整大小的动态内存空间。   
》》将先前开辟的内存的指针p指向的动态空间大小改为size,单位字节。   
》》返回值是一个全新的地址(数据也会自动赋值过去),也可能返回跟原来一样的地址。分配失败返回NULL。

》realloc函数有限在原有内存块上进行缩减,尽量不移动数据,所以通常是返回原先的地址   

》如果新内存块,小于原来得到大小,则丢弃超出的部分
》》如果大于原来的大小,则不会对新增的部分进行初始化。

》函数原型:`void *realloc(void* p unsigned int size);`

》代码
```C
int* b;
b = (int*)malloc(sizeof(int * 10));
b = (int*)realloc(b,sizeof(int)*2000;
```
》》指针b原来指向10个成员整形数组,使用realloc调整为2000个成员数组

Johnnny 发表于 2024-11-22 19:26

很有用,收藏了

pjkey 发表于 2024-11-22 19:44

哇哇,非常漂亮的笔记

zakang 发表于 2024-11-22 20:32

看完了,代码写完了,给一个工作吧{:1_918:}

D22832 发表于 2024-11-22 20:46

非常好用的笔记谢谢分享

wdj500 发表于 2024-11-22 20:56

要学多久才能学会

naisitu 发表于 2024-11-22 21:31

最近在业余时间也在学习C语言中,感谢楼主的笔记,共同进步,哈哈!
弱弱的问下:楼主是学生还是自学的呢?看笔记这架势估摸着是大佬一枚!:lol

anorith 发表于 2024-11-22 21:34

第一个就看到amin哈哈哈

yycvip 发表于 2024-11-22 21:36

太牛了{:1_921:}
现在肯定是大神了

asmtodeath 发表于 2024-11-22 23:58

感谢分享!再来一份C++和VC++的笔记吧!
页: [1] 2
查看完整版本: 我的C语言笔记分享