OC

mac快捷键
command+c 用来复制文本内容
command+v 用来黏贴文本内容
command+x 剪切
command+s 保存
command+tab  切换_窗口
command+~  切换同一个程序的窗口
command+w  快捷关闭窗口
终端操作
用户前方的~符号表示一个路径
可表示根目录或者绝对路径(相对于根目录的路径)或者是相对路径
——普通操作
pwd  显示当前的路径
ls  显示当前路径下的内容
ls -a 将会显示出所有的资料,包括当前路径和父路径
cd 切换路径
clear  清屏,讲以前的操作以及其他操作或者显示清理掉
——创建文件和文件夹,移动文件和文件夹
touch 文件名  新建文件会默认的创建在当前你命令符所处的路径下面
mkdir   创建文件夹
cp 文件名路径拷贝文件到指定的路径下面
cp -rf 拷贝文件夹到指定的路径下面    -rf  表示强制性
mv   文件名    文件夹     移动路径
mv  文件名    文件名   这样可以实现文件重命名
mv 文件夹   文件夹  移动文件夹
rm   文件名  删除文件
———vi编辑器
vim编辑器
基本用vi创建文件或者打开文件,而不是用touch
普通模式    接受指令
编辑模式    读写操作
i 表示insert 马上可以进入编辑模式,并且会输入到光标的前方
a  也进入到编辑模式,只是输入到光标的后面
o  也进入到编辑模式,换行的操作,输入到另起一行,下一行
shfit+O  大写O进入编辑模式,到上一行,而不是下一行,
shfit+6   ^可以进入行首
shfit+4  $可以进入到行尾
gg   跳转到第一行
shfit+G   大写G可以跳转到最后一行
:set number 打开行号显示      //  该位置显示着行号
:number 跳转到number行     //  直接跳转到输入的数字的行数中
yy   拷贝一行的数据
p   粘贴一行数据
nyy   拷贝n行,2yy表示拷贝两行,3yy表示拷贝三行
p  粘贴拷贝的内容
dd   删除
ndd  删除n行
x  删除一个字符  类似于backspace,一个一个字符的删除
:/你要搜索的字符串    搜索字符串,按N可以搜索
n   可以切换搜索的项
:w   保存
:q  退出
:!q  不保存退出
esc  退出编辑模式,进入到普通模式
——    GCC
gcc 要编译的文件名  -o   编译后的文件名
——    Xcode
//搜索导航面板
command+f 搜索
command+0   导航面板关闭或显示
shift+command+y   显示或关闭,终端栏,调试区
option+0     检查器面板关闭或者开启
command+[    左边缩进
command+]    右边缩进
代码全选
Ctrl+i      自动排列代码
选中行代码,command+/   整体注释
1.1     进制
人类对进位的方式做出的规定
生活中大部分都是用十进制,玛雅地区人民有用五进制
但是可以随意的进制表示一些数据,并不只是2进制,8进制,10进制,16进制
计算机常用的是,2进制,8进制,10进制,16进制
10进制
求十进制,不断对该数字除10,所得余数是每一位数值
2进制
计算机最适用
由0和1构成,逢二进一
cpu正负5伏电压 交换,才能进行数据传输
2进制只有两种状态,正好正和负进行数据传输
但是CPU可以做到+5,+4,+3…..-5精确到数值
但是工业生产上难
从一种进制到另一种进制
除另一种进制取余数即可
在oc和c中直接代码中写进去的数字都是十进制,输出的适合没办法会把十进制弄成2机制,可以弄成8进制和16进制
如果数字面前加 0b   则系统会认为这个数字是个二进制的数字
8进制
逢8进1,由0~7构成
可以将十进制数值不断除以8,所得余数就是8进制数值
将一个8进制的数值不断的乘以8的N-1次幂,所得结果全部相加,得出十进制
如果数字面前加 0   则系统会认为这个数字是个八进制的数字
16进制
0~9和a~f
通常用来表示计算机内存地址,地址都是用的是16进制
如果数字面前加   0x 则系统会认为这个数字是个十六进制的数字
十进制   八进制     二进制
0        0        0
1        1        1
2        2        10
3        3        11
4        4        100
…..
15     17     1111
128            1000 0000
为什么说8进制和16进制是2进制和10进制的媒介
十进制68=64+4
0100 0000   4*16 0×40
0000 0100       0×04
0×44
四位的二进制1表示一位的十六进制
OXEF
1110 1111
三位的二进制1表示一位的八进制
027
010 111
—————C基础
printf
//%d 十进制
//%o 八进制
//%x 十六进制
#if 0   //宏定义 注释屏蔽函数
#endif
1.2     三码
源码、反码、补码
原码:一个数值的二进制表现形式,首位为符号位,用来表示正负数,0表示正数,1表示负数,其他位表示数值大小
5 假如用8位的二进制表示
0000 0101
这是5的原码
-5
1000 0101 -5的原码
如果5和-5相加计算机结果应该等于零
但是
0000 0101
1000 0101 不等于零
因为计算机中存储的是补码,所以不是0000 0101 与1000 0101相加
正数的反码就是原码
正数的补码也是原码
负数的反码,是把符号位提取,其余的位数依次取反
1111 1010
负数的补码,在反码的基础上末尾加1
1111 1011
这个时候
1111 1011+0000 0101 相加为1 0000 0000
因为取值8位,所以截断操作为0000 0000
在计算中,存储数据都是补码而不是原码,为了方便后期的运算
————
如果我们本就知道的是补码,可以逆向推,或者补码再次运算求出补码
如果符号位是0直接写出数值,因为补码等于原码
如果符号位1可以推原码
计算机存储数据基本单元一个字节,8位二进制,byte
1k=1024 byte
1m=1024 k
1g=1024 m
1t=1024 g
bit位
1.3     数据类型
分为以下几种
基本类型:整形,浮点型,字符型
复杂类型:数组,指针,结构体,枚举,联合体…..
空类型:void
1.3.1     整形———整数 64位pc
short 短整型   2个字节
int 整形             4个字节
long 长整形  8个字节
//指定数据类型的第一个作用:指定需要分配多大的空间
粉色代表关键字,蓝色代表函数
如sizeof和printf
————
通常普通int省略了signed
signed 最高位是符号位,其余都是数值位
-2^31~2^31-1
unsigned 最高位是数是数值位
0~2^32-1
———
1.3.2     浮点型
float   4字节 小数点后5~7位
double 8字节小数点15~18位
%f固定输出六位
浮点类型没有unsigned无符号,不能用signed和unsigned修饰
float 四字节,分两部分存,一部分存10幂数,一部分存符号位和大小
float 1
0000 0000 0000 0000 0000 0001 0000 0000
int 1
0000 0000 0000 0001
最后一个字节存10的幂数
第一位存+/- 表示正负数
剩下23位存数值
浮点型数据不能进行%运算,取模运算
1.4     字符类型
‘’ 字符
“” 字符串
一个字节
-2^7~2^7-1
0 000 0000
1 000 0000
-0和-2^7补码相等
所以负数小一个
1000 0000   -0
-128
1000 0000
字符和数字的1相互转换
temp+’0′;
输出格式
%f输出浮点型
%.nf .n表示输出小数点后n位小数
%c 输出字符
%s 输出字符串
没有字符串类型
1.5     变量常量
#define PI 符号常量
指定数据类型的两个作用:
1:指定分配多大的内存
2:制定数据存储的方式
整形的数据,直接把数据的补码放入内存中去
由数字对应英文符号字符 ASCII
计算机存储的都是补码,二进制
命令变量规则
1:全都是以下划线,数字和字母
2:不能数字开头
3:不能使用关键字
int a=10;
sizeof(a); //4
sizeof(10);//4
整型常量默认int类型,如果数据超出了int长度会按照long类型处理
sizeof(‘A’) //4个字节
字符型常量默认也为4个字节,字符为ASCII排列,默认也为正数
sizeof(‘1.234’)//8个字节
浮点型默认8个字节,保证精度不丢失
1.6     const
修饰变量被称为常变量
表示不能被改变,被const修饰的内存只能读取内容不能写入内容
常量在程序中运行开始到结束才消失,常变量则不会,常变量与普通变量一致,除了赋值
运算符/表达式
数据类型转换
控制语句
! ++ –
运算符优先级
逻辑运算符 ! ++ –
算术运算符 * / %
算术运算符 + -
关系运算符> ,>= <, <= ,== ,!=
逻辑运算符 && ,||,
算术运算符 =
逗号运算符 ,
但是!的逻辑运算符很高,最高级别
1.7     运算符
1.7.1     算数运算符
+
-
*
/ 除法运算和数学有区别,不会四舍五入,舍弃小数点
% 取模,取余
在运算过程中,短的数据和长的数据运算,会先把短的数据转成长的数据然后在进行计算机的计算
如果数据比int类型还短,比如short数据和char数据相加,都会转成int类型都是在64位色的pc上显示4个字节
int a=-5;
unsigned int b=3;
//有符号会向无符号看齐
printf(“%d\n”,a+b);
//   -5
//   1000 0101 原码
//   1111 1010
//   1111 1011 补码
//   3
//   0000 0011 补码
//相加后的补码
//1111 1110 补码
//1111 1101 反码
//1000 0010 原码   -2
printf(“%d\n”,a+b>0);
1.7.2     关系运算符
布尔就是int型 false为零 true为非零
表达式一 > 表达式二
<
>
>=
<=
==               判断表达式是否相等
!=
1.7.3     逻辑运算符
&& ,! ,|| //   &&比||高
它也是个双目运算符,除了!是单目运算,其他都是双目
表达式一 && 表达式二
逻辑运算符的优先级
1 || 0 && 0 先与后或返回的是:真
int a=9;
printf(“%d\n”,a>2||(a=2))// 1,正确
printf(“%d”,a); //a=9
对于或运算,如果第一个成立,第二个表达式就不去执行了
对于与运算,如果第一个不成立,第二个表达式就不会执行下去
1.7.4     赋值运算符
变量=表达式
错误语句   表达式=变量
表达式 所产生的值在寄存器中,不与内存纷争空间,再拷贝到变量中
但是=的运算符比||逻辑运算符低
a=b=10 从右算起,成立
a=b+1=10 不成立 b+1表达式出现等号左边
1.7.5     逗号运算符
int a=4,b=2,c=2;
逗号隔开多个表达式隔开
int a=4,b=2,c=2;
printf(“%d”,(a+2,b+3,c+5));   //7
逗号表达式的值其实是逗号隔开的最后一个表达式的值
逗号表达式里面的所有表达式都会依次执行一次
1.7.6     三目运算符
表达式一?表达式二:表达式三
先判断表达式一的值,如果值为真,则执行表达式二,否则执行表达式三
三目运算会有右结合性
从左边开始取: ,左边一块表达式,右边为新的表达式
printf(“%d\n”,2>3?2:(3>2?3:2));     3
1.7.7     复杂运算符
+=
/=
-=
*=
%=
++   //最好用于整形变量   加1
赋值操作先算右边
int a=10;
a+=a-=a*=10   //0     1:100 2:0 3:0
++ — 放在变量后面,表示先把变量的值拿出来用,然后再自增
float a=1;
printf(“%f”,a++);   1
printf(“%f”,a);     2
++ — 放在变量前面,表示先把变量自增,然后再把变量的值拿出来用
float a=1;
printf(“%f”,++a);   2
printf(“%f”,a);     2
int main(int argc, const char * argv[])
{
int a=2,b=3;
printf(“%d”,a+++b);   //5
printf(“%d”,++a+b);   //7
}
1.7.8     数据类型转换
1.7.9     赋值的时候、隐式转换
长数据放入短数据   //截断操作,截取低位,舍弃高位
短数据放入长数据 char 转 int//扩展的时候,正数高位补0,负数高位补1
浮点型转整形   //直接舍弃小数点部分
整形转浮点     // 小数点后面全是零
自己主动的要求计算机给我们数据进行转换
(要转成的数据类型)数据
1.7.10运算的时候
计算机自身运行,不包括格式化输出,格式化输出已经赋值操作了
截断和扩展都是针对计算机补码进行操作(负数高位1,正数高位0)
有符号会向无符号看齐
int a=-5;
unsigned int b=3;
//有符号会向无符号看齐
printf(“%d\n”,a+b); //-2
//   -5
//   1000 0101 原码
//   1111 1010
//   1111 1011 补码
//   3
//   0000 0011 补码
//相加后的补码
//1111 1110 补码
//1111 1101 反码
//1000 0010 原码   -2
printf(“%d\n”,a+b>0); //1 TRUE
整形向浮点型看齐
整形和浮点型运算,先转换为浮点型
单精度会向双精度看齐
单精度和双精度运算,为了不丢失双精度的值,会把单精度转成双精度
1.7.11格式化输入输出
scanf 格式化输入
&取址符 取出变量的地址于内存
scanf(“格式”)   格式写什么输入匹配什么
printf 格式化输出
1.7.12分支语句
1.7.13goto
写goto需要指定一个跳转的地方 叫 标号 ,对程序的运行没有任何影响
只能对函数内部有效
goto A;
printf(“test”);
A:
printf(“test”);
1.7.14选择分支
switch(表达式),表达式的值只能是整形,常量也可以
case 必须是常量
switch(表达式)
{
//大括号括起来的内容是代码块
case 常量表达式 : //case 的常量表达式,常量可以,常变量也可以但是要保证常变量不变
//选择匹配会自动执行后面的代码,遇到break跳出switch{ }代码片段
//switch语句块可以带括号
break;
default:
//默认的处理情况,不在case中出现的情况
}
int fs;
scanf(“%d”,&fs);
switch (fs/10) {
case 10:
case 9:
printf(“90分以上奖励\n”);
case 8:
printf(“80分以上奖励\n”);
case 7:
printf(“70分以上的奖励\n”);
case 6:
printf(“60分以上的奖励\n”);
break;
default:
break;
break只能跳出一层switch,在switch的代码段(代码块)
1.8     分支结构
1.8.1     if语句
if(表达式) 表达式的值是boolean
如果为真,非零就为真,执行后方语句,否则结束整个结构
if(表达式一){
}
else if(表达式二){
}
if语句中只要有一个判断成功,就会去执行里面的代码块然后结束
连下面的表达式都不会去执行,直接结束
break; 不能跳出if,和if无                       关
if表达式 switch 固定值
1.9     函数
不写函数
程序会混乱
不利于代码的复用
功能和功能之间会干扰
提高代码的复用率
程序思路清晰
函数在使用之前要先告诉编译器这是什么东西
库函数:由系统提供的函数、自定义函数
函数有,返回值类型,函数名字,参数列表,函数代码块
没有函数体的函数叫做空函数
传参实际上是值的拷贝
函数在定义的时候不分配内存,只是在调用的时候会去分配内存
包括主函数int main
函数执行过程
1:定义函数
2:给形参分配内存
3:把实参的值拷贝给形参
4:给变量分配内存
5:运算
6:运算结束,先返回值,再释放内存
定义一个函数其实叫定义性声明,简称定义
void test(int n){
//……
}
还有一种声明方式叫做引用性声明,简称为声明
void test(int n);
//可以省略n
引用性声明可以放到函数内部
但是这个声明的函数只能在函数的内部使用
函数体里面可以声明一个函数,但是不能定义一个函数
return 作用:
1:将值从被调函数传回主调函数
2:结束函数 ,return后面的代码不执行了
3:函数里面可以有无数个return,但是只有一个会生效
1.10循环
1.10.1Goto循环
不多说
1.10.2While循环
While(表达式){
// 代码块
//循环体
}
//1:判断表达式的值
//2:如果表达式的值为真,进入循环体
//3:跳转到第一步
//4:如果表达式的值为假,结束循环
可以拿控制循环自增的变量,但是不要轻易的改变他的值
表达式中的变量不能使用while代码片段内的变量
1.10.3do
1.10.4{循环体}while(表达式)
1:执行循环体
2:判断表达式的值
3:如果表达式为真,则执行循环体
4:跳转第二步
5:表达式的值为假,结束循环
do while特点就是刚开始不管什么都会执行,然后再判断表达式
for(表达式一;表达式二;表达式三)
{
循环体
}
表达式一:给自增变量赋予初值
表达式二:判断条件
表达式三:表示增量
如果自增的变量是外界的变量,则表达式三,无论条件成立不成立,只要进入for
就会再执行一次表达式三
int i=9;
int j=10;
for(;1.2;i++,j++){
}
当初始量是变量不定值,则可以通过这种方式赋予初值
省略表达式一,表示不给变量赋予初值
省略表达式二,表示死循环
省略表达式三,表示不给变量自增和变化
1.10.5break,continue
break;结束本次离自己最近的一层循环,跳出switch,while,for
return 它也是跳出代码,但是区别在于,return直接结束本函数
continue; 结束当次的循环,进入下一次循环
素数/质数就是除了1和它本身之外不能被其他数整除的数,从2开始的素数有 2,3,5,7,11,13等等
temp提取之前值   //一个提取值,一个向后挪,最后加临时
之前值等于后者
后者=后者+temp
1.11函数续—内存
1.11.1C内存篇
C语言在内存中分三大块
动态存储区,静态存储区,代码区
动态存储区:局部变量,形参,函数,常变量
静态存储区:常量(1,2……),静态局部变量(static修饰的局部变量),全局变量
1.11.2数据存储方式
动态存储方式 -> 动态存储区,高地址,地址很大,容量大
程序在运行期间,根据需要分配内存,不需要则被释放
静态存储方式 ­–> 静态存储区,低地址,地址很小,容量小
常量是程序一开始运行就有,计算机会分配固定的内存,不会被释放
代码区-> 放入你可以执行的代码
1.11.3函数内存
#include <stdio.h>
void test(){
int b=9;
printf(“%p\n”,&b);
}
int main(){
test();
test();
test();
}
当调用完test,test的内存的确销毁,但是空出来的内存又有test进来调用,则之前空出来内存块就调用给test
局部变量
1:只能在一定范围内起作用的变量
2:生命周期也比较短
register int 寄存器int变量
下方则表示当有另一个函数插入,内存容量改变
void test(){
int b=9;
printf(“%p\n”,&b);
}
void test1(){
short b=1;
printf(“%p\n”,&b);
}
int main(){
test();
test1();
test();
}
0x7fff5fbff88c
0x7fff5fbff88e
0x7fff5fbff88c
 
1.11.4全局与局部
全局变量:从定义开始到文件结束;
extern int number;
void test1(){
//也可以这里,当前函数从此处开始生效作用域
//extern int number;
printf(“%d\n”,number);
}
int number=1;
void test(){
printf(“%d\n”,number);
}
int main(){
test();
test1();
}
局部变量:定义开始,函数结束;
1.11.5extern
extern
用来扩展全局变量的作用域,不能扩展局部变量的作用域,也不能扩展被static修饰的全局变量,(static修饰的静态全局变量只能从它定义开始到文件结束)
在外部文件也extern 修饰一下全局变量,则可以实现文件中变量共享
全局变量可以实现数据的共享
extern 修饰函数的时候,可以被拿去外部文件使用
外部文件也修饰extern,并且无方法体
1.11.6static
static修饰局部变量
使得局部变量从本应该进动态存储区搬入到静态存储区
且定义第一次执行,以后在运行到此定义变量代码,默认不执行
static修饰全局变量
一旦全局变量被static修饰,此全局变量就不能被extern扩展
static修饰函数
当static修饰了函数,那此函数只能被文件内部使用
头文件主要放接口,也就是声明
1.12函数序章-嵌式调用
任何一个函数都可以调用其他的函数
其他的函数都可以在内部随意的调用
1.13函数序章-链式调用
//嵌式调用
int test1(){
printf(“test1\n”);
return1;
}
void test2(){
printf(“test2\n”);
test1();
}
//链式调用
void test3(int n){
printf(“%d\n”,n);
}
int main(){
test3(test1());
}
1.14函数序章-递归
函数自己直接或者间接的调用自己
//递归必须能够停止
//递归先执行最里面的,最后执行最外面的
递归必须要有终止(停止调用自己的判断)条件,且层次比较深的也不适合用递归,否则系统资源开销太大了
在内存资源中,会先把函数压入到内村栈,当执行到一定的函数中间段,运行下一个函数的内存段,一直到最后的函数运行结束,又跳回到上一个内存段的中间位置继续运行一层层往上翻……
1.15数组
复杂类型之一数组
数组是一些相同类型数据有序的排列构成的集合
定义数组
数据类型 数组名[长度]
长度表示数组里面有多少个数据,且是常量,如果是变量C99规定也可以使用变量,但是保证在数组定义之前,就是必须要变量有值得情况下,但是static修饰的数组长度如果变量则无效, static 静态局部变量会只执行初始化一次
int score[10] sizeof//40个字节
其中score就是首位地址,第一个元素的地址,输入数字不需要&取址符
数组在内存中所有的数组数据都是连续的
数组定义之后,没有赋值,和变量情况一样,这块内存空间会是随机值,或者上一次销毁的变量留下来的内存空间
数组名相当于地址常量,是数组的首地址,即首个元素的地址
int num[5]={1,2,3,4,5}; //完整初始化
int num1[5]={1,2,3} //num1[4] = 0 不完整初始化会初始化前面的元素
int num2[5]={}; 后方数据也为0,因为他有初始化动作
对于数组,只要有初始化的动作,就会把所有的元素都初始化一次
对于整形数组,没有初始化的一部分元素,值为0,char类型,值为’\0’,对于指针数组,,值为null
int n=9;
int num3[n]={}// 这种是不行的,可以用变量定义数组,但是不能初始化
打乱数组
#include <time.h>
#include <stdlib.h>
#if 0
//打乱数组功能
int nums[8]={1,2,3,4,5,6,7,8};
int main(){
srand((unsignedint)time(0));
for (int i=0; i<8; i++) {
int n=rand()%8;//
int temp=nums[i];
nums[i]=nums[n];
nums[n]=temp;
}
for (int i=0; i<8; i++) {
printf(“%d “,nums[i]);
}
}
#endif
#if 0
//打乱数组二
int main(){
srand((unsignedint)time(0));
int nums[8]={1,2,3,4,5,6,7,8};
int num1[8]={};
for (int i=0; i<8; i++) {
int index=rand()%8;
while (1) {
if(num1[index]==0){
num1[index]=nums[i];
break;
}
index++;
index%=8;
}
}
for (int i=0; i<8; i++) {
printf(“%d “,num1[i]);
}
}
#endif
//排序
#if 0
//选择排序
int main(){
int nums[5]={1,2,3,4,5};
for (int i=0; i<5; i++) {
//外层循环控制拿哪一个数
int index=i;
for (int j=i+1; j<5; j++) {
if (nums[i]<nums[j]) {
index=j;
}
}
int temp=nums[i];
nums[i]=nums[index];
nums[index]=temp;
}
for(int i=0;i<5;i++){
printf(“%d “,nums[i]);
}
printf(“\n”);
}
#endif
//冒泡排序
#if 0
int main(){
int nums[5]={5,4,2,3,1};
for(int i=0;i<5;i++){
for(int j=1;j<5;j++){
if(nums[j-1]<nums[j]){
int temp=nums[j];
nums[j]=nums[j-1];
nums[j-1]=temp;
}
}
}
for(int i=0;i<5;i++){
printf(“%d “,nums[i]);
}
printf(“\n”);
}
#endif
1.16数组续篇—二/多维数组
二维数组可以看做由多个一维数组组成的新的数组
1.16.1定义二维数组
数据类型数组名[行宽][列宽]
int num[4][4];
printf(“%d\n”,sizeof(nums));//64   二维数组的行宽必须是固定的,
跟一位数组一致,定义长度必须要变量有效值,但是基本也是同一样常量定义数组长度
1.16.2初始化
低地址-à二维数组的第一行,第一个元素
第二行的第一个元素紧接在第一行最后一个元素
int num[2][3]={{1,2},3,4};   //第一行 1,2,0 第二行,3,4,0
int num2[][]={1,2,3,4,5}; // 因为没有给出具体值,计算机无法计算行宽和列宽,所以错误,直接报错,如果num2[][2]//给出多少列那就ok
1.16.3字符数组
为了处理字符串方便,字符串会在字符后面加上尾零,\0表示字符串结束
检测到尾0,表示字符串结束
如果用字符串初始化数组,会直接把字符串的所有内容拷贝到数组里
char c[] =“ghs”=sizeof=4
初始化数据的时候,空数据默认以尾零填充,’\0’;
scanf(“%s”,c[0]);
输入字符串,遇到空格,就会自动的表示字符串输入结束,除非字符单个接受
%s 输出字符串
printf(“%s\n”,c);
如果遇到了%s的占位符,程序会自动的去循环读取字符,所以这里用c这个数组名,表示一个首地址,而且他自己写了一个判断是否到‘\0’结束读取
数组中如果要用char类型数组处理字符串,那么主动基本都带尾0,
如果处理其他int类型的基本数据不需要尾0
任何字符数组都可以用%S占位符打出来,当遇到字符数组中有尾零结束,如果没有尾零,会到最后一个自动加上去;
字符数组也可用多维
char c[3][10]={“hello”,”totototo”,”gogogogo”};
printf(“%s\n”,c[0]);
printf(“%s\n”,c[1]);
printf(“%s\n”,c[2]);
C[0] 相当于一维数组的数组名,也是一个首地址,地址常量
1.17指针
指针是什么?
指针的类型?
指针的运算?
指针的用法?
指针就是地址,指针变量存储地址的变量(存储指针的变量)
平时程序员沟通的适合会直接简称指针,平时为指针变量
int *a,b;
b为int,a为int指针
//直接访问,通过变量名访问数据
int a=123;
printf(“%d”,a);
//间接访问,通过指针变量访问数据
int *b=&a;
printf(“%d”,*b);
定义指针变量
数据类型 *变量名
int *b;   //* 表明这个变量为指针变量
printf(“%ld\n”,sizeof(b));
野指针,给了一个无效的地址这个指针就是野指针
int* p;
printf(“%p\n”,&p);
空指针,表示没有任何的指向,也称为悬挂,系统有预留一个null的地址0X0
int* p=NULL;
printf(“%p\n”,&p);
在内存中,释放内存只是简单的标记一下,这块内存可以被其他人使用,里面还有缓存
int num=100;
int b=200;
int *p=&num;//在定义的时候 * 表示一个指针类型
*p=b;//
printf(“%d\n”,*p);// 在时候的使用,*表示一个指针运算符
printf(“%d\n”,num);
//当*p在赋值号左边的时候,表示往指针指向的内存单元放数据
//当*p出现在赋值号右边或者没有赋值号,表示要引用其指向的内存单元里的内容
指针在类型完全匹配的时候才能直接赋值
int a=256;
char *p=&a;
//256
//1 0000 0000
//定义指针char类型,存储的a的变量地址,取数据的时候会把该地址的变量看成char类型
printf(“%d\n”,*p); //0
1.17.1指针的用途
指针的用途是用来跨函数去访问内存
//指针作为返回值的叫做指针函数,如果a变量用static修饰变静态存储区的静态局部变量即可
int* test(){
int a=10;
printf(“%d\n”,a);
printf(“%p\n”,&a);
return &a;
}
int main() {
//*b是野指针,返回的时候,a的变量已经被销毁了,其返回的地址也不是指向准确的变量地址,无效的地址
int *b=test();
for (int i=0; i<10; i++) {
*b+=i;
}
printf(“%d\n”,*b);
printf(“%p\n”,b);
}
主调函数,不能使用被调函数动态存储区的内存
因为被调函数会被释放,导致野指针
要想有效,可以返回全局变量,静态局部变量和常量的地址只要不被函数结束后释放的数据,都可以作为指针返回值
全局变量的指针效果最好,可以保护全局变量的数据安全
//一个函数,把传过来的大写字母转换成小写字母.
//这样写,会导致产生两个值
#if 0
char test(char c){
if (c>=’A’&&c<=’Z’) {
return c+32;
}
return c;
}
int main() {
char ccc=’C’;
ccc=test(ccc);
printf(“%c\n”,ccc);
}
#endif
void test(char* p){
if (*p>=’A’&&*p<=’Z’) {
*p=*p+32;
}
}
int main(){
char ccc=’C’;
test(&ccc);
printf(“%c\n”,ccc);
}
1.17.2const –>指针
(看const修饰什么就锁定什么),const p 锁定指向的值不能变,* const p,指针不能变
指针指向的变量不可变,指针可以变
int a=10;
int b=9;
constint *p=&a; // p可以变,*p不能变,可以改变p的指针,不可以改变指针指向的变量
//intconst *p=&a; p可以变,*p不能变,可以改变p的指针,不可以改变指针指向的变量
p=&b; //9
*p=*p+1; //报错
printf(“%d\n”,*p); //10
指针不可以变,指针指向的值可以变
int a=10;
int b=9;
int *const p=&a; //指针不可以变,指针所指向的值可以变
*p=*p+1;
printf(“%d\n”,*p);
两者都不能变
constintconst*p=&a;//两者都不能变
intconst * const p,j=10;//p指针不能变,指针指向值也不变,j也不能变
1.17.3指针运算
指针只有加减法运算
int a=10;
int b=5;
int c=1;
int *p=&b;
printf(“%p\n”,p);
printf(“%p\n”,p+1);
printf(“%p %p\n”,&a,&b);;
0x7fff5fbff898
0x7fff5fbff89c
0x7fff5fbff89c 0x7fff5fbff898
指针做加减法运算,加1表示加一个数据类型的宽度,b是一个int类型所以加1等于加上4
指针的加减法要看指向的数据类型的宽度
因为目前函数的数据是连续的,所以b的地址加1就是a,a是从高地址走向低地址
指针运算的结果还是指针
指针和指针之间可以做减法运算(可以计算出这两个指针指向头中间的元素个数,但是得要一致类型的指针变量)
int nums[10]={1,2,3,4,5,6,7,8,9,10};
int *p,*q;
p=&nums[0];
q=&nums[5];
printf(“%ld\n”,q-p); //5 之间相差多少元素其实相差20取出%ld自动除以4字节
1.17.4指针和一维数组
数组的地址不能改,所以不能运用数组++的运算
int nums[10]={1,2,3,4,5,6,7,8,9,10};
//直接用数组名访问
for (int i=0; i<10; i++) {
printf(“%d “,nums[i]);
}
printf(“\n”);
//将数组名以指针的形式来访问数组的元素
for (int i=0; i<10; i++) {
printf(“%d “,*(nums+i));
}
printf(“\n”);
int *p=nums;
for (int i=0; i<10; i++) {
printf(“%d “,*p++);
}
printf(“\n”);
//数组名可以当做指针来用,但是不能使用赋值运算
for (int i=0; i<10; i++) {
printf(“%d “,*nums++);//报错
}
printf(“\n”);
 
指针在数组的运用
建立一个函数,传进数组的首地址进去,处理数组比传进数组后处理方便的多
 
1.18指针与二维数组
指针的类型要看整体
int (*q)[3] 指向三个int类型的元素的一维数组,指向行的指针
p[1]=*(p+1);
w==q[1]=*(q+1);
w[1]=*(w+1);
q[i][j]=*(*(q+i)+j);
int main(){
int num[2][3]={1,2,3,4,5,6};
int *p;
p=num;
//num 表示第一行的首地址指向行
//num+0 表示第一行的首地址指向行
//num+i 表示第i+1行的首地址指向行
//num[0] 表示第一行第一列的元素的地址   指向列
//*(num+0)==num[0]   正确,完全一样,表示第一行第一列的元素
//*(num+0)==num+0   正确,值相等,含义不一样
//num[0]+1//表示第一行第二列的地址,指向列
//*(num+0)+1 //表示第一行第二列的地址指向列
//*(num+i)+j //表示第i+1行第j+1列地址指向列
printf(“%p\n%p\n”,num,p);
printf(“%p\n%p\n”,num[0],p+0);
printf(“%d\n%d\n”,*num[0],*(p+0));
printf(“%p\n%p\n”,num[1],*(num+1));
printf(“%p\n%p\n”,&num[0][1],*(num+0)+1);
}
二维数组传参数,行长度可以无视,列的长度必须带上,就是有几列
否则指针在*(p+1) 不知道加多少列为下一行,多维数组也是如此
void test(int a[][3]){ //形参也可以int (*a)[3]
for (int i=0; i<2; i++) {
for (int j=0; j<3; j++) {
printf(“%d “,*(*(a+i)+j));
}
printf(“\n”);
}
}
int main(){
int numbers[2][3]={1,2,3,4,5,6};
test(numbers);
}
//封装一个函数,求2行3列的二维数组最大值
int* Max(int (*p)[3]){
int *max=p;
for (int i=0; i<2; i++) {
for (int j=0; j<3; j++) {
if (*max<*(*(p+i)+j)) {
max=(*(p+i)+j);
}
}
}
return max;
}
int Max2(int *p){
int max=0;
for (int i=0; i<2; i++) {
for (int j=0; j<3; j++) {
if (max<*(p+i*3+j)) {
max=*(p+i*3+j);
}
}
}
return max;
}
int main(){
int numbers[2][3]={1,2,3,11,5,6};
printf(“max=%d\n”,*Max(numbers));
printf(“max=%d\n”,Max2(numbers));
//下标值
printf(“%ld\n”,Max(numbers)-*numbers);
long max_index=Max(numbers)-*numbers;
long x=max_index/3;
long y=max_index%3;
printf(“%d\n”,numbers[x][y]);
}
指针访问数组 / 数组名访问
数组内存大,指针只有8个字节
指针需要先找到元素的地址
数组名不能赋值,指针可以赋值
数组存储元素的值
指针存储的是元素的地址
1.19指针和字符串
char *q=”str”;//str这个常量存入常量中,也是以一维数组的形式存在
printf(“%s\n”,q);
常量字符串在内存里也是静态存储区以一维数组的形式存在,且尾零结束
//字符串常量初始化数组的时候,是把常量的内容拷贝到数组里
//用字符串初始化指针的时候,是返回常量的地址给指针
char str[10]=”str”;
char *s=str;
char *q=”str”;
printf(“%p\n”,q);
printf(“%p\n”,s);
printf(“%p\n”,”str”);
0x100000f8c
0x7fff5fbff884
0x100000f8c
用指针修改字符串的内容必须保证指针指向一块可以被修改的内存,比如,如果指向常量就无法修改了
const char h=’H’; //const修饰只是保证原始变量还是初始值
char* p=&h;
*p=’A’;
printf(“h===%c\n”,h); // H
printf(“p====%c\n”,*p); // A
//在main函数里,实现两个字符串的拷贝
int main(){
char *a=”hello”;
char buff[6];
char *b=buff;
for (int i=0; i<6; i++) {
*(b+i)=*(a+i);
}
printf(“%s”,buff);
}
指针返回的用途
如果在各类函数里面操作指针,传递指针,中途非常极其有可能会利用这个指针做其他事情,从而改变原有的值
那就只能通过返回指针赋值给新的指针,然后就拿返回的指针,那这样这个指针中途赋值给新的指针,然后操作也跟返回的指针也没有什么作用,不会改变最初值
1.20函数指针
指针函数只是返回一个指针的函数
函数指针是另一个领域,是指向函数的指针
函数返回值是什么类型参数什么类型都要和函数指针相同
test();
void (*p)();
//函数名是函数的首地址
p=test;
p();
typedef 取别名
typedef对函数指针取别名,可以定义一个数据类型名
typedef void (*FUNCTION)();
void test(){
printf(“test\n”);
}
int main(){
FUNCTION t=test;
t();
}
函数回调,跟java的接口回调一模一样,但是唯一不同点的是,java的接口可以通过ioc容器或者代码里设值注入
typedef void (*FUNCTION)(int state);
void huanqian(int state){
if (state) {
printf(“您还钱给我\n”);
}
else{
printf(“不还钱\n”);
}
}
FUNCTION buy_yinliao(){
printf(“买饮料\n”);
printf(“你把饮料送给我\n”);
return huanqian;
}
int main(){
FUNCTION p=NULL;
p=buy_yinliao();
int a;
scanf(“%d”,&a);
p(a);
printf(“\n”);
}
函数指针可以传递操作
1.21二级指针
刚开始
指针指向变量
指针指向一维数组
指针指向二维数组
指针指向字符串
指针指向函数
二级指针
指向指针的指针
void test(int *p){
static int a=10;
p=&a;
printf(“%d\n”,*p);
}
int main(){
int *p;
test(p);//放进去就会销毁
*p=100;//错误
test(p);
}
void test(int **q){ //传进p存储的变量地址
static int a=10;
*q=&a;   //实际上将p指向的地址改变成指向a,所以q的作用在于帮助p指针改变指向,然后这个形参销毁掉,也没有关系,因为那个时候q已经帮p得指向改成了a
printf(“%d\n”,**q);
}
int main(){
int *p;
int* *q;
q=&p; //q存储的就是p的地址,就能拿到p指向变量的地址
test(q);//放进去改变了p指针存储的地址值
*p=100;//正确
test(q);
}
void change(int **a,int **b){
int *temp=*a;
*a=*b;
*b=temp;
}
int main(){
int a=10;
int b=9;
int *p=&a;
int *q=&b;
change(&p,&q);
printf(“%d %d\n”,a,b);
printf(“%d %d\n”,*p,*q);
//10 9
//9 10
}
int main(){
int sum[2][3]={1,2,3,4,5,6};
int *p=sum[0];
int **q=&p;
for (int i=0; i<2; i++) {
for (int j=0; j<3; j++) {
printf(“%d “,*(*q+i*3+j));
printf(“%d “,*(*(sum+i)+j));
}
printf(“\n”);
}
}
int main(){
int num[10]={1,2,3,4,5,6,7,8,9,10};
int *p=&num[0];
int **q=&p;
printf(“%d\n”,**q);//1
printf(“%d\n”,**q+1);//2
printf(“%d\n”,*(*q+1));//2
printf(“%d\n”,**(q+1));//崩掉
}
1.22指针数组
一个数组里面全是指针,由指针构成的数组
int main(){
int num[10]={1,2,3,4,5,6,7,8,9,10};
int * p=num;
int * q[10]={};
q[0]=num;
q[1]=num+1;
for (int i=0; i<10; i++) {
q[i]=num+i;
//q+i=num+i;   q+i表示一个二级指针,指向一个指针数组的首元素指针
}
for (int i=0; i<10; i++) {
printf(“%d\n”,*q[i]);
}
int **w=q; //对指针数组首地址取地址作为二级指针
for (int i=0; i<10; i++) {
printf(“%d\n”,*(*w+i));
printf(“%d\n”,**(w+i));//也正确
}
}
1.23字符串函数
#include <string.h>
//自己实现str_len
size_t my_strlen(const char *s){
size_t length;
while (*(s++)!=’\0′) {
length++;
}
return length;
}
//自己实现的strncopy
char* my_strncopy(char *s1,const char *s2,size_t length){
for (int i=0; i<length; i++) {
*(s1+i)=*(s2+i);
if (*(s1+i)==’\0′) {
break;
}
}
return s1;
}
//自己实现的字符拼接
char * my_strcat(char* s1,const char*s2){
int length=0,i=0;
while (*(s1++)!=’\0′) {
}
s1–;
while ((*(s1++)=*(s2++))!=’\0′) {
}
return s1;
}
//自己第二个实现的高效率字符拼接
char* my2_strcat(char* s1,const char* s2){
for (int i=0,j=0,state=0;; i++) {
if (*(s1+i)==’\0′) {
state=1;
}
if (state) {
if ((*(s1+i)=*(s2+j))==’\0′) {
break;
}
else{
j++;
}
}
}
return s1;
}
int main(){
char st[100]=”nihao”;
printf(“%ld\n”,sizeof(“hhhh”));//5
printf(“%ld\n”,strlen(“hhhh”));//4 strlen 自动去尾零取有效字符
printf(“%ld\n”,strlen(st));//5
char *str1=”testa”;
char str2[20]=”h”;
strncpy(str2, str1, 5); //字符拷贝,带拷贝数量
my_strncopy(str2, str1, 5);
printf(“%s\n”,str2);//testa
char *str3=”hello”;
char str4[100]=”nihao a “;//要拼接的总和数组必须要有足够的容器,不能是指针
//将s3拼接到s4的末尾
//strcat(str4,str3);
my_strcat(str4,str3);
printf(“%s\n”,str4);
}
//比较s1和s2的两个字符串 -68
printf(“%d\n”,strcmp(“ABC”, “ABCD”));
字符串的比较规则是s1==s2 返回0
s1>s2 返回正数
s2<s2 返回负数
从前往后比,依次进行字符比较,遇到第一个不相同的字符,就以当前这个字符的比较结果为准
int my_strcmp(constchar*s1,constchar*s2){
while (*s1==*s2) {
if (*s1==’\0′)
return0;
s1++;
s2++;
}
return *s1-*s2;
}
//s1里面搜索s2是否出现过的,出现了就返回第一次出现的位置
//没有出现过返回空
//自己实现的字符搜索
char* my_strstr(constchar* s1,constchar* s2){
size_t length=strlen(s1);
for (int i=0; i<length; i++) {
for (int j=0; j<strlen(s2); j++) {
//比较s1和s2的相同值
if (*(s1+i+j)!=*(s2+j)) {
break;
}
if (j==strlen(s2)-1) {
//如果全都比完了,都还正确,就直接拿出来
return s1+i;
}
}
}
return s1;
}
scanf函数
int   scanf(constchar * __restrict, …) __scanflike(1, 2);
返回的int表示正确匹配到的参数的个数
printf函数
int   printf(constchar * __restrict, …) __printflike(1, 2);
返回printf中输出的字符串长度
char *p=”%d %d\n”;
printf(p,1,2);
空白符,表示忽略掉所有的空白符
非空白符,作格式用的
占位符
1.24解包组包
//解包,组包
//sscanf(data, format,参数列表)   à返回数据包长度
//将data里面的数据解析到参数列表中,按照一定的格式
char* data=”10,7.9,hello”;
// , 逗号分隔符
int a;
float b;
char str[10];
sscanf(data, “%d,%f,%s”,&a,&b,&str);
printf(“%d\n”,a);
printf(“%f\n”,b);
printf(“%s\n”,str);
//扫描集
//扫描集
//%[字符]
//匹配扫描集中所有出现的字符,或者用a-z,a到z中出现的字符,或者[a-zA-Z],
//^符号,^a,凡是不是a的字符都匹配
sscanf(data, “%d,%f,%[heop]“,&a,&b,&str);
扫描集解决空格不能输入
char data[90];
scanf(“%[^\n]“,data);
printf(“%s”,data);
//sprintf(<#char *restrict#> data, format,参数列表 );
//返回数据包的大小长度
//将参数列表里的内容按照指定的格式组包到data里面
char data[100];
int a=10;
float b=7.9;
char *str=”hello”;
int temp=sprintf(data, “%s%d%f”,str,a,b);
printf(“%s\n”,data);
printf(“%d\n”,temp);
hello107.900000
15
int temp=sprintf(data, “%s,%d,%f”,str,a,b);// hello,10,7.900000 17
1.25进程和线程
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void *thread1_entry(){
while (1) {
printf(“线程一\n”);
sleep(1);
}
returnNULL;
}
void *thread2_entry(){
while (1) {
printf(“线程二\n”);
sleep(2);
}
returnNULL;
}
int main(int argc, constchar * argv[]) {
pthread_t thread1;
pthread_t thread2;
pthread_create(&thread2, NULL, thread2_entry, NULL);
pthread_create(&thread1, NULL, thread1_entry, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
return0;
}
1.26内存分配
静态存储,分配固定大小的空间,不会被释放
常量,static修饰的局部变量,全局变量
动态存储,在程序运行期间,根据需要分配内存,可以被释放
函数,局部变量,形参,常变量
其函数调用完后即销毁掉所占用的内存区
static 修饰变量
修饰全局变量:作用域不能被扩展
修饰局部变量:改变变量存储的位置
static 修饰的局部变量           未赋值初始化都为0   ->未初始化区
其他的局部变量                        未赋值初始化都是随机值
返回值—只能返回一个确切的结果,后期不能再修改该内存的内容
全局变量—可以拿到返回运算结果,还可以记录修改的值,但是常驻内存,提高程序的耦合性
被调函数使用主调函数的内存,将主调函数的变量地址传入被调函数的参值
主调函数使用被调函数的内存,只能通过static局部变量来实现
1.26.1动态存储区
动态存储区分栈和堆
栈:先进后出
规规矩矩一个栈,先进去的后出来
在代码片段函数体中,先初始化的变量在高地址,后初始化的变量在低地址
堆:类似于一张表
先从底层一行开始堆内存起来,如果不够,再往上爬一层再堆起来
如果之前有释放的,且正好这个内存能容纳进去,则在这个堆中放入这块内存
其实就是遍历的去寻找合适的位置,然后放入这个位置
1.26.2静态存储区
静态存储区有未初始化区和初始化区
未初始化区将数据设为0
初始化区根据用户放入的值设定
1.26.3静态内存分配
在编译的时候不会马上给代码段分配内存的,
但是编译的时候都已经知道分配多大的内存,
不需要管它的内存,都是程序自动分配和释放
总结,在编译期间就已经知道分配多大的内存
存储到:静态存储区,动态存储区(栈)
由系统来管理内存,不需要我们操心
1.26.4动态内存分配
由程序员去调配分配内存
在程序运行的时候才能计算出应该分配多大的内存
存储到:动态存储区(堆)
手动分配,手动去释放
数据比较大
//动态分配内存
int *p=malloc(4);
指定要分配4个字节
malloc的参数表示要分配的内存的大小,
返回值表示分配的内存的首地址
void *a=malloc(4);
void * 表示泛型指针,表示可以指向任意的一种类型的地址
printf(“%d\n”,*p); //报错
你可以用泛型指针指向一个地址,但是不能通过泛型指针访问数据
int a=256;
void *p=&a;
char *b=p;
printf(“%d\n”,*b); //为0
所以泛型指针可以传递通过指定类型的指针来显示数据
malloc(值),如果值非常的大
如果分配好了,返回所分配的内存的首地址
如果失败了,返回Null
//预设一个一百万个字节的数组
//输入一万次
//只有一次是数据有一百万个字节,其他的9999个输入数据都是1个字节
//这个时候就需要动态内存分配
for (int i=0; i<10000; i++) {
scanf(“%d”,&length);
char *str=malloc(length);
scanf(“%s”,str);
printf(“%s\n”,str);
}
但是上面的程序代码malloc分配的数据越来越大,没有释放,所以需要
int main(){
for (int i=0;; i++) {
void*p=malloc(100);
free(p);
}
}
free(泛型指针);释放内存
free只管堆上的手动分配的内存
一个free对应一个malloc
一旦用到了malloc,就要对应给一个free,并且只能有一个free
防止free误操作
在被调函数中释放,但是又在其他或者主调函数中释放
可以在第一次释放的时候悬挂指针
泛型指针=NULL;
free自己会去判断是Null就不会操作,不是就会去释放内存
1.26.5内存泄露
程序员压根没有释放内存
一个字节操作/8个位处理
memset(//数据地址,//替换值,//执行长度);
//calloc(<#size_t#> n, <#size_t#> l);
//手动分配n个l字节的内存
int *p=calloc(1, 4);
*p=9;
*(p+1)=10;
printf(“%d\n”,*p);
printf(“%d\n”,*(p+1));
不允许带*p++这样的运算,如果运算成这样,那就无法找到原来 *p的值
1.26.6内存函数操作
//void *memset(<#void *#>, <#int#>, <#size_t#>)
一个字节操作/8个位处理
重置清零
memset(//数据地址,//替换值,//执行长度);
printf(“%ld\n”,sizeof(“hello”)); //6
printf(“%ld\n”,sizeof(‘h’)); //4   字符都是以数字的方式存储的
int *p=malloc(4);
int *q=realloc(p, 8);
//realloc(<#void *#> 指针, <#size_t#> 大小);
重新分配内存,讲原来的p扩大到了8个字节,遍历内存堆,寻找合适的位置能够容纳下8个字节的位置
realloc传进来的指针只能是手动分配的
1.27结构体
结构体是一种数据类型
可以把任意的类型的数据放到结构体里面
//定义结构体类型
//struct 结构体名
//{
// 成员列表
//};
struct Std{
char name[20];
int age;
int score;
};
结构体嵌入结构体
struct Birthday{
int year;
int month;
int day;
};
struct Std{
char name[20];
int age;
int score;
structBirthday birthday;
};
Std的sizeof大小为28
所有的成员都有字节的数据类型和名字
每一个成员之间,用;隔开表示它是不同的成员
结构体完整初始化
structStd student={“xiaobai”,20,100};
结构体不完整初始化
structStd student={“xiaobai”};   //age,score未初始化的都为0
结构体指定初始化
structStd student={.age=1};
结构体中结构体初始化
structStd student2={“t”,1,2,3,4,5};
在结构体中
. 点为成员运算符
structStd student={};
scanf(“%s %d %d”,student.name,&student.age,&student.score);
printf(“姓名:%s,年龄:%d,成绩:%d\n”,student.name,student.age,student.score);
printf(“%d\n”,student2.birthday.day);
1.27.1结构体拷贝
1:单个属性拷贝,name为数组名,不能进行直接赋值操作给其他的结构体中,可以通过strcpy字符拷贝函数实现
2:数组memcpy,直接通过这个函数拷贝
memcpy(student2, student, sizeof(student2));
3:同类型结构体变量直接赋值
student2=student;
结构体访问
//如果是结构体变量用.
//如果是结构体指针用->
1.27.2结构体数组
//创建5个学生的结构体数组
//输入信息,按照年龄排序
structStd students[10];
for (int i=0; i<5; i++) {
scanf(“%s %d %d”,&students[i].name,&students[i].age,&students[i].score);
}
for (int i=0; i<4; i++) {
for (int j=i; j<5; j++) {
if (students[i].age<students[j].age) {
structStd temp=students[j];
students[j]=students[i];
students[i]=temp;
}
}
}
for (int i=0; i<5; i++) {
printf(“姓名:%s 年龄:%d 成绩:%d\n”,students[i].name,students[i].age,students[i].score);
}
Int GetDay(structDate date){
int temp=0;
int run=28;
if ((date.year%4==0&&date.year%100!=0)||date.year%400==0) {
run=29;
}
switch (date.month) {
case12:
temp+=31;
case11:
temp+=30;
case10:
temp+=31;
case9:
temp+=30;
case8:
temp+=31;
case7:
temp+=31;
case6:
temp+=30;
case5:
temp+=31;
case4:
temp+=30;
case3:
temp+=31;
case2:
temp+=run;
case1:
temp+=0;
break;
}
return temp+date.day;
}
int main(){
//输入年月日
//封装一个函数,输出这一天是一年中的第几天
structDate date;
scanf(“%d %d %d”,&date.year,&date.month,&date.day);
printf(“%d\n”,GetDay(date));
}
1.28链表
链表是一个结构体指针
1.29结构体续篇
返回两个值的函数不可行
但是如果想要确切这么做,必须通过
1:使用指针,传入变量地址
2:使用全局变量
3:返回结构体
结构体指针数组
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Std{
int age;
char name[20];
int score;
};
void test(structStd *p){
printf(“年龄是:%d\n”,p->age);
}
void test2(structStd **p){
//printf(“年龄是:%d\n”,p[0]->age); 也可以
//p[0]表示第一个学生的地址
//p[0]+1
//p[1]
printf(“%p\n”,p[0]);
printf(“%p\n”,p[0]+1);
printf(“%p\n”,p[1]);
printf(“年龄是:%d\n”,(*p+1)->age);
}
int main(int argc, constchar * argv[]) {
structStd student={20,”zs”,99};
test(&student);
structStd students[10];
structStd *w[10];
for (int i=0; i<10; i++) {
students[i]=student;
if (i==1) {
students[i].age=99;
}
w[i]=&students[i];
}
test2(w);
}
//十个人,对三个人进行投票
struct Person{
char name[20];
int ps;
};
int main(){
structPerson p[3]={{“xiaobai”,0},{“xiaohei”,0},{“xiaohong”,0}};
for (int i=0;i<10; i++) {
char temp[20];
scanf(“%s”,temp);
if (strcmp(temp, “xiaobai”)) {
p[0].ps+=1;
}
elseif (strcmp(temp, “xiaohei”)){
p[1].ps+=1;
}
elseif (strcmp(temp, “xiaohong”)){
p[2].ps+=1;
}
}
for (int i=0; i<3; i++) {
printf(“%s的票数是:%d\n”,p[i].name,p[i].ps);
}
}
1.29.1结构体的动态内存
结构体和动态内存的结合,relloc 动态扩展学生数量
struct Std{
char name[20];
int age;
};
unsignedlong student_max=0;
//添加一个学生资料
void* Add(structStd *p){
//分配第一个人的空间
p=malloc(sizeof(structStd));
structStd *q=p;
//扩展原来的空间,扩展2个人的空间
p=realloc(p, sizeof(structStd)*(student_max+1));
//如果P分配失败,返回的是NULL,则为了不让p的数据丢失,执行以下步骤
if (!p) {
p=q;
}
q=NULL;
student_max+=1;
return p;
}
int main(){
structStd *p=malloc(sizeof(structStd));
student_max+=1;
for (int i=0; i<10; i++) {
p=Add(p);
}
printf(“学生数量为:%d\n”,student_max);
}
名字动态分配内存
将结构体std中的name不写成数组类型,直接用指针指向其他字符数组的地址
则std中name指向的变量,使用malloc动态分配字符数组的大小
创建
结构体可以一层套一层结构体,然后通过动态内存分配
将外层先动态内存分配出来再一层层往里分配内存出来
释放
释放先释放最里的一层结构体,然后在逐步释放外面的结构体,否则会成为内存泄露,指向不到里层结构体
1.29.2内存对齐
//内存对齐
struct TEST{
char a;
int b;
short c;
};
1 1 1 1 1 1 1 1 1 1 1 1
int main(){
printf(“%d\n”,sizeof(structTEST));//输出为12而不是7
}
1:整个结构体的大小是最大的数据类型的整数倍
2:如果剩余的空间不足以放下一个数据,那么直接再分配一个最大的宽度
3:每一个数据所在的位置必须是自己宽度的整数倍,2个字节为2,4,8
4:如果中间的空闲空间不能放任何数据,就直接空闲.
//内存对齐
struct TEST{
char a;
short c;
int b;
};
则为8的大小
1 1 1 1 1 1 1 1 1 1 1 1
1.29.3关于越界
一般数组越界就会崩
但是结构体有不定长结构体
可以初始分配的时候扩展内存空间
1.29.4typedef
//1:将简单的数据类型重命名
//typedef 原始数据类型新的数据类型名
typedefint sint;
int main(){
sint a=1;
printf(“%d”,a);
}
2:简化函数指针的写法
typedefvoid (*FUNCTION)();
void test(){
printf(“test\n”);
}
int main(){
FUNCTION p=test;
p();
}
typedefint *h[10];//指针数组
typedefint (*H)[10];//指向行的指针
结构体链表
//结构体链表,结构体信息一层层链下去
typedefstruct Std{
char name[20];
int age;
structStd *next;
}StudentInfo;
1.30联合体
联合体也叫做共用体
特点:共用同一段内存
Price1 Price={‘A’};
printf(“%d\n”,sizeof(Price)); //4
printf(“%c\n”,Price.a);//A
printf(“%d\n”,Price.b);//65
printf(“%f\n”,Price.c);//0.000000 65被存入了10的幂数中去
  1. a=’b’;
  2. b=100;
printf(“%d\n”,sizeof(Price)); //4
printf(“%c\n”,Price.a);//d   都是最后存入的一次数值决定
printf(“%d\n”,Price.b);//100
printf(“%f\n”,Price.c);//0.000000 100被存入了10的幂数中去
联合体可以存好几种类型的数据不是共同存入好几种技术
它可以变成int型,又可能是float型
基本都是结构体内嵌入共用体
1.31枚举
把有限的情况一一列举出来
枚举主要用来做状态的判断和选择
//enum 枚举类型名
//{枚举常量列表}
//每一个常量之间用,隔开
//最后一个常量不需要,
enum WeekDay{
MON,TUE,WEN,THU,FRI,SAT,SUN
};
定义好数据类型已经有值,所以叫枚举常量,可以直接拿出来,而不是放入一个数据类型等待存值
printf(“%d\n”,sizeof(enumWeekDay));//4
int main(){
printf(“%d\n”,MON); //0 第一个元素默认赋值0,也可以自己指定一个数值,后面的元素一次递增1,且都为整数
printf(“%d\n”,TUE); //1 后面的元素也可以制定一个值,但是这个元素后面的值不会变,还是跟以前一样
printf(“%d\n”,WEN); //2
}
枚举放入的值都会从0开始递增
enum Qiu{
Bai,
Hei,
Hong
};
void myprint(int t){
switch (t) {
caseBai:
printf(” 白色 “);
break;
caseHei:
printf(” 黑色 “);
break;
caseHong:
printf(” 红色 “);
break;
default:
break;
}
}
int main(){
for (int i=Bai; i<=Hong; i++) {
for (int j=Bai; j<=Hong;j++) {
for (int k=Bai; k<=Hong; k++) {
if (!(i==k&&i==j)) {
myprint(i);
myprint(j);
myprint(k);
printf(“\n”);
}
}
}
}
}
1.32预编译
#符号 表示一个预编译指令
1.32.11:宏定义
用一个名字替换一个字符串
#define 宏名将要替换的字符串
1:无参宏
//无参宏
#define FORMAT “%d\n”
#define PI 3.14*2
#define TEST test()
void test(){
printf(“test\n”);
}
int main(int argc, constchar * argv[]) {
TEST;
printf(FORMAT,123);
return0;
}
哪里用到了format,就会替换%d\n
2:带参宏
也成为宏函数,类似于函数一样
#define MAX(a,b) a>b?a:b
#define ADD(a,b) a*3+b
#define SWAP(a,b) a=a+b;b=a-b;a=a-b;
printf(“%d\n”,ADD(2+3,3));//14   2+3*3+3=14
printf(“%d\n”,ADD((2+3),3));//18
SWAP(a,b);
printf(“a=%d,b=%d\n”,a,b);
1.32.22:文件包含
#include <系统库> 到系统指定的目录下搜索头文件
#include “自定义” 到我们自己的工程下面搜索文件搜索不到的时候才会去系统指定的目录下搜索
不要与系统的头文件重名
1.32.33:条件编译
#if 0
//代码
#else
//代码
#endif
0为假,非0即为真
#define MY_DEBUG
#ifdef MY_DEBUG
int main(){
printf(“定义了宏\n”);
}
#else
int main(){
printf(“没有定义宏\n”);
}
#endif
#undef MY_DEBUG   //取消宏的定义
#ifndef MY_DEBUG   //如果没有定义这个宏
以下这种结构是为了防止include文件包含重复,导致define重定义
#ifndef test_Header_h
#define test_Header_h
#endif
经典的DEBUG模式
#define MY_DEBUG
#ifdef MY_DEBUG
#define debug printf
#else
#define debug /\
/printf
#endif
void test(){
debug(“%s\n”,__FILE__);
}
int main(){
printf(“main\n”);
test();
}
1.33位运算
int a=1;
int b=1;
//0000 0001
//0000 0001
//& 按位与两位都为1则1,否则为0
//| 按位或有一位为1则1,否则为0
//^ 按位异或相同则为0,不相同则为1
// 按位取反 0变1,1变0
//<< 左移把所有的二进制位往左边移动几位,高位舍弃,低位空掉的补上0
char c=-9;//c>>2   -3
//1111 0111
//1000 1001
//
//>> 右移把所有的二进制位往右边移动几位,低位舍弃,负数高位补1,正数高位补0
printf(“%d\n”,c>>2);
}
1.1     函数和方法
函数
没有+ -号
可以写在任意的地方
可以直接调用函数
方法
有+ -号
只能写在类里面,因为方法是依赖于对象的,如果对象没有
那么就不存在这样的行为
1.2     重载构造方法
构造方法主用于初始化
只能在创建对象的时候并在开辟内存之后开始初始化
-(id)initWithSpeed:(int)speed{
//   if (self==[super init]) {
//       _speed=speed;
//       _color=@”黑狗”;
//   }
   self=[selfinitWithSpeed:speed withColor:@"黑狗"];
   returnself;
};
-(id)initWithSpeed:(int)speed withColor:(NSString*)color{
   if (self==[superinit]) {
       _speed=speed;
       _color=color;
   }
   returnself;
};
以上只能参数少得调用参数多的
不然省的参数浪费,没法设值
1.3     类方法
Class 类类型一个结构体指针
由类调用的方法称类方法,由实例对象调用的方法称实例方法
在类方法不能直接调用实例方法
要想调用实例方法,要通过给予的实例对象才能调用
在实例对象不能直接调用类方法
要想调用类方法,可以通过类去调用类方法[类 类方法]
在类方法不能直接访问实例变量
1.4     类中内存
对于工具类通常都用类方法去实现
类的内存里面会存入一些函数指针代表实例方法和类方法
而在对象实例中内存会存入成员实例变量
而且还有一个isa,它会指向哪一个类
1.5     枚举类
类中有枚举值
typedefenum{
   YUAN,
   ZHENGFANGXING,
   CHANGFANGXING
}ShapeType;
@interface Lei : NSObject{
   int _length;
   int _type;
}
-(id)initWithLength:(int)length withType:(ShapeType)type;
-(int)jiSuan;
@end
@implementation Lei
-(id)initWithLength:(int)length withType:(ShapeType)type{
   if (self==[superinit]) {
       _length=length;
       _type=type;
   }
   returnself;
}
-(int)jiSuan{
   int temp=0;
   switch (_type) {
       caseYUAN:
           break;
       caseCHANGFANGXING:
           NSLog(@”我计算长方形”);
           break;
       default:
           break;
   }
   return temp;
};
@end
int main(int argc, constchar * argv[]) {
   @autoreleasepool {
       Lei *lei=[[Leialloc]initWithLength:100withType:CHANGFANGXING];
       [lei jiSuan];
   }
   return0;
}
1.6     字符串
类方法创建对象会方便一些,
但是不知道什么时候会释放对象,
因为我们不需要关心类方法创建的对象的释放
那么类方法创建的对象相当于是一个临时变量
//C语言的字符串
char str1[20]=”hello”;
char *str2=”hello”;
printf(“%s\n”,str2);
//oc的字符串
NSString* str3=@”hello”;
NSLog(@”%@”,str3);
//对象指针指向产量
NSString *str1=@”hello”;
//创建字符串对象
//语法是没有错误,但是没有实际的意义,因为NSString不可以改变的
NSString *str2=[[NSStringalloc]init];
NSLog(@”%p”,str1);//0x1000020a8
NSLog(@”%p”,str3);//0x1000020a8 str3做了优化,因为str1,str3本身无法改变,为了节省资源,直接将str3的指针指向str1
NSLog(@”%@”,str3);//hello
//将C语言的字符串转换为OC的字符串
//unicode编码 每个字符都占两个字节
//utf-8 能灵活控制内存字节数,一个字母一个字节,一个汉字两个字节或者三个字节
//将控制台C语言的scanf输入的c字符串转换OC能够使用的字符串
NSString * str4=[[NSStringalloc]initWithUTF8String:”哈哈”];
//作用
//1:可以拼接字符串
//2:把任意的类型的数据以指定的格式组装到字符串里
//3:将其他类型的数据,转换成字符串类型
NSString * str5=[NSStringstringWithFormat:@"%@ %d",str4,123];
//类方法创建对象
//stringWithString事先判断是否是指向NSString对象的指针,那此条符合语法,不会警告
NSString* str6=[NSStringstringWithString:str4];
NSLog(@”%@”,str6);
1.7     字符串实用函数
1.7.1     //字符串比较,option 字符串比较
BOOL rec=[str1 isEqualToString:str2];
NSLog(@”%@”,rec?@”相等”:@”不相等”);
//字符串比较,option 不区分大小写
NSComparisonResult state=[str1 compare:str2 options:NSCaseInsensitiveSearch];
       //字符串划定一个区域对比
NSComparisonResult state1=[str1 compare:str2 options:NSCaseInsensitiveSearchrange:NSMakeRange(1, 3)];
if (state==NSOrderedSame) {
     NSLog(@”相等”);
}
1.7.2//寻找字符
NSRange range=[str1 rangeOfString:str2];
NSLog(@”%ld”,range.location);//位置
NSLog(@”%ld”,range.length);//长度
1.7.3//抽取字符串
NSString* str=@”abcdefghijklmn”;
str1=[str1 substringFromIndex:0];
NSLog(@”%@”,str1);
//抽取字符串
//抽取到指定下标位置,不包含指定下标
str1=[str1 substringToIndex:5];
NSLog(@”%@”,str1);
//抽取字符串1,3位置
NSRange rage={1,3};
str1=[str1 substringWithRange:rage];
NSLog(@”%@”,str1);
1.7.4     //字符串——长度
NSString *zifuc=@”中文abcdefg”;
NSInteger length=[zifuc length];
NSLog(@”%ld”,length);
1.7.5//字符串——取出字符
unichar c=[str characterAtIndex:0];
NSLog(@”%c”,c);
unichar d=[zifuc characterAtIndex:0];
//欲打出中文字符,占位符C需大写
//oc的字符串都是以utf-8存入内存,可能一会一个字节,一会两个字节
//所以打印的时候要用unincode打印以两个字节的方式
NSLog(@”%C”,d);
1.7.6// 字符大小写转换
zifuc=[zifuc uppercaseString];
NSLog(@”%@”,zifuc);
zifuc=[zifuc lowercaseString];
NSLog(@”%@”,zifuc);
//首字母大写
zifuc=[zifuc capitalizedString];
NSLog(@”%@”,zifuc);
1.8     OC字符串续篇
//OC字符串转C字符串
char *s=[str UTF8String];
NSLog(@”%s\n%p\n%p”,s,s,str);
NSString *str3=@”10.2″;
//OC字符串转数字,遇到字符就不管了, a10 和 10a
int num1=[str3 intValue];
float num2=[str3 floatValue];
NSLog(@”%d %f”,num1,num2);
//检测是否有前缀,后缀
//www.baidu.com
NSString *str4=@”www.baidu.com”;
//前缀
BOOL rec=[str4 hasPrefix:@"www"];
//后缀
BOOL dec=[str4 hasSuffix:@".com"];
NSLog(@”%d %d”,rec,dec);
1.8.1     可变字符串
可变字符串是不可变字符串的子类
所以,不可变字符串能用的东西,可变字符串都可用
可变字符串就是在不可变字符串的基础上增删改
1.8.2     //增删改
1.8.3     //增加——追加
[str1 appendString:@"增加新的字符"];
[str1 appendFormat:@"%d",123];
1.8.4     //增加——插入
//往指定的索引插入字符串
[str1 insertString:@" 二次增加 "atIndex:5];
1.8.5     //删除
//从哪个下标开始,删几个字节,删除指定范围内的字符
[str1 deleteCharactersInRange:NSMakeRange(1, 1)];
//字符串设置字符串,将字符串设置成另一个字符串,类似于替换
//[str1 setString:@"1"];
1.8.6     //改
//替换某个范围的字符串
[str1 replaceCharactersInRange:NSMakeRange(0, 5) withString:@"haha"];
1.9     类/对象打印
Person *p=[[Personalloc]init];
NSLog(@”%@ %@”,p,[Persondescription]);
-(NSString*)description{
   return [NSStringstringWithFormat:@"%@的年龄为%d",_name,_age];
};
+(NSString*)description{
   return [NSStringstringWithFormat:@"这个是处理类的"];
}
1.10数值类
NSNumber *a=[[NSNumberalloc]initWithInt:100];
NSString *str=[a stringValue];
NSNumber *b=[NSNumbernumberWithInt:[str intValue]];
NSLog(@”%@ %@ %@”,a,str,b);
1.10.1数组
//数组
//一些相关类型的数据有序的排列构成的集合
//在oc中常用的集合:数组,字典,NSSet
//数组用来存储对象,但是本质上oc上的数组存储的全都是对象指针
NSArray * array=[[NSArrayalloc]initWithObjects:@”你好”,nil];
NSLog(@”%@”,[array objectAtIndex:0]);
1.10.2数组变化
//数组
//一些相关类型的数据有序的排列构成的集合
//在oc中常用的集合:数组,字典,NSSet
//数组用来存储对象,但是本质上oc上的数组存储的全都是对象指针
NSArray * array=[[NSArray alloc]initWithObjects:@”A”,@”V”,@”3″,nil];
NSLog(@”%@”,[array objectAtIndex:0]);
//可变数组
//输入的顺序就是数组的顺序
NSMutableArray *arr1=[[NSMutableArray alloc]initWithArray:array copyItems:YES];
//%@可以输出任意对象,因为description方法
NSLog(@”%@”,arr1);
//用数组之间初始化新的数组
NSMutableArray *arr2=[[NSMutableArray alloc]initWithArray:arr1];
NSLog(@”%@”,arr2);
//不可变数组
NSArray *arr3=[NSArray arrayWithObjects:@"A",@"B",@"C",@"D",@"E",@"F", nil];
//2.0新特性
NSArray *arr4=@[@1,@2,@3];
NSLog(@”%@”,arr4);
NSArray *array=[NSArrayarrayWithObjects:@1,@1,@3, nil];
//输出元素个数
NSLog(@”%ld”,[array count]);
1.10.3获取元素
//获取制定索引对应的元素
id temp=[array objectAtIndex:0];
NSLog(@”%@”,[temp class]);
//检测对象是否是某一个类或者其子类的对象
if ([temp isKindOfClass:[NSNumberclass]]) {
     NSLog(@”这是数值对象”);
}
if ([temp isMemberOfClass:[NSObjectclass]]) {
      NSLog(@”这是其子类”);
}
//获取对象对应的索引
NSInteger i=[array indexOfObject:@1];
//在指定范围内所搜对应的对象索引
NSInteger j=[array indexOfObject:@1inRange:NSMakeRange(1,2)];
if (i!=NSNotFound) {
           NSLog(@”%ld”,j);
}
//判断数组有没有包含对象
if ([array containsObject:@1]) {
           NSLog(@”它是包含对象的”);
}
else{
     NSLog(@”它没有包含对象”);
}
//获取第一个或最后一个元素
id first=[array firstObject];
id last=[array lastObject];
NSLog(@”%@ %@”,first,last);
1.10.4切割字符
1.10.4.1    //拼接和切割字符串成数组
NSString* str=@”users/qianfeng/desktop”;
NSArray *arr_str=[str componentsSeparatedByString:@"/"];
1.10.4.2    //只要出现的字符就会切割
NSArray *arr_str_2=[str componentsSeparatedByCharactersInSet:[NSCharacterSetcharacterSetWithCharactersInString:@"tes"]];
NSLog(@”%@”,arr_str_2);
1.10.4.3    //拼接字符
NSArray *arr_2=[[NSArrayalloc]initWithObjects:@”users”,@”qianfeng”,@”desktop”, nil];
NSString* arr_str_3=[arr_2 componentsJoinedByString:@"/"];
NSLog(@”%@”,arr_str_3);
1.10.5数组遍历
NSArray *array=[NSArrayarrayWithObjects:@1,@2, nil];
1.10.5.1     //使用for循环遍历
for (int i=0; i<[array count]; i++) {
      NSLog(@”%@”,[array objectAtIndex:i]);
}
//逆序遍历的适合用NSInteger接受i
//删除元素逆序遍历
for (NSInteger i=[array count]-1;i>=0; i–) {
}
1.10.5.2     //for in 结构
//不要改变数组元素的个数,否则会崩溃,因为越界
for (id obj in array) {
      //NSLog(@”%@”,obj);
}
1.10.5.3     //枚举器
//把对象枚举出来
NSEnumerator *enumerator=[array objectEnumerator];
id obj=nil;
while (obj=[enumerator nextObject]) {
      NSLog(@”1—-%@”,obj);
}
//枚举器只能用一次,指针就会移出对象外
while (obj=[enumerator nextObject]) {
      NSLog(@”2—-%@”,obj);
}
//枚举器逆序遍历
NSEnumerator *resenumerator=[array reverseObjectEnumerator];
while (obj=[resenumerator nextObject]) {
      NSLog(@”3—–%@”,obj);
}
1.11子数组
//从数组中取出一段范围内的子数组
NSArray *array=[NSArrayarrayWithObjects:@1,@2,@3,@4,@5, nil];
NSIndexSet *set =[NSIndexSetindexSetWithIndexesInRange:NSMakeRange(0, 2)];
//可变下标集合
NSMutableIndexSet *set2=[NSMutableIndexSetindexSetWithIndexesInRange:NSMakeRange(1, 2)];
[set2 addIndex:0];
NSArray *t1=[array objectsAtIndexes:set];
NSArray *t2=[array objectsAtIndexes:set2];
NSLog(@”%@ %@”,t1,t2);
1.12可变数组
//可变数组,是在不可变的基础上增加了增删改
//而且可变数组只是赋值一个新的对象划分空间
NSMutableArray *array1=[[NSMutableArrayalloc]init];
//增加
//增加一个元素
[array1 addObject:@"1"];
NSLog(@”%p”,array1);
[array1 addObject:@"2"];
NSLog(@”%p”,array1);
[array1 addObject:@"3"];
NSLog(@”%p”,array1);
[array1 addObject:@"4"];
NSLog(@”%p”,array1);
[array1 addObject:@"5"];
NSLog(@”%p”,array1);
[array1 addObject:@"6"];
NSLog(@”%p”,array1);
[array1 addObject:@"7"];
NSLog(@”%p”,array1);
[array1 addObject:@"8"];
NSLog(@”%p”,array1);
//把一个数组元素增到另一个元素,接到尾巴上面
NSMutableArray *array2=[NSMutableArrayarrayWithObjects:@"1",@"2", nil];
[array1 addObjectsFromArray:array2];
NSLog(@”%@ %@”,array1,array2);
//插入到指定索引
//NSMutableString *tete=[NSMutableString stringWithFormat:@"haha"];
[array1 insertObject:@"99"atIndex:7];
//删除
//删除指定对象
[array1 removeObject:@"99"];
//删除指定范围的指定对象
[array1 removeObject:@"99"inRange:NSMakeRange(1, 7)];
//删除最后一个元素
[array2 removeLastObject];
//清空所有数组
[array2 removeAllObjects];
//修改,替换
[array1 replaceObjectAtIndex:1withObject:@"9"];
NSIndexSet *set1=[NSIndexSetindexSetWithIndexesInRange:NSMakeRange(0, 2)];
[array1replaceObjectsAtIndexes:set1withObjects:[NSArrayarrayWithObjects:@"8",@"10",nil]];
1.13数组排序
1.13.1      //数组排序
//自己实现的数组排序
1.13.2      //第一种排序,数字排序
       NSMutableArray *array=[NSMutableArrayarray];
       for (int i=0; i<10; i++) {
           NSNumber *number=[NSNumbernumberWithInteger:arc4random_uniform(50)];
           [array addObject:number];
       }
       for (int i=0; i<[array count]-1; i++) {
           for (int j=i+1; j<[array count]; j++) {
              if ([(NSNumber*)array[i] integerValue]>[(NSNumber*)array[j] integerValue]) {
                   //数组内元素交换
                   [array exchangeObjectAtIndex:i withObjectAtIndex:j];
               }
           }
       }
       NSLog(@”%@”,array);
1.13.3      //第二种排序字符比较
       for (int i=0; i<[array count]-1; i++) {
           for (int j=i+1; j<[array count]; j++) {
               //字符串比较大小是比较第一个不相同的字母的ASCII码值
               if ([(NSString *)array[i] compare:array[j]]==NSOrderedAscending) {
                   [array exchangeObjectAtIndex:i withObjectAtIndex:j];
               }
           }
       }
1.13.4      //第三种排序选择器 使用集合元素自身的方法
//通过方法签名来实现自己循环并筛选
//[array sortUsingComparator:@selector(sortWith:)];
1.14选择器
选择器就是一个容器,用来帮你存储消息
他的作用是判断对象是否实现了某一个方法
//容器,用来存储消息
SEL func=@selector(test1);
Person *p=[[Personalloc]initWithAge:100andSetName:@”张三”];
//对象响应选择器存储消息
[p performSelector:@selector(test1)];
[p performSelector:@selector(test2:) withObject:[NSNumbernumberWithInt:100]];
[p performSelector:@selector(test3:andTo:) withObject:[NSNumbernumberWithInt:1] withObject:[NSNumbernumberWithInt:2]];
//如果传多个值,可以使用包装类然后包装类存数据,传送包装类
//选择器的作用就是可以看对象是否实现这个函数,如果实现这个函数,则执行否则不执行
//respondstoSelector 检测是否有实现这个函数
   if ([p respondsToSelector:@selector(test1111)]) {
//如果有实现就直接执行这个函数
     [p performSelector:@selector(test3:andTo:) withObject:[NSNumbernumberWithInt:1] withObject:[NSNumbernumberWithInt:2]];
   }
1.15字典
//也是一个集合,存储键值对
//key————value 哈希
NSDictionary *ziDian=[NSDictionarydictionaryWithObject:@"123123"forKey:@1];
//哈希算法,一个键直接找到一个值(数据),一一对应
//键必须是单独不重复,值可以重复
NSLog(@”%@”,[ziDian objectForKey:@1]);
1:无序排列
2:值可以重复,键不能重复
3:键可以都指向同一个值
//字典创建
NSDictionary*ziDian2=[[NSDictionaryalloc]initWithObjectsAndKeys:@”one”,@1,@”two”,@2, nil];
//键值对放一起,值在前,键在后
NSArray * keys=@[@1,@2,@3];
NSArray * values=@[@"one",@"two",@"three"];
NSDictionary *ziDian3=[[NSDictionaryalloc]initWithObjects:values forKeys:keys]; //数组中值扯进字典
NSLog(@”%@”,ziDian2);
NSLog(@”%@”,ziDian3);
//2.O新特性
NSDictionary *dic4=@{@”one”:@1,@”two”:@2};
1.15.1字典常用方法
NSDictionary *dic8=@{@”one”:@1,@”two”:@2,@”three”:@3,@”four”:@4,@”five”:@5,@”six”:@6,@”seven”:@7,@”two”:@2};
//获取键值对的个数
NSLog(@”%ld”,[dic8 count]);
//找key对应的value
NSLog(@”%@”,[dic8 objectForKey:@"two"]);
//获取值对应的键,只要符合这个object的key全都取出来,并存在数组中
NSArray *keys=[dic8 allKeysForObject:@2];
NSLog(@”%@”,keys);
//获取所有值
NSArray *values=[dic8 allValues];
//获取所有键
NSArray *keys2=[dic8 allKeys];
NSLog(@”%@ %@”,values,keys2);
1.15.2字典遍历
//字典遍历
for (id obj in dic8) {
      NSLog(@”%@”,obj);//只是取到了键
       NSLog(@”%@”,[dic8 objectForKey:obj]);//通过键取值
}
//枚举器
NSEnumerator *ator1=[dic8 objectEnumerator];
id obj1=nil;
while (obj1=[ator1 nextObject]) {
       NSLog(@”%@”,obj1);//取到object
}
1.16可变字典
NSDictionary *dic8=@{@”one”:@1,@”two”:@2,@”three”:@3,@”four”:@4,@”five”:@5,@”six”:@6,@”seven”:@7,@”two”:@2};
   NSMutableDictionary *dic9=[[NSMutableDictionaryalloc]initWithDictionary:dic8];
   //增 / 改
//增和改是同一个,如果key不存在就增加,否则就修改value的值
   [dic9 setObject:@10forKey:@"ten"];
   NSLog(@”%@”,dic9);
//删
[dic9 removeObjectForKey:@"ten"];
[dic9 removeObjectsForKeys:@[@"one",@"two",@"three",@"six",@"four"]];
1.17继承
接受了前人的精神或者是物品
父类
综合了子类所有的公有特征
father class super class
子类
继承父类的属性和方法
派生出自己独有的特征
subclass 派生类
父类的自身属性在子类也有但是要重新赋值定义
在调用方法的时候会先到子类查找方法,如果子类没有实现,则去父类查找,直到最后基类中也没有,就报错
优点:
1:大量创建相似的类,可以用继承,提高代码复用率
2:统一接口,衍生多态产生
3:当第三方类库或者系统类,有些不适用,可以直接继承,在父类实现一些我们添加的接口或者属性(最常用)
继承可以继承下父类的协议
@CLASS 头文件
#import “Animation.h”
@classDog;
@interface Cat : Animation
{
   Dog *dog;
}
@end
#IMPORT 实现文件
#import “Cat.h”
#import “Dog.h”
@implementation Cat
-(void)test{
   [_dogbark];
}
@end
1.18方法重写
当父类的方法不再适用子类的时候,可以重写父类的方法
当子类的方法是在父类的基础上添加一些操作的时候,可以先重写,然后在子类方法中调用父类的流程
子类也可以增加增加的属性和方法
1.19多态
同一个接口,不同的实现方式
有点像方法的重写
父类的指针指向子类的对象————赋值兼容原则
然后父类指针调用一个方法功能接口呈现出多个样式(各个子类重写)的功能
但是父类指针不能调用子类新增的方法体
父类作为消息接收者接收了一个子类新增的方法体
编译会错误,父类不能调用子类的方法体
编译的时候显然会看到父类调用了一个不存在的方法体,所以报错
但是实际上,父类指针调用子类方法的时候还是可以的
在运行的时候,父类会先从子类寻找isa中合适的方法体
1.20类簇_不能继承的类
NSString,NSArray,NSDictionary,NSSet,NSNumber
这些类是有工厂设计模式
返回的不是NSString而是返回抽象类,具有的方法体由子类实现
OC 继承是单继承
1.21自动SetGet,点语法
点语法,点语法本质上是在发送消息
//p.name=@”1” 与 p setName:@”1” 相等
  1. name=@”hello”;
[p setName:@"hello"];
//点语法会把p.gee 改成 p SetGee
//” = ” 改成 ” : “
//后面带参数
@property
声明一个Get / Set方法
//老版本必须写,新版本可以不写
@synthesize name=_name;
//synthesize 会自动实现setter getter方法
//synthesize还会帮你检测是否写了setter和getter
//如果有写,系统就不会去实现了
synthesize 如果没有=符号@synthesize name=_name;
就先回去找实例变量中有没有变量是不带下划线的,如果有就直接用,且不创建带下划线的变量
如果没有的话会创建带下划线的变量
1.22nonationmic,copy关键字
nonatomic //非原子操作
natomic //原子操作
单线程/多线程操作
copy   // 拷贝副本到资源
NSMutableString *str=[[NSMutableStringalloc]initWithString:@”哈哈”];
Person *p1=[[Personalloc]init];
  1. name=str;
[str appendFormat:@"啦啦啦"];
NSLog(@”%@”,str);
NSLog(@”%@”,p1.name);
外面的资源改变没有copy修饰的实例变量也会改变,只要这个实例变量是可变类型的NSMutable的,都要copy的修饰符
1.23assign / retain
@property (nonatomic,retain) NSArray * nameArray;
//retain 计时器加1 只要是对象,都用retain
@property (nonatomic,assign) int num;
//只要不是对象的类型,都是数据类型,那不用指针去指向变量,不涉及内存问题都用assign
1.24类别 作用   —— 扩展类
@interface原类(类别名)
@end
@implementation原类(类别名)
@end
不需要继承类
@interface NSString (MyString)
-(NSInteger)numberForWord;
@end
@implementation NSString (MyString)
-(NSInteger)numberForWord
{
   int sum=1;
   for (int i=0; i<self.length; i++) {
       unichar temp=[selfcharacterAtIndex:i];
       if (!((temp>=’a’&&temp<’z’)||(temp>=’A’&&temp<=’Z’)||(temp>=’0′&&temp<=’9′))) {
           sum++;
       }
   }
   return sum;
}
@end
1:可以在类别中添加新的方法
2:这些方法可以是实例方法
3:在类别中可以访问实例变量
4:可以在类别中使用原来的类的方法
5:方法和实力变量是必须是声明在接口部分,才可以直接在类别里访问
6:类别里面只能添加方法,不能添加属性
7:想要使用类别的方法,就得import类别扩展文件
8:如果想要类别被重复利用,那么要分文件实现,在使用的适合直接包含类别头文件
9:如果类别中重写了原类/或者父类的方法,那么优先类别中的方法
10:子类可以调用父类中类别的方法,主要能加载到这份父类+类别的文件
类别中的方法优先级: 类别>原来>父类
三个主要作用
扩展类
多人分工协作
自己管理自己方法整理
1.25匿名类别
内部声明
可以声明实例变量
1.26类中间的复合/继承关系
子类调用super父类的方法时,其父类中的self表示的是子类的对象值
在继承中,方法的查找,首先从当前类开始查找,找到则调用,没有则去父类找,直到根类都没有找到该方法,则报错
多态父类的指针可以指向子类对象
赋值兼容规则 -> 可以用父类的指针指向子类的对象
会先指针指向的类型调用相应的方法
复合 ——>包含 has a
继承 ——> is a
1.27内存管理
内存管理,是保证自己在内存堆上分配的内存空间能够得到正确的释放
1.27.1内存管理容易出现的问题
1:内存忘了释放,在堆上忘了释放【内存泄露】
2:在内存释放后继续使用指针【内存提前释放】
3:在释放内存后,再次释放。【重复释放】
1.27.2内存管理的困境
1:在C中,我们释放内存之前,得确保内存不会被再次使用,避免内存提前释放
2:我们释放内存之前,得确保内存没有被释放,避免内存重复释放
3:在模块化使用内存时,内存到底该由哪个模块负责释放,不好确定
4:在多线程中,多个线程使用的内存,无法确定由哪个线程来释放
一般引用计数都是在此变量指针赋值给新的指针才会增1
减去引用计数都是在新的指针使用完毕不使用的适合才会减1
1.27.3OC的内存管理
保证对象的引用计数增加和减少的次数的相同
设置在工程中使用手动内存管理
关掉ARC自动管理计数
1.27.4内存管理黄金法则
1:使用alloc,new,copy(copy开头的方法),mutableCopy开头的方法,创建的对象,必须用release或者autoRelease来释放;
2:通过retain使用对象也需要用release或者autorelease来释放
3:谁写alloc,谁写release,谁创建谁释放,谁污染谁治理
4:
NSString *str=@”我是字符串”;
NSString *str1=[[NSStringalloc]initWithFormat:@”我是字符串”];
NSString *str2=[[NSStringalloc]initWithUTF8String:”我是字符串”];
//在这里,alloc并没有真正的分配内存,系统优化
NSString *str3=[[NSStringalloc]initWithString:str];
NSLog(@”%p %p %p %p”,str,str1,str2,str3);
NSLog(@”%ld %ld %ld %ld”,[str retainCount],[str1 retainCount],[str2 retainCount],[str3 retainCount]);
//按照内存管理黄金法则,只要alloc就得release
//在str3,alloc并没有真的内存分配了,实际指向的是str常用的字符串对象,由于对常量对象retain和release不会影响该对象,为了内存管理黄金法则的统一,我们不需要考虑alloc出来的对象是否在内存上分配,只需要按照内存管理的黄金法则来处理
[str1 release];
[str2 release];
[str3 release];
NSLog(@”str3 retainCount=%ld”,[str3 retainCount]);
1.27.5引用计数在数组中
Dog *dog=[[Dogalloc]init];
NSLog(@”[dog retainCount=%ld]“,[dog retainCount]); //1
//数组对象会自动将加入其中的对象的引用计数加1
NSArray*array=[[NSArrayalloc]initWithObjects:dog,dog,dog,dog,nil];
NSLog(@”[dog retainCount=%ld]“,[dog retainCount]);   //5
//数组在释放的适合,会自动对数组中的每一个对象调用release方法
[array release];
NSLog(@”[dog retainCount]=%ld”,[dog retainCount]); //1
当对象调用release方法,引用计数变成0的时候,dealloc才会被调用
(如果调用dealloc后retainCount还为1,此为非法操作,此对象的指针已经不能用了)
dealloc才是真正释放对象内存的方法
1.28initWith和dealloc
只要我们在initWith方法中创建了对象,则需要重写dealloc方法,保证初始化时创建的对象,在当前对象销毁时也会被正确地释放
@classTest;
@interface Dog : NSObject
{
   @public
   Test *_t;
}
@property(nonatomic,copy) NSString* name;
@end
- (instancetype)initWithTest:(Test*)t
{
   self = [superinit];
   if (self) {
       _t=t;
   }
   returnself;
}
-(void)dealloc{
   NSLog(@”程序销毁了啊!”);
   [_trelease];
   [superdealloc];
}
1.29Setter调整内存
1:
if (_songInfo!=songinfo) {
       [_songInfo release];
       _songInfo=[songinfo retain];
}
在initWith的时候创建对象,然后在dealloc中释放对象,但是在set成员变量的时候将对象换了
就得使用上面的方法
之所有有判断
就是为了防止set函数进来的值和原来的值一致
1.30成员属性retain修饰符
//retain修饰的属性生成的setter和getter方法会和我们写的一致
@property(nonatomic,retain) LrcSongInfo *songInfo;
[lrc setSongInfo:songInfo];
//property 系统生成setter的方法和我们手动写的一样
//   if (_songInfo!=songinfo) {
//       [_songInfo release];
//       _songInfo=[songinfo retain];
//   }
1.31成员属性 assign修饰符
//assign修饰的只是简单的赋值
@property(nonatomic,assign) LrcSongInfo *assignInfo;
-(LrcSongInfo *)assignInfo{
   return_assignInfo;
}
-(LrcSongInfo*)songInfo{
   return_songInfo;
}
1.32成员属性Copy修饰符
-(void)setCopyInfo:(NSString *)copyInfo{
   if (_copyInfo!=copyInfo) {
       [_copyInforelease];
       _copyInfo=[copyInfo copy];
   }
}
1.33autoRelease自动释放池
#import “Cat.h”
int main(){
   //自动释放池,会自动dia释放池中销毁的时候,保存在池中的每个对象调用一次release方法
   @autoreleasepool {
       Cat *cat=[[Catalloc]init];
       //autorelease 会自动将对象添加到离他最近的自动释放池中,由自动释放池来对dog对象进行释放
       [cat autorelease];
   }
}
一旦对象autorelease 就会在离它最近的自动释放池起效果
从类方法产生出来的不需要去释放,因为不是alloc,new,copy的
通过类方法实现,交由自动释放池去释放所以不需要去手动释放
+(instancetype)cat{
   return [[[Catalloc]init]autorelease];
}
在类方法中,创建了对象,按照内存管理的黄金法则,必须释放内存,但是在这,如果释放了内存,这个方法就无法创建对象,为了解决这个问题,引入了autorelease,将类方法中创建的对象交给离他最近的自动释放池释放
1.34数组中内存
NSArray *array=@[@1,@2,@3,@4,@5];//此种也是类方法创建
//而且array release的时候会对数组每个元素release
如果从数组中取出的数据,你想长时间使用,必须自己retain
使用结束后进行release
//可变数组调用copy方法,对可变数组调用copy方法可以取出可变属性
对可变数组用copy,其结果也是赋值了新的副本类似于不可变的mutableCopy
1.35协议
协议和java接口类似
通过声明简单的方法列表来发布接口
任何想要实现协议的类,都可以去遵循
协议的作用:
1:用来声明方法列表,规范接口
2:声明对象方法的同时,隐藏类的接口
3:捕获与继承无关的相似接口
在协议中只需声明方法列表
协议中方法的调用,是通过遵循协议的类的对象发送消息的方式来调用的
OC没有多继承,但是实现多协议可以模仿多继承功能
类如果遵循协议,就相当于继承了协议中的方法
1.36协议注意点
小括号是类别,尖括号是协议
遵循协议的泪必须实现协议中的方法
在协议中的方法,在头文件中可以不声明
协议中不能声明字段,只生成方法,也没有实现;
@protocol IsOlderThanIbjectPortocol <NSObject>
@propertyNSInteger age; // 这里不生成字段,只生成setter和getter方法
@end
//id obj;obj 可以是任意对象
//id <协议名> obj 必须是遵循协议的对象,也就是obj所属的类必须遵循协议名指定的类。
//通过遵循协议的类创建的对象,就是遵循协议的对象
//Obj只能调用协议中的方法
id<协议> obj=[[ 类 alloc]init];
obj只能调用协议里面的方法不能使用类的方法
在required后面的方法,遵循协议的类必须实现
默认情况下,协议中的方法必须实现
在optional后面的方法,遵循协议的类可实现或者不实现
默认情况下,协议中的方法可以不实现
可以用respondsToSelector去验证,然后用performSelector去实现
1.37代理delegate
就是代理类
1.38   对象归档/解挡
//归档(序列化) 将对象转换成二进制数据保存到文件中的过程
//解挡(反序列化) 读取文件中数组转换成自己的对象的过程
归档
1:将对象转换为二进制数据
2:将数据写入文件中
//归档
//将对象转换为二进制数据
NSString *str=@”abcdefg”;
//NSData 对象用来存放二进制数据
//将字符串按照UTF-8的编码格式转换为二进制数据
NSData *data=[str dataUsingEncoding:NSUTF8StringEncoding];
//将数据写入文件中
//参数1:文件名(全路径)
//参数2:是否是原子操作保证写入的操作是原子操作,在data写入完成之前,其他线程或者资源不可能操作这个文件
[data writeToFile:@"/Users/qianfeng/Desktop/课程/day24/test.txt"atomically:YES];
//解挡
//从文件当中读取数据
NSData *data2=[NSDatadataWithContentsOfFile:@"/Users/qianfeng/Desktop/课程/day24/test.txt"];
NSLog(@”%@——->%s”,data2,data2.bytes);
//将二进制数据转换成字符串
NSString *str1=[[NSStringalloc]initWithData:data2 encoding:NSUTF8S
1.39自建类实现归档/解挡
自己创建的类要实现归档和接档必须要遵循NSCoding协议
实现协议的方法
系统的类基本上遵循了该协议
如果想要对数组进行归档解档,数组中的每一个对象都必须遵循NSCoding协议
//归档
NSArray *array = @[@"one", @"two", @"three"];
//NSKeyedArchiver 归档类
// 将对象转换成二进制的数据。
NSData *data = [NSKeyedArchiverarchivedDataWithRootObject:array];
if (data != nil) {
   // 把数据写入到文件
   BOOL res = [data writeToFile:@"/Users/zsq/Desktop/test.data"atomically:YES];
   if (res) {
       NSLog(@”写入文件成功”);
   }
   else
   {
       NSLog(@”写入文件失败”);
   }
}
else
{
   NSLog(@”归档失败”);
}
// 解档
NSData *data1 = [NSDatadataWithContentsOfFile:@"/Users/zsq/Desktop/test.data"];
//NSKeyedUnarchiver 解档类,将二进制数据解析成原始的数据
NSArray *array1 = [NSKeyedUnarchiverunarchiveObjectWithData:data1];
//       [array1 writeToFile:<#(NSString *)#> atomically:<#(BOOL)#>]
//       NSArray *array1 =   [NSArray arrayWithContentsOfFile:@"/Users/zsq/Desktop/test.data"];
NSLog(@”array1 = %@”, array1);
//日期格式化工具
NSDateFormatter *formatter=[[NSDateFormatteralloc]init];
//设置日期的风格
  1. dateStyle=NSDateFormatterFullStyle;
NSLog(@”%@”,[formatter stringFromDate:[NSDatedate]]);
//自定义日期输出风格
  1. dateFormat=@”yyyy年MM月dd日 h时:m分:s秒”;
1.40NSValue
int a[5]={1,2,3,4,5};
//参数1:保存的数组的首地址
//参数2:oc的数据类型
NSValue *value=[[NSValuealloc]initWithBytes:a objCType:@encode(int[5])];
//@encode(type name)会自动将参数的类型转换成oc的类型
NSLog(@”%s”,@encode(int[5]));
NSLog(@”%s”,@encode(int));
NSLog(@”%s”,@encode(char));
NSLog(@”%s”,@encode(float));
NSLog(@”%s”,@encode(double));
NSLog(@”%s”,@encode(ST_STUDENT));
NSLog(@”%@”,value);
int b[5];
[value getValue:b];
for (int i=0; i<5; i++) {
NSLog(@”%d”,b[i]);
}
1.41NSNull
用来占位
NSNull *nu=[NSNullnull];
//主要为了用来占位的作用
//NSArray *array=@[@"1",@"2",nil,@"3"];
NSArray *array=@[@"1",@"2",nu,@"3"];
NSLog(@”%p”,nu);
NULL和nil还有Nil以及[NSNull null]的区别
NULL 在C语言的指针中表示空指针
nil 在OC的对象中表示空对象
NSNull null 在oc中起占位作用
Nil 在Class中空的class
1.42工厂类
类簇
NSString *str=@”string”;
NSLog(@”[str class]=%@”,[str class]);
NSString *str1=[[NSStringalloc]initWithUTF8String:”string”];
NSLog(@”[str1 class]=%@”,[str1 class]);
//NSString 类簇中通过不同的方法创建的字符串对象类型不一样
//NSString 不能够被继承自定义的类
1.43NSFileHandler/文件句柄类
NSFileHandle 文件句柄类
文件句柄相当于经纪人,对文件句柄操作就是对文件的操作
//文件句柄就是一个文件操作的代理,对文件句柄操作就是对文件操作
//参数:要操作的文件的路径
//创建一个对路径指定文件的只写的文件句柄
NSFileHandle *write=[NSFileHandlefileHandleForWritingAtPath:@"/Users/qianfeng/Desktop/php/test.data"];
if (write==nil) {
           NSLog(@”文件打开失败”);
}
NSString *str=@”你好啊1″;
NSData *write_Data=[str dataUsingEncoding:NSUTF8StringEncoding];
[write writeData:write_Data];
//如果文件句柄刚打开开始写入,在文件开始的位置写入
//如果不是的话在往文件中写入数据,接着写入会再用来写入的最后面写入
[write writeData:write_Data];
也可以用如下,是下次开启写文件的跳到最后或者指定位置写数据
[write seekToEndOfFile];
[write seekToFileOffset:20];
记得关闭文件句柄
[write closeFile];
读取文件也是,如果打开的文件句柄没有关闭又读取的话那它读取的内容会从上一次读取的结束位置继续开始读取固定长度
//读取文件长度
NSData *read_data=[read readDataOfLength:10];
//读取整个文件
NSData *read_Data=[read readDataToEndOfFile];
NSString *str1=[[NSStringalloc]initWithData:read_data encoding:NSUTF8StringEncoding];
NSString *str2=[[NSStringalloc]initWithData:read_Data encoding:NSUTF8StringEncoding];
NSLog(@”%@   %@”,str1,str2);
清空文件指定长度数据 //读取和写入都可以执行此功能
[read truncateFileAtOffset:10];
1.44单例方法
单例方法都是default,shared,standard等开头,加一个类名
+(instancetype)initDanli{
   //将对象指针放入静态存储区
   //只初始化一次,在编译的时候就初始化了
   staticDog *dog=nil;
}
static 修饰符的变量在编译的时候就初始化了
而不是在第一次调用的时候去执行
static 修饰的变量作用域看写在哪里
写在里面为内部,写在外面为整个文件使用
static修饰的变量,生命周期是整个应用程序
@interface Dog : NSObject
//单例方法保证:用单例方法创建出来的对象永远都是同一个.
//单例方法都是类方法
+(instancetype)defalutDog;
@end
+(instancetype)defalutDog{
   static i=0;
   //将对象指针放入静态存储区
   //只初始化一次,在编译的时候就初始化了
   //static修饰的变量,生命周期是整个应用程序
   //static 修饰的变量作用域看写在哪里,写在里面为内部,写在外面为整个文件
   staticDog *dog=nil;
   if (dog==nil) {
       dog=[[Dogalloc]init];
   }
   return dog;
}
1.45NSFileManger
//单例方法,可以取到管理文件的单例对象
NSFileManager *fm=[NSFileManagerdefaultManager];
1.45.1遍历目录
//参数1:遍历的目录
//参数2:错误的信息
//这个方法会遍历path路径,将该路径下的文件和目录保存到数值中
//如果遍历失败,将创建错误信息对象,并让error指针指向该对象
NSError *error=nil;
NSArray *array=[fm contentsOfDirectoryAtPath:PATHerror:&error];
ERROR(error);
for (NSString *files in array) {
           NSLog(@”files = %@”,files);
}
NSLog(@”——————————–”);
//便利目录,深遍历
array=[fm subpathsOfDirectoryAtPath:PATHerror:&error];
for (NSString *files in array) {
           NSLog(@”files = %@”,files);
}
1.45.2有参宏
#define ERROR(error) if(error){\
   NSLog(@”遍历目录错处->%@”,error);\
}
//pathDir stringByAppendingString:
//pathDir stringByAppendingPathComponent: 比上一个方法加字符串的时候会多一个反斜杠\
1.45.3创建目录
//参数1:希望穿件的目录路径
//参数2:YES 如果该目录中间有存在的目录,则不存在的目录也会创建,NO,有不存在的目录,则创建失败
//参数3:创建的目录属性,填nil表示默认属性
//参数4:创建出错的错误信息
NSError *error=nil;
NSFileManager *fm=[NSFileManagerdefaultManager];
BOOL result=[fm createDirectoryAtPath:PATHwithIntermediateDirectories:YESattributes:nilerror:&error];
1.45.4      创建文件
NSString *strPath=[PATH stringByAppendingPathComponent:@"tt.txt"];
NSData *data=[strPath dataUsingEncoding:NSUTF8StringEncoding];
BOOL result1=[fm createFileAtPath:strPath contents:data attributes:nil];
NSLog(@”文件创建 –   %@”,result1?@”创建成功”:@”创建失败”);
if (!result1) {
   NSLog(@”文件创建失败 %s %d”,__FILE__,__LINE__);
}
1.45.5文件属性
NSDictionary *dict=[fm attributesOfItemAtPath:strPath error:&error];
NSLog(@”%@”,dict);
1.45.6删除文件/目录
BOOL rest=[fm removeItemAtPath:strPath error:&error];
NSLog(@”文件删除 — %@”,rest?@”删除成功”:@”删除失败”);
1.45.7拷贝目录和文件
NSString *newPath=[PATH stringByAppendingPathComponent:@"t1"];
BOOL result2=[fm copyItemAtPath:strPath toPath:newPath error:&error];
NSLog(@”文件拷贝   –   %@”,result2?@”拷贝成功”:@”拷贝失败”);
1.45.8移动文件或目录
//移动文件或者目录
NSString *ddPath=newPath;
BOOL restule111=[fm moveItemAtPath:strPath toPath:ddPath error:&error];
1.45.9拷贝
//1:对Foundation下的非容器类,进行Copy
//1.对可变对象进行Copy,会创造新的对象,并且新建的对象是不可变对象
NSMutableString *str1=[NSMutableStringstring];
NSString *str2=[str1 copy];
NSLog(@”%@ %@”,[str1 class],[str2 class]);
//__NSCFString,__NSCFConstantString 0x7fff7c0d53b8 0x7fff7c0e79c0
//对不可变对象进行MutableCopy,会创造出新的对象,并且新建的对象是可变对象
NSString *str3=[NSStringstring];
NSMutableString *str4=[str3 mutableCopy];
NSLog(@”%@ %@”,[str3 class],[str4 class]);
//__NSCFConstantString,__NSCFString 0x7fff7c0d5390 0x1002044b0
//对可变对象进行MutableCopy,会创造新的对象,并且新建的对象是可变对象
NSMutableString *str5=[NSMutableString string];
NSMutableString *str6=[str5 mutableCopy];
NSLog(@”%@ %@ %p %p”,[str5 class],[str6 class],str5,str6);
// __NSCFString __NSCFString
0×100301860 0×100300760
//对不可变对象进行Copy会创造新的对象,并且新建的对象是不可变对象
NSString *str7=[NSStringstring];
NSString *str8=[str7 copy];
NSLog(@”%@ %@ %p %p”,[str7 class],[str8 class],str7,str8);
__NSCFConstantString __NSCFConstantString
0x7fff7a0c17b8 0x7fff7a0c17b8
1.45.10容器类拷贝
不管你如何拷贝,容器可能是新的,但是你容器里面的对象全是原来的
Dog * dog1=[[Dogalloc]init];
Dog * dog2=[[Dogalloc]init];
Dog * dog3=[[Dogalloc]init];
NSMutableArray *arr1=[NSMutableArrayarrayWithObjects:dog1,dog2,dog3, nil];
NSMutableArray *arr2=[arr1 copy]; mutableCopy 也一样
容器类拷贝也会是容器类的对象retainCount +1
NSLog(@”%@ %@ %p %p”,arr1,arr2,arr1,arr2);
(
   “<Dog: 0×100200390>”,
   “<Dog: 0x1002021e0>”,
   “<Dog: 0x1002021f0>”
) (
   “<Dog: 0×100200390>”,
   “<Dog: 0x1002021e0>”,
   “<Dog: 0x1002021f0>”
) 0x100202c70 0x100204d00
1.45.11深赋值/浅赋值
浅拷贝只是指针的拷贝
资源并没有真正的被拷贝
在C中实现深复制
首先要开辟一块与其相同的内存空间
开辟了内存空间后要一个地址一个地址的去拷贝内容
在OC中实现深复制
我们自己创建的类,如果希望可以Copy,那么,必须遵循NSCoping协议,如果希望可以mutableCopy,那么必须遵循NSMutableCoping
1.46NSCoping
@implementation Engineer
-(id)copyWithZone:(NSZone *)zone{
   //在这个方法,应该通过allocWithZone来创建对象
   Engineer *en=[[selfclass]allocWithZone:zone];
  1. name=self.name;
  2. age=self.age;
   return en;
}
@end
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值