题记
越来越能明白为什么年长的人喜欢泡茶.以前我爸泡茶的时候,总叫我品尝,慢慢含着,再吞进去,第二次加水后,再品.3年前学的C语言,当时的我,完全没有被它吸引,只是念于期末考试会不会通过.大学课堂只是一个偌大的教室,放着PPT,堂堂计算机的课堂,仅仅是在一页一页的PPT中度过,迷茫和疑惑永远留在了上级课,为了scanf有没有加&这种问题纠结了一节课.回到寝室,如果不看着谭浩强的书,自己都敲不出代码.过了那么久,突然再次捡起C,尽是满满的感悟,好像遇到了一位老同学一样有亲切感,像品到一口从前不觉清甜的茶,现在我明白,C不仅仅是一种代码,更像一种精神,值得人学习和品味.
-----------喵之纸鸢
一.函数
1.定义:函数是一段具有特定功能的程序段
2.格式:
返回值类型 函数名称(形参列表)
{
函数体;//语句;...
}
3.定义函数4步骤
3.1确定函数名称(*取一个有意义的名称,看一眼能知道函数是干什么的*)
函数是标示符 26个英文字母,数字,_,组成 规范:驼峰命名
3.2.确定形参列表()
3.2.1形参列表的格式(数据类型 变量名称,...)
3.2.2如何确定形参列表?依据以后调用函数时,是否需要传递一些辅助的数据给函数
3.3 确定返回值
3.4.确定返回值类型
3.4.1 return的是什么类型,那么返回值类型就写什么类型
return有两个作用:
1.返回数据给函数调用者
2.结束函数
3.提前结束函数
4.作用
4.1 提高了代码的复用性,代码更简洁
4.2 当需求变更,不用改动很多地方
5.注意点
1?? 函数名称是一种标示符,不能同名(哪怕形参,返回值,返回值类型不同)
2?? 函数调用时,才会给形参开辟存储空间,调用结束,释放函数形参的空间.形参列表中的每一个变量我们称为形式参数,简称形参.调用函数时传入的参数,我们称之为实际参数,简称实参
3?? 如果基本数据类型(char int double float)作为函数的参数,在函数中修改形参的值,(值传递)不会影响到外面实参的值.(址传递)地址会影响
4?? 实参的个数,必须和形参的个数相同
5?? 返回值可以和返回值类型不一样,但如果返回值和返回值类型不一样的时候,系统会对返回值进行自动类型转换(隐式类型转换),将返回值转换为返回值类型
6?? 函数可以有形参也可以没有形参
7?? 函数可以有返回值也可以没有返回值 void 空类型
8?? 如果函数没有返回值,那么可以不写return,写return也不会报错
9?? return后面不可以写任何语句,因为执行不到
?? 函数不能嵌套定义:函数中定义函数
由此可得:C语言是弱语言,不严谨:①函数可以没有返回值类型
②没有写返回值类型,默认是int
6.声明和定义
6.1 声明:调用前告诉系统有这个函数,名称,接收什么参数,返回声明类型的值
声明的写法
int sum(int,int);
6.2 完整的实现了函数的功能的代码,称之为函数的定义(函数的实现)
6.3注意点
1?? C语言规定:一般情况下,如果想要调用一个函数;该函数必须在调用之前定义
2?? 返回值类型一定要写
3?? 函数的声明,只需在函数被使用之前告诉系统即可,可以写在主函数的外面也可以写在函数里面
4?? 不允许重复定义
7.main函数
1??main是函数的名称,是系统给定,系统启动程序就会自动调用名为main的函数
2??return 0;0返回给系统,代表程序正常结束,若返回其他值的代表程序非正常结束
3??main只有系统调用,不能手动调用
4??argc:系统传进来的参数个数 默认是1
argv:就是系统传进来的参数的实际的值 默认的程序的名称
8.递归函数
特点:1??自己调用自己
2??一定要有明确的结束条件
注意:递归要慎用,存在性能问题
应用:遍历文件 阶乘
二.进制(二进制,八进制,十六进制)
十进制:12
二进制:1100(逢2进1)
八进制:14(逢8进1)
十六进制:C(逢16进1) 10-15ABCDEF(大小写都可以)
进1:当前位归0,上一位加1
1.编程中默认是十进制
1.1如何输出十进制? %d %i 以十进制输出数据
1.2如何表示二进制? 在数字前加上0b
1.3如何表示八进制? 在数字前面加0
1.4如何表示十六进制? 在数字前面加0x
1.5如何输出八进制数? 在pirntf中%o
1.6如何输出十六进制数? 在pirntf中%x
注意:在C语言中没有以二进制输出的占位符
2.十进制转换为二进制
规律:十进制数除以2取余数,然后倒序输出余数
3.二进制转换十进制
规律:按位运算 8421....
3.1N位二进制的取值范围
1位:0~1 0~2(1)-1
2位:0~3 0~2(2)-1
3位:0~7 0~2(3)-1
4位:0~15 0~2(4)-1
4.二进制转换为八进制
规律:从后往前分,3个二进制位转换为1个八进制位;将3个二进制位转换为十进制,之后再将所有的结 果连接起来.
eg: 二:1001--->00 001 001
1 1 --->八:11
原理:3位:0~7 0~2(3)-1
5.二进制转换为十六进制
规律:从后往前分,4个二进制位转换为1个八进制位;将4个二进制位转换为十进制,之后再将所有的结 果连接起来.
eg:二:11011---->0001 1011
1 B --->十六:1B
原理:4位:0~15 0~2(4)-1
三.原码反码补码
1.内容:数据在内存中存储的都是二进制,二进制又可分为 原码/反码/补码
其实内存存储在内存中的是 补码
eg:9--> 存储在内存中--->二进制
9是int ==4个字节==1个字节等于8位==整型有32位
0000 0000 0000 0000 0000 0000 0000 1001 (原码) 2.正数的原码=反码=补码(3码合1)
eg:-9 --> 存储在内存中 --> 二进制
1000 0000 0000 0000 0000 0000 0000 1001 (原码)
3.二进制第一位是符号位,0:正数,1:是负数
4.负数反码:符号位不变,其他位取反(0->1,1->0)
注意:反码和取反~是不一样的操作
1111 1111 1111 1111 1111 1111 1111 0110 (反码)
5.负数补码:1??反码+1
2??从右往左,遇到第一个1,第一个1不变,前面的按位取反
1111 1111 1111 1111 1111 1111 1111 0111 (反码)
eg:-15
1000 0000 0000 0000 0000 0000 0000 1111 (原码)
1111 1111 1111 1111 1111 1111 1111 0000 (反码)
1111 1111 1111 1111 1111 1111 1111 0001 (补码)
6.为什么有这3种码? 主要为了方便计算机
6.1由于最高位是符号位,0:正 1:负
6.2若直接存储原码,计算机还要判断符号位,效率低
6.3为方便计算机,有了反码和补码,以后计算机就不用判断最高位,直接计算
ps:计算机只会做加法
7. 原码+原码=原码
反码+反码=反码
补码+补码=补码
8.为了解决原码做减法,出现了反码
9.为了解决0的符号及两个编码的问题,出现了补码
四.位运算符
1.& 按位与:二进制的每一位进行运算
规律:1??一假则假
2??任何数和1&都不变
eg: 9 & 5 =? // 1
1001
&0101
------
0001 = 1
2.| 按位或
规律:1??一真则真
3.^ 按位异或(非常重要)
规律:不同为1,相同为0
一个n位数异或n个1可以取反,因为异或将0->1,1->0
9^5=?
eg: 1001
^0101
------
1100 == 12
重点:1??异或的结果和参与运算的顺序没有关系
9^5^6=5^9^6
2??相同的两数异或等于0
3??任何一个数异或0结果还是本身
4??任何一个数异或同一个数2次,结果不变
4.左移右移
左移:<< 多出来的去掉,少的一位用0补
规律:左边移的数×2的移动次幂
eg: 9左移1次==9×2(1)=18
9左移2次==9×2(2)=36
注意点:
1??左移,移除的最高位会被抛弃,所以可能会改变一个数的正负性
右移:>> 多出来的去掉,少的一位按符号位补位
规律:右移的的数除以2的移动次幂
eg: 9右移1次==9/2(1)=4
9右移2次==9/2(2)=2
注意:负数的左移右移是补码在移动!
5.应用场景:
如果想要某一个数乘以(除以)2的幂,最高效的运算方式就是左移右移
6.二进制最后一位是0则是偶数,是1则为奇数
7.交换两个变量的3种方法
1??定义一个变量temp (引入第三个变量)
2??a=a+b b=a-b a=a-b (不引人第三个变量)
3??a=a^b b=a^b a=a^b (不引人第三个变量)
b=a^b a=a^b b=a^b
8.对用户密码进行加密(异或的性质)
1??对密码异或一个数 加密
2??再对密码异或这个数 解密
(补充)变量的存储(可补充到4月7日总结)
1??变量为什么有类型?
每种类型占用的空间不同 int 4 char 1 double 8
2??只要定义变量,就会为变量开辟存储空间,内存寻址是从大到小
3??越先定义的变量,其内存地址越大
4??变量的地址就是所占存储空间最小的字节地址
注意:由于内存寻址是从大到小,所以存储数据也是从大到小的存储(先存储二进制的高位低位)
5??数组的首址是从分配的最低位开始
五.类型说明符
1.说明长度(用于修改类型所占用的存储空间的大小)
short 2 %hi %hd
long 8 %li %ld
long long 8 %lli %lld
2.说明符号位(它可以用于修改符号位)
unsigned 无符号 %u
signed 有符号
2.1 long
int == 4个字节 == -2(31) ~2(31)-1
long int == 8个字节 == -2(63)~2(63)-1
输出用%li 或 %ld 来输出long类型的数据 (注意:C语言中,不看怎么存,只看怎么取)
2.2 long long
long long在64位变一下,基本没区别,但是如果在32位就有区别
32位下long占4个字节 long long占8个字节
注意:若用long long修饰变量 ,那么输出用%lld %lli
long long int == long long
2.3 short
优化内存空间,存取不到4个字节内容是时,可以用short int == 4个字节 == -2(31) ~2(31)-1
short int == 2个字节 == -2(15)~2(15)-1
注意:在范围内可以用short,但是开发很少用
输出%hi %hd
2.4 signed
如果给变量加上修饰符signed,代表当前变量的取值可以是正数/负数/零,二进制最 高位作为符号位,而且默认情况下所有变量都是有符号的(signed)
2.5 unsigned
代表无符号,无符号就代表当前变量的取值只能是正数/零
如果打印无符号,只能用%u
unsigned表示不把二进制的最高位作为符号位
3.不同类型的说明符可以混合使用
unsigned long int num = 99 ;
printf("num= %lu\n",num);
注意:相同类型的说明符不能同时在一起用!eg:unsigned signed
六.一维数组
1.定义:当需保存很多相同类型的数据时,就可以用数组保存
2.格式: 元素类型 数组名称[数据的个数]
元素类型:一旦指定,数组中只能存储该类型
元素个数:数组中能够存储的(元素)个数,又称索引
3.初始化(二维数组处有具体例子)
格式: 元素名称[元素个数]={, ,..., ,}
完全初始化:全部元素都初始化
部分初始化:没有被初始化的,默认是0或垃圾数字
注意:
1??没有初始化的数组的数据不要随便使用(禁止越界),可能是一段垃圾数据
2??不能用变量初始化 eg:int score[num]={, , };
3??如果定义的同时初始化,那么元素个数可以省略,初始化了几个,元素就是几个
4??如果定义的数组没有初始化,那么就不能省略元素个数
5??可以通过[索引]= 的方式eg:int score[n]=3 对指定元素初始化
6??只能在定义的同时{}进行初始化,如果先定义不能用{}初始化,只能单个赋值,不能整体
4.遍历(打印数组)
1??通常用for循环遍历,次数为数组元素个数
2??遍历数组的时候,尽量不要把遍历的次数写死,要动态计算数组个数
int length=sizeof(scores)/sizeof(score[0]);
注意:score[0]不要写成int类型,因为当类型变了的时候,还需要更改
5.数组的内存
1??数组是从低字节开始存储
2??变量的内存寻址是从大到小,从高字节开始存储
6.数组的地址
value=&value=value[0]
数组名就是数组的地址,就是数组第一个元素的地址
7.注意点
1??数组调用时越界问题
2??在函数的形参中更改数组的某个值不会改变实参的值,可以将数组的地址传进函数再进行操作,则将值传递变成址传递,则可以改变实参的值
3??如果数组作为形参,那么在函数中就不能通过数组的名称计算出元素的个数,因为系统会自动将数组转换为指针,指针占用8个字节
七.排序
无序排序
1.填坑法
例:从键盘输入6个0~9的数字,排序后输出
1??思路:①循环输入6个数字的同时,将这6个数在0~9数组中对应下标的数值标记为1;
若有数字重复出现了N次,标记N为该数的数值
②遍历数组时,外层按循环顺序输出数值内容为1的数,内存循环打印数值>1的 数N次
2??循环次数:外层固定0~9次,内层6次
2.选择排序
1??思路:从第一个元素开始,递增式与其余元素比较,将最值和第一个元素交换,继续下一个元素.....
2??循环次数:(n-1) * (n-2) *.... *(1)
3??形状:尖尖朝下
4??角标变化:12 13.........1n
23 34...2(n-1)
...
(n-1)n
3.冒泡排序
1??思路:相邻两个元素比较大小,将最值沉底(交换到右边),继续比较第二个相邻的2个数...
2??循环次数:(n-1) * (n-2) * .... * (1)
3??形状:尖尖朝下
4??角标变化:12 23 ..........(n-1)n
12 23 ...(n-2)(n-1)
...
12
有序排序
4.折半查找
原理:
1??数组必须是有序的
2??必须已知max和min(知道范围) 关键字key min max mid是角标
3??动态计算mid 取出mid的值进行比较
ps:当min==max时候返回min的值
4??若mid对应的值大于key,那么max=mid-1
5??若mid对应的值小于key,那么min=mid+1
总结排序思路:
1??画出排序的步骤
2??思考循环的形状 尖尖朝下,尖尖朝上or其他
3??打印角标的变化
4??再进行修改优化封装
5.进制查表法
八.二维数组
二维数据:数组中的每个元素又是一个数组,那么这个数组就称之为二维数组
1.格式:
元素类型[一维数组的个数][每个一维数组的元素个数];
元素类型[行][列];
元素类型:二维中每一个一维数组存储什么类型的数据
一维数组的个数:说明二维数组有多少个元素
每个一维数组的元素个数:说明二维数组中每一个一维数组的元素个数
2.取值
printf("%c\n",name[0][0]);
3.遍历
思路:
1??取出二维数组的每一个元素(取出二维数组中的一维数组)
2??遍历一维数组
两个for循环 外层=一维数组的个数 内层=每个一维数组的元素个数
4.初始化
1??定义的同时初始化
char name[2][3]={
{‘d‘,‘x‘,‘w‘},
{‘w‘,‘x‘,‘f‘}
};
2??先定义再初始化
char name[2][3];
char name[0][0]=‘d‘;
char name[0][1]=‘x‘;
char name[0][2]=‘w‘;
char name[1][0]=‘w‘;
char name[1][1]=‘x‘;
char name[1][2]=‘f‘;
3??完全初始化
char name[2][3]={
{‘d‘,‘x‘,‘w‘},
{‘w‘,‘x‘,‘f‘}
};//全部空间都初始化了!
4??不完全初始化
char name[2][3]={
{‘d‘,‘w‘}
};
5??省略元素个数的两种写法
//明确告诉二维数组,我们有2个一位数组
① char name[2][3]={
‘d‘,‘x‘,‘w‘,
‘w‘,‘x‘,‘f‘
};
//没有告诉二维数组 有几个一维数组
//如果在定义的同时初始化,那么一维数组的个数可以省略
系统会自动根据每一个一维数组存的元素推出有几个一维数组
② char name[][3]={
‘d‘,‘x‘,‘w‘,
‘w‘,‘x‘,‘f‘
};
6??错误写法
注意点:每个一维数组的元素个数不能省略
系统搞不清分配多大的空间,哪些数据给第一个数组....
char name[2][]={
‘d‘,‘x‘,‘w‘,
‘w‘,‘x‘,‘f‘
};
5.二维数组和函数
&name=name=name[0]!=num[0][0]
注:以后只要看到函数的参数是一个数组,那么就是地址传递,在函数中修改形参会影响到参数
6.应用
插秧 下棋 游戏
九.字符和字符串(char)
字符 占位符%c或者‘ ‘单引号引起来的
字符串 占位符%s或者" "双引号引起来的
字符串和字符的区别是:字符串保持时比字符多一个\0
注意:输出字符或字符串的时候注意接收键盘的回车或者间隔符!!!
---------字符-----------
1.char 占1个字节
2.表示范围:-2(7)~2(7)-1
3.关于大小写转换
--------字符串----------
1.组成:由字符组成的
2.定义:字符串是由同一种类型的数据组成,且有序,可用数组来存
注意:C语言规定,字符串必须以\0结尾 所以字符串变量的元素比字符数组的元素多一个\0
3.如何输出字符串变量
printf("%s",str);
%s的原理,从传入的"地址"开始逐个取出,直到遇到"\0"为止
4.注意点
char str[]=‘dxw‘;
char str2[]={‘d‘,‘x‘,‘w‘,‘\0‘};
部分初始化,其余元素默认为0 \0的ASCII值为0
char str3[10]={‘d‘,‘x‘,‘w‘};
1??字符串以\0结尾,没有\0就不是字符串
2??只要用双引号括起来的都是字符串
3??字符串的本质是数组
5.常用方法
5.1 如何输出字符串
5.1.1 printf的%s
弊端:想要换行要加\n
优点:可以自定义格式化需要输出字符串
5.1.2 puts()输出(了解)
优点:自动换行
弊端:不可以自定义格式,只能原样输出
5.2 如何输入字符串
5.2.1 scanf %s 不用加&
接收字符时,会以空格 回车 TAB作为结束符!
5.2.2 gets()
弊端:系统提醒,用gets不安全
优点:在字符串中输入空格 tab
5.3 如何计算字符串的长度
在string.h中
//这种写法比sizeof好,简洁,不包括\0
size_t length=strlen(str);printf("%lu")
原理:从传入地址开始逐个取,每取一个计数器+1
6.常用方法2
头文件string.h
注意要给\0留出位置
6.1 strcat连接
strcat(str1,str2);
dest:目标 src:源
将str2里的内容拷贝到str1里面
注意:想要使用dest必须是一个数组,且长度大于拼接之后的长度
6.2 strncat连接
char * == dest const char * == src size_tx需要拼接的个数
strncat(str1,str2,1)
为了避免拼接之后超出str1的存储范围,可以动态计算str2需要拷贝几个到str1中不会超出(空格也算一个位置)
在OC中方便强大,不要考虑容积的问题
6.3 strcpy拷贝
将源数据拷贝到目标中,并且会覆盖原有的数据,所以目标容积要足够
6.4 strncpy
char * == dest const char * == src size_tx需要拷贝的个数
拷贝操作是逐个替换 没有进行操作的部分 不会被覆盖
6.5 strcmp比较
比较完成后会返回值
若该值>0 str1>str2
=0 str1=str2
<0 str1<str2
原理:取出字符串中的每一个进行逐个比较,不相等就不会继续比较
字符串对应的是ASCII表,在进行大小写转换时,建议不要写数字! eg:int temp=‘a‘-‘A‘;
十.指针:只用来保存地址
1.如何定义指针变量
普通变量: 数据类型 变量名称
指针变量: 数据类型 * 变量名称
数据类型:指针变量是什么类型,将来只能保存这个类型变量的地址
* 仅仅为了表示是一个指针变量
变量名称:用于区分不同的变量
eg: int *p; p=&a; &取地址符
1??指针在64位编译器下,占用8个字节
2??指针变量前的 *星号 代表访问指针变量指向的那一刻存储空间
3?? *和& 是逆运算 *p==a(重要) ;
2.指针的一个重要用途:进行址传递,便于数组
3.指针的注意点
1??指针只能保存地址
2??同一变量可由多个指针指向它
3??指针的指向可以修改
4??不要访问野指针
没有赋值的指针 int *p=NULL;
5??指针类型是什么类型,就只能指向什么类型的数据
4.指针为什么要分类型?
mac系统中的指针无论是什么类型,几级真正都占用8个字节,因为mac是64位.(==64/8)
答:我们利用指针去取值时,系统会自动根据指针的类型来确定应该取多少字节
十一.多级指针(很少使用,重在理解原理)
一级指针:*p
二级指针:**p
...
n级指针:(n颗星)p
1.只要是指针,其占内存8个字节
2.指针在定义的时候要使用*的个数来描述其级别,在定义之后出现*都是在调用指针的存储空间所指向的数值!(重要)
3.每个指针自身有地址,但是指针所指向的是定义时候的变量的地址!!
4.指针在函数中当形参时,自身的地址无法传入(函数会给形参分配存储空间,从而不是指针定义时候的地址!),若想确保这个指针形参的地址的正确性,需要传入比其高一级的指针,指向这个充当形参的指针.
十二.unix命令
1.Xcode的运行原理:
1??将代码转换成0和1
2??Mac系统中所有的目录都是文件夹,没有C D盘的概念,所有的东西都存在一个盘里.因为Mac系统是基于unix系统,没有C D盘的概念.ios系统也是基于unix系统.正式因为Mac系统是基于unix的,所以也支持unix指令
桌面创建文件夹相关指令
pwd : 查看当前所在的位置
ls: 查看当前文件夹中的内容
cd: 进入指定目录
mkdir: 新建文件夹
rmdir: 删除文件夹
cd../: 返回上一级
clear: 清空终端
open /文件名 打开一个文件
touch 文件名.类型 创建一个文件
cat 文件名.类型 查看一个文件
自动测试和自动打包时会用到终端
Xcode4之前,Xcode是用GCC编译器来翻译代码
GCC编译器:开源免费的编译器 C/OC/C++/JAVA...
乔布斯开发了Xcode4之后,LLVM(虚拟机 Clang前端就是专门用来编译代码--翻译代码)
cc-c 文件名 翻译我们自己的代码
链接:
cc 文件名 将我们的代码和系统以及其他依赖文件的代码组和在一起
cc-c main.c ---> main.i 替换文件中所有的预处理指令 可编译多个文件 只需要空格隔开
----> main.s 汇编
---> main.0 二进制001001
./文件名称.out 运行文件 ps:要是.out类型
cc main.o --->将我们的二进制和依恋的文件的二进制组和在一起,生成一个可以执行的文件
2.注意:
1??如果只有警告,没有错误,那么clang会帮我们翻译代码;如果有错误,那么不会翻译 clang编译器会帮我们检查警告和错误
2??clang编译器在编译的时候,只会检查语法错误
3??clang编译器只会在链接的时候检查函数没有真正的实现
3.关于include
1?? 声明printf函数
2?? include指令不一定要写在文件的头部
3?? include后面的<>和""的区别
<>会先从开发工具的编译环境中去查找,若没找到的话,会去系统的编译环境中找
""会先从当前文件所在的文件夹查找,若没找到的话,会从开发工具的编译环境中去找,若没找到,会去系统的编译环境中找
4?? include后面不一定要写文件名称,也可以写路径(相对路径/全路径)
5?? 全拷贝的意思
4.多文件开发(重点)
多人开发,大型开发,可以进行分工,独立
头文件:可以在头文件中声明,然后在调用地方直接使用
.h是专门用来被拷贝的,不会参与编译
*快捷键*
control + i 可以调整代码