c语言
递归函数
就是一个函数调用了函数本身
- 要有一个明显结束的条件
- 要有一个结束条件的趋势
常用系统函数
字符串函数
标准库头文件<string.h>
strlen(str) 返回一个数组的长度(有元素的长度 字符数组中的结束标识符不算) 接收类型是 size_t
strcpy(str,str1) 将str1的字符串赋值给str中要注意数组长度 本质就是一个字符一个字符的替换
因为str它是一个数组名(常量标识符不可以直接改变它的值,可以通过下标一个元素一个元素的改)
strcat(str,str1) 将str1的字符串加在str后面
标准库头文件<stdoio.h>
sprintf(),用于将格式化数据写入字符串。相比于 printf(),多了一个参数,第一个参数是要写入的字符串,后面参数与 printf() 一致。简单地讲,sprintf() 是将内容写入字符串而不是输出。
sscanf(),用于从一个字符串中按照指定的格式提取数据。相比于 scanf(),多了一个参数,第一个参数是要提取数据的字符串,后面参数与 scanf() 一致。简单地讲,sscanf() 是从字符串中提取数据而不是从用户输入提取数据。
日期时间函数
标准库头文件 <time.t>
time(&变量) 获取当前日期赋值到变量中,改变量要是size_t类型 时间戳
ctime(&时间值) 将时间戳转换为字符串并返回
difftime(时间值1,时间值2)返回两个时间值的差,后面减前面,返回值是double类型
指针
指针加减
指针+n 地址向后移动n个指向对象的数据类型大写
指针+n 地址向前移动n个指向对象的数据类型大写
同类型指针相减
相差的元素个数 (相差的字节除数据类型)
指针和数组
指针数组
本质上是一个数组
数组里的元素都是指针
定义方式
数据类型 *数组名[长度]
数组指针
本质是一个指针
指向一整个数组,寻常数组是指向数组的第一个元素
数组指针是指向的整个数组
定义方式
数据类型 (*数组名)[长度] = &数组名
数据类型 | sizeof 计算结果 | +1 的值 | * 取值(解引用) | 能否重新赋值(可变性) | |
---|---|---|---|---|---|
数组名 | 元素类型[] | 整个数组的存储长度 | 下一个元素地址 | 首元素的值 | 否 |
指向首元素指针 | 元素类型* | 地址的存储长度 | 下一个元素地址 | 首元素的值 | 是 |
数组指针 | 元素类型 (*)[] | 地址的存储长度 | 越界(数组后面) | 整个数组 | 是 |
指针和函数
指针函数
1. 指针函数是个函数,返回值是指针或地址
2. 指针函数的返回值不要指向局部变量(函数调用结束局部变量就销毁),可以指向静态局部变量。
定义方式
int *函数名() int表示的是指针类型
函数指针
1. 函数名
函数名特定情况下可以视为指向函数代码区域指针
函数名、*函数名、&函数名 三者等价
函数名的类型: 返回类型 (参数类型,参数类型,参数类型),如 double (int,int)
2. 函数指针
是个指针,指向一个函数
通过函数指针来调用函数, 函数指针名 和 *函数指针名 等价
定义方式
int (*指针名)() int表示的是函数类型
野指针
1.指针未初始化
2.指针越界
3.指针指向被销毁的数据
空指针
让一个指针指向空,未初始化时可以让先指向空,当函数里需要传指针时,也可以传空指针 NULL
枚举类型
枚举类型是我们自定义的一种数据类型
里面的成员是一对常量列表 给0123这些常量取一个名字提升可读性
定义方式
enum 类型名
{
常量列表
}
D:\study\Code\chapter13
结构体类型
结构体类型的定义
struct 类型名称
{
成员类型 成员名称1;
成员类型 成名名称2;
、、、
}
根据结构体类型定义结构体变量
1.先定义结构体 ,后在定义变量
struct Weel
{
int a;
double b;
}
- 同时定义结构体类型及变量
struct weel
{
int a;
double b;
char c;
}shar = {1,2.0,'a'};
3.同时定义结构体类型及变量省略变量名,后面无法再定义这个类型找不到
struct
{
int a;
double b;
char c;
}shar = {1,2.0,'a'};
可以定义的时候就初始化,也可以后面再初始化
怎么访问成员
- 在大括号一次给结构体赋值是 用 . 成员变量名
- 一一 赋值的时候 用 结构体变量名 . 成员变量名
结构体指针
访问成员
使用 指针名 ->成员名 与 变量名 . 成员名 是一模一样的
结构体变量的存储大小
类型对齐适用于所有变量 变量的地址一定是类型长度的整数倍
1.结构体整体对齐 : 结构体的存储大小是基准长度(在没有特别指定时是自接最大的成员的长度)的整数倍
2.结构体成员对齐 : 结构体里面的成员的地址相对于结构体的地址来说肯定是自身的整数倍
struct Weel
{
int a; //占8个位置 相对与结构体的整数倍,因为下面的double要对齐所有补4个
doubel b; //地址长度要是自类型的整数倍,所有会上上面的int补4个
char c; //cahr放哪里都行 但是结构体需要时最大类型的整数被所有会补7个
}aa; //aa这个结构体变量的地址一般情况就是double的整数倍
1)结构体变量所占内存长度,可以认为是各成员占的内存长度的叠加;每个成员分别占有其自己的内存单元。
D:\SGG Learning documents\演示图例\演示图例\结构体存储
共用体
共用体的概率
共用体变量所占的内存长度等于最长的成员的长度;几个成员共用一个内存区。
共用体的定义
union 类型名
{
类型 成员名称;
、、、
}
-
定义共同体变量
① 先定义共用体类型,再定义共用体变量
② 同时定义共用体类型和共用体变量
③ 同时定义共用体类型和枚举变量,并省略共用体类型的名称 -
共用体变量的初始化
① 先声明变量,再给成员初始化赋值
② 同时声明变量并给第一个成员初始化赋值
union 类型 变量名 = {第一个成员的值}
② 同时声明变量并给指定成员初始化赋值
union 类型 变量名 = -
访问共用体变量的成员
. 运算符
同一时间只会有一个成员有意义,最后定义哪一个
经过验证后面的成员在赋值是冲后面的字节开始占
D:\study\Code\chapter010\main01.c
使用也是 变量名.成员名 指针名 -> 成员名
typedef
使用方法 tapedef 类型名 别名
② 结构体别名
第一种写法:先定义结构体类型,再取别名
第二种写法:同时定义结构体类型并取别名
第三种写法:同时定义结构体类型并取别名,且省略结构体类型名称
③ 共用体别名
第一种写法:先定义共用体类型,再取别名
第二种写法:同时定义共用体类型并取别名
第三种写法:同时定义共用体类型并取别名,且省略共用体类型名称
④ 数组别名
元素类型名 别名[数组长度]
⑤ 指针别名
指向类型名 *别名;
别名就在正常变量名的位置
动态内存的分配
内存模型
栈区(Stack): 局部变量
堆区(Heap):动态分配的内存空间
静态区:全局变量、静态局部变量
代码区:字面量常量、函数代码块
void *指针
1.void* 类型的指针可以指向任何类型的数据
2.void* 类型的之不能被解引用
3.任何类型指针都可以转换为void类型的指针(会隐式转换,一般不会警告)
void指针也可以转换各种指针类型(建议加上强制类型转换)
// int 指针转为 void 指针
void *viPtr = # // 隐式类型转换
// double 指针转为 void 指针
void *vdPtr = π // 隐式类型转换
// void 指针转换为 int 指针并解引用
// int *intPtr = viPtr; // 隐式类型转换
int *intPtr = (int *)viPtr; // 显示类型转换
printf("整数值:%d \n", *intPtr);
// void 指针转换为 double 指针并解引用
// double *doublePtr = vdPtr; // 隐式类型转换
double *doublePtr = (double *)vdPtr; // 显式类型转换
printf("浮点数:%f \n", *doublePtr);
动态类型分配函数
malloc() 分配指定字节长度的空间 void *malloc(size_t 字节长度)
calloc() 分配内存空间,需要指定元素个数和单个元素的长度 void *calloc(int 元素个数,size_t 单个类型字节长的)
realloc() 调整已经分配的内存空间
void *realloc(void * 需要更该的空间地址 ,size_t 修改后的字节长度)
free() 释放分配的内存空间 void free(void * 需要释放的内存地址)
以上函数都在 <stdlib.h>
动态内存分配基本原则
(1)避免分配大量的小内存块。分配堆上的内存有一些系统开销,所以分配许多小的内存块比分配几个大内存块的系统开销大。 因为内存地址要和自己的数据类型对齐(要与自己类型成整数倍),当分配很多小内存时,有的内存湖北浪费
(2)仅在需要时分配内存。只要使用完堆上的内存块,就需要及时释放它,否则可能出现内存泄漏。
(3)总是确保释放已分配的内存。在编写分配内存的代码时,就要确定好在代码的什么地方释放内存。
提前想好什么时候要释放避免溢出
内存泄漏和内存溢出
内存泄漏: 内存空间没有被正确释放,称为内存泄漏
内存溢出: 当系统分配内存空间的时候发现不够用了,称为内存溢出; 内存泄漏增加内存溢出的风险。
预处理器
基本介绍(比用记)
预处理器:
预处理器的主要任务包括宏替换、文件包含、条件编译等。
预处理指令:
(1)预处理指令应该放在代码的开头部分。
(2)预处理指令都以 # 开头,指令前面可以有空白字符(比如空格或制表符),# 和指令的其余部分之间也可以有空格,但是为了兼容老的编译器,一般不留空格。
(3)预处理指令都是一行的,除非在行尾使用反斜杠,将其折行。
(4)预处理指令不需要分号作为结束符,指令结束是通过换行符来识别的;如果写分号,分号会成为预处理指令的一部分。
(5)预处理指令通常不能写在函数内部,有些编译器的扩展允许将预处理指令写在函数里,但强烈不建议这么干。
宏定义 #define
宏定义
#define
1. 使用宏定义定义常量
2. 使用宏定义给数据类型取别名(建议使用typedef)
3. 表达式和语句也可以作为宏定义的替换文本
4. 替换文本中可以保护其他宏名称
5. 可以使用 #undef 取消宏定义
取消宏定义
#undef
带参数的宏定义
与函数区别:
(1)宏展开仅仅是文本的替换,不会对表达式进行计算;宏在编译之前就被处理掉了,它没有机会参与编译,也不会占用内存。
(2)函数是一段可以重复使用的代码,会被编译,会给它分配内存,每次调用函数, 就是执行这块内存中的代码。 文件包含 #include
包含标准库头文件
#include <头文件名称>
包含自定义头文件
#include "自定义头文件路径"
1. 相对路径
从当前文件开始,找目标文件
./ 表示当前目录(当前文件所在的目录)
./ 开头的路径可以省略 ./
../ 表示上一级目录
../../ 表示上上级目录
../../../ 表示上上上级
2. 绝对路径
windows系统,以盘符开头,路径分隔符默认是 \, / 也可以使用
linux 系统,以 ,路径分隔符只能是 /
标签:定义,void,内存,类型,指针,函数
From: https://www.cnblogs.com/Wangleijava/p/18134652