目录
一、函数
看一个程序:
int Add(int x, int y)
{
int z = x + y;
return z;
}
int main()
{
int num1 = 10;
int num2 = 20;
int sum = 0;
int a = 100;
int b = 200;
sum = Add(num1, num2);
printf("sum = %d\n", sum);
sum = Add(a, b);
printf("sum = %d\n", sum);
return 0;
}
基于之前的一个加法函数做了增添。之前只用了num1, num2,如果想要再算a,b,按照之前的思路,还需要再写代码来一步步算。现在我们可以定义一个加法函数,在程序里使用这个加法函数就可以了。在main之前,我们定义了函数,大括号里的是函数体,小括号里的是函数的参数,int是它的类型。整个过程是,进入main代码块里,引用了add函数,上面的x,y 就分别变为num1, num2。经过相加后,add函数返回z,z里面存放了和的值,然后回来,再一次调用,计算a + b,之后打印sum值。这就是定义一个函数。C语言中有库函数和自定义函数,刚才的add就是自定义函数。
二、数组
数组是一组相同类型元素的集合。看代码:
#include <stdio.h>
int main()
{
int arr[10] = {.......}
return 0;
}
我们也可以定义float,char等类型的数组,只要数组中的元素是同类型,上面的省略号可以自由添加想储存在数组里的数据。接下里具体说说。
int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
当要访问这个数组时,比如要拿出7这个数字用,此时需要注意下标问题,数组是从0开始数的,也就是说现在这个数组里,1的下标是0,2的下标是1,10的下标是9。那么访问这个数组时:
printf("%d\n", arr[5]); 这时打印出的是6。访问数组的方式是用下标访问。现在要打印数组里所有的数字,也就是说0-9全都要打印出来,这里就可以用循环体。
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int i = 0;
while (i < 10)
{
printf("%d\n", arr[i]);
i++;
}
return 0;
这样就能打印出所有了。
三、操作符
算术操作符
+ - * / %
加 减 乘 除 模。模运算是取余数的算术法,叫做取模。int a = 7%3 得出来的数值是1;/是取商,7/3就是2。
移位操作符
>> , <<。分为右移和左移。这个移是移了二进制的位。int a = 1,那么a的表达就为 30个0后01,如果a << 1,a左移1位,也就是把这个二进制数字左移一位,那么结果就是 30个0后10,这里的操作是最左的0去掉,最右边补零,补完之后,int b = a << 1 然后打印b,就会结果是十进制数字2。不过这时候打印a,还是1,因为a移后的值给了b,所以a不动,如果赋值给a,a就会变。
位操作符
& ^ |
这个位仍然是二进制的位,C语言的基础就是二进制。
& 按位与
| 按位或
^ 按位异或
int a = 3;
int b = 5;
int c = a & b;
a 011
b 101
001
&的规则是只要有一个为假,则为假。C语言中0为假,非零为真。与就是并且,所以一个不符合规则则全部不符合。这样的结果就是001。
^的规则是有一个为真即为真。或就是有一个符合条件就行。则结果为111。
|的规则是对应的二进制位相同为0,不同为1。结果就是110。
赋值操作符
= += -= *= /= &= ^= |= >>= <<=
上一篇记录中提到过赋值不是等于。这里都是赋值操作符。a += b以及a = a + b这两种写法完全一样。其他操作符不必多说了。可以这两种都写出来就知道什么意思了。
单目操作符
! 逻辑操作符
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度
~ 对一个数的二进制位取反
-- 前置、后置---
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
对应的双目操作符,三目操作符
!
int a = 10;
int b = 20;
a + b,这个+就是双目操作符,就是两个操作数,所以三目操作符就明白了。继续单目操作符。
int a = 10;
printf("%d\n", a);
printf("%d\n", !a);
打印出结果为10和0。!的作用就看出来了。0为假,非零为真。如果a = 0,那么结果就是0和1,变为真后默认为为1。
sizeof
sizeof,会计算变量/类型所占空间的大小,单位为4字节。
int a = 3;
printf("%d\n", sizeof(a));
此时会计算a所占字节大小,是4。
sizeof(int)
sizeof a
sizeof int
前两个都是4,第三个出错。sizeof也可以算数组大小。
int arr[10] = {0};
sizeof(arr)
结果是10 * sizeof (int) = 40,每个数字都是int类型,总共十个。总结一下。数组数字个数 = 数组总大小 / 每个元素大小。
int sz = 0;
sz = sizeof(arr) / sizeof(arr[0]);
~
按位取反,就是把二进制位取反。
int a = 10;
int b = ~a;
printf("%d\n", b);
~按二进制位取反。具体来看:
a:0000000000000000
按位取反后,就是1111111111111111,数字个数并不是32位,只是要表达这种转换。但是如果打印,结果就不是1111111111111111对应的二进制数字。这里涉及到原码,反码,补码。二进制数字最左边的数字代表正负,1为负,0为正。负数在内存中存储的时候,存储的是二进制的代码。所以此时这个111111111111111是补码,而要打印出来,打印的是原码,所以减一,在取反,符号位不动,就得到1000000000001,这样输出的结果就是-1。原先是多个0是原码。所以一个数据存储在内存中是以原码存的。
--,++
int a = 10;
int b = a++;
printf("a = %d, b = %d\n", a, b);
++在a的后面,是后置,后置的用法是a先赋值给b后,再++;先使用再加。前置++是++a,先加再赋值。这是前置和后置的区别,--也同样。可以看出来,这里的操作只针对这一行,之前的像line++,意思就是先用line做一定的操作,再把它+1,但是这一行并没有操作,所以就直接+1了,这时候++line效果一样。关于强制类型转换符:
int a = 3.14这样打印a必定出错,但是如果(int)3.14,强制转换成整数类型,int a = (int) 3.14这样就没有问题了。
至于*符,之后再说。
关系操作符
>
>=
<
<=
!= 用于测试不等于
== 用于测试等于
逻辑操作符
&& 逻辑与
|| 逻辑或
C语言中0为假,一切非零即为真。
int a = 3;
int b = 5;
int c = a && b;
只有两个都为真,c才会真,输出结果为1。||则是有一个为真则为真。
条件操作符
expr1? expr2:expr3
选择一个表达式,表达式一的结果如果为真,那执行表达式二,表达式二的结果是整个表达式的结果,如果表达式一的结果为假,那表达式三要被执行,表达式三的结果是整个表达式的结果,
int a = 0;
int b = 20;
int max = 0;
max = (a > b ? a : b);
具体例子
if (a > b)
max = a;
else
max = b;
这也是三目操作符。三个操作数。
逗号表达式
表达式之间要用逗号隔开,逗号与下一个表达式之间也要有空格。下标引用、函数调用和结构成员:() [] . ->
int arr[10] = {0};
arr[4]; 这个就是下标操作符。
在之前的加法程序中,调用Add函数时用括号来用函数,这是调用操作符
至于 . 和 ->操作符之后再说, 之前的*和&也会再写。
四、常见关键字
auto,break,case,char,const,continue, default, do, double,else,enum,extern,float,for,goto,if,int,long,register,return,short,signed,sizeof,static,struct,switch,typedf,union,unsigned,void,volatile,while
先写几个
auto:当我们写局部变量时,出了范围就不能再用,进入范围就可以用,是自动的属性。局部变量都是自动变量,在声明之前都隐藏了一个auto,auto int a。在C++11中这个还有奇用。
break、case:在循环中break可以跳出循环,在switch case语句中也有使用break,可自行搜索。
const:常变量
default:是switch case语句的,意为默认。
do:do while循环
enum、extern:枚举常量,引入外部符号,之前写过。
register:运用一段话
你买电脑的时候说,我们这个电脑500个g的硬盘,对吧?这叫硬盘数据可以存到硬盘上,也可以存哪里呢?可以存到内存里边儿去,也可以存哪里呢?哎,还有一些比如说寄存器啊,这样的东西还有什么呢?还有一些空间呢我们叫什么呢?叫高速缓存。在内存和寄存器之间呢我们还有一种叫高速缓存。高速缓存,为什么会有这样一个东西呢?其实这个东西呢是这么一个情况啊,在我们的计算机里边儿可以看到我们的硬盘啊,我们买电脑的时候动不动就500个g的硬盘,对吧?
动不动就500个g的硬盘,但内存呢?你在买电脑的时候有没有说内存500个g啊。很少会去到,我们一般是八个g内存,对吧?8个g内存或者是4个g内存,对吧?如果条件好一点的话,买的电脑的可能是16个g的内存,大概是这么一个情况,对于这是内存啊,内存你会发现内存空间的时候比硬盘要小,比硬盘要小。啊,那如果说给我来500个g的一个内存行不行?哎,那是不行的,为什么不行呢?因为内存它的访问速度是高于硬盘的,所以它的造价就比较高,造价一旦高,那我们太大的空间我们买不起了,否则这个电脑贵的压根儿就没人能买得起了,对不对?三四千,四五千,五六千就买不到一个电脑了,对不对?一个电脑可能哎几万,对吧?啊,几万几十万,对吧?所以一定要注意要在这个地方的内存他的访问速度是高于硬盘的。那在这个地方呢再往上一层呢叫什么呢?叫高速缓存,高速缓存速度呢比我们的内存的还要高,那其实在我们的电脑上的高速缓存啊可能就几个MB,对不对?几十个M它比我们的内存的还要小一些啊,还要小一些,那寄存器比我们的高速缓存的还要快啊,访问速度还要快还要快,那大家注意在这个地方呢那访问速度越快,那它的造价是不是就越高?如果造价越高,那它的一个空间大小在我们的设备上是不是就不敢太多了?这个设备是不是就买的起来太贵了,对不对?所以如果从下往上看访问速度的话,访问速度是由低到高啊,速度是越来越快,但是空间的话由上往下空间是在我们的硬件上是越来越小。
寄存器高速化都存在这个地方。在我们的电脑上还有个东西叫什么叫CPU。CPU的叫什么呢?叫中央处理器,我们要增加这样的理解,对吧?叫中央处理器,那这个中央处理器是干什么的呢?我们要在计算机上进行一系列的运算的时候呢,其实啊我们的计算机里边儿要进行一系列的运算的时候呢,我们其实是怎么做的呢?首先你要从内存里面儿拿数据。这时候还不能用它,你要从内存里面儿拿个数据到哪里去呢?到我们的CPU啊,给我们的CPU,让我们的CPU的进行很好的一个计算啊,从内存里面去拿啊,早期到我们这个计算机出现的时候,早期特别早期的时候,当我们计算机出现的时候呢,内存的访问速度,它和我们的CPU处理数据的速度是比较搭配的,CPU说内存能拿多快我就能处理多快,但是后来随着这个计算机的发展,让我们CPU去处理数据的速度越来越快,越来越快,那这个时候就有一个问题出现了,CPU的处理速度越来越快,但是内存的访问速度却跟不上内存的访问速度却跟不上啊,内存的访问速度呢相对来说还是比较慢的啊,那怎么办呢?有人就想说诶,那内存的访问速度如果比较慢的话,内存的访问速度如果比较慢的话,那CPU的处理速度再快,是不是整体计算机的处理速度也跟不上了。那后来有人想,那是这样吧,我们在计算机里边儿再给一些更快的设备,比如说高速缓存,再比如说寄存器。
好,这个时候呢我们达成了这样一个想法,说未来你的CPU处理的数据,你去哪里拿?你去寄存器里边儿去拿你的数据,这样速度是不是更快了。因为寄存器的访问速度更快嘛,我去寄存器里去拿,那这个时候访问速度更快了。但是呢我们说寄存器的没数据啊,那怎么办呢?先让内存的数据代表高速缓存,高速缓存的数据加到内存里边儿去,然后我们CPU在从这个寄存器里边儿去拿数据,当我们有一次在寄存器怎么没有拿到我们想要的一个数据的时候,我再向下去访问,我再去高速缓存和内存去访问,所以其实我们每次CPU处理数据的时候都去寄存器里边儿去拿,当我拿不到数据的时候,我再去下面的空间里面去拿,这样我们计算机的整体速度时候就上去了。这是我们计算机里边儿存在这样一个存储体系的一个基本原因。当然寄存器这个关键字就是我们刚刚写的register。这样一个关键字,对吧?register这样一个关键字。那寄存器的访问速度那么快的话,那我们未来呢,我们写代码的时候就允许这样去写代码吗?怎么去写代码呢?诶,我们这样写,你看这是我的一个main函数,那写什么代码呢?我说int a = 10 而这个10未来会帮我们频繁大量地使用时候,那想象一下,每次我去访问a的时候去内存里边儿去拿是不是速度比较慢?
因为他们频繁地使用嘛,那怎么办呢?我们就想说那能不能把a放到寄存器里边儿去?怎么怎么把a放到寄存器里边儿去呢?那c给出一种解决方案是这样的,register他们给出了这样一个关键字,register往这儿一放,register in a就是说我把a给大家定义成一个寄存器变量。把a定义成寄存器变量,寄存器变量把a定义成寄存器变量,当然这个定义只是一个定义作用,建议把a定义成寄存器变量。为什么是建议的一个作用呢?想象一下,如果我们在这个地方代码里边儿只要写上register a就放到寄存器里边儿去了,那所有程序员写代码的时候都觉得自己的这个这个变量的特别重要,好了,给变量的都加上register,你想象一下我们的代码里边儿出现成千上万个寄存器变量,那到底把哪一个放到寄存器里边儿去呢?这个时候是不是形成了一个问题啊?所以我们的寄存器在我们的计算机上是有限的,可能在我们的计算机上只有几十个寄存器,对不对?几十个寄存器,那这个时候没有那么多寄存器可以使怎么办呢?好了,那这个时候呢这个register的关键字仅仅是建议把a放到寄存器变量里边儿去,但是不是最终真的把这个a放到寄存器变量里边儿去,这个是取决于谁呢?取决于编译器会自己去判断能不能要不要把这个a能放到计算器里边儿去,大概就这么一个意思。
typedef:类型重定义
比如:unsigned int num1 = 20;
那么typedef unsigned int u—int;
这样u—int num2 = 0。
意思是给unsigned int起一个别名u—int,用这个别名去声明起同样效果。
static:用来修饰变量和函数
void test()
{
int a = 1;
a++;
printf("a = %d\n", a);
}
int main()
{
int i = 0;
while (i < 5)
{
test();
i++;
}
return 0;
}
如果开始进行程序,那么进入程序之后声明了一个i之后进入will的那个循环体,然后i等于零,i小于5符合条件,那么就会碰到遇到这个test函数,test函数遇到后就会跳转到上面的这个void test函数这块,进入之后它就会创立一个a这么一个变量,a等于1,然后a++加1之后a等于2再打印,所以说就会打印出2,打印出2后再回来到i++,所以i之前等于0,i++之后就会变为1,然后再开始往上找while的这个判断条件,i等于1,1仍然小于5,那么再到t的函数的时候又会上去,但是又会上去就需要注意到一点,再上去之后我们再去考虑这个程序的话,它会再次声明一个a,a被赋值为1,那么也就是说我们又重新来了一遍,a又被赋值为1,然后呢a++输出2,然后在下面i++,i等于3,3还小于5,再碰到test又回去,a又建立了一次,然后被赋值为1,循环往复,我们会一直到i等于5的时候停止这个循环程序结束。
在int a前加一个static,会使变量会变为一个静态的局部变量。这样输出的结果就是23456。也就是说a赋完新值后会保持这个值,下一次在进入test函数后,2++,a的值不销毁。所以得出结论:static修饰局部变量,变量声明周期变长。实际上这个涉及到内存空间的问题,以后再写,现在只是了解概念的时候。
修饰全局变量:
创建一个新文件。add.c
int g_val = 2021 放入一个变量,然后回到之前的程序。
用extern来引用这个外部的变量
int main()
{
extern int g_val;
printf("%d\n", g_val);
return 0;
}
会输出2021这个数字。extern可以引用外部变量来提供给所在的程序。如果给g_val前面放上static,static int g_val = 2021。再次引用程序就会报错,变量无法被引用。那么结论:static修饰全局变量,改变了作用域,使其只能在源文件内使用。
它还可以修饰
int a = 100;
int b = 200;
int sum = Add(a, b);
printf("%d\n", sum);
return 0;
再在其他文件声明这个add函数
int Add(int x, int y)
{
int z = x + y;
return z;
}
回到前面的程序,要用extern来引用
extern int Add(int, int);
只需要这样声明即可,放在int main前面,即可输出结果,如果在extern前加static,那么就会出错,找不到这样的函数,也就是说static不是在int Add之前写的,而是在引用时写的。那么结论:修饰函数,改变函数的连接属性。一个正常的函数拥有外部连接属性。
#define定义的常量和宏
常量说过,来说说宏。
#define MAX 100 //当程序再遇到MAX时,就会自动变为100。也可以定义宏,宏是带参数的。
一个新程序:
/*int MAX(int x, int y)
{
if (x > y)
return x;
else
return y;
}*/
#define MAX(X, Y)(X > Y? X : Y)
int main()
{
int a = 10;
int b = 20;//用宏的方式来做加法
int max = MAX(a, b);
printf("max = %d\n", max);
return 0;
}
运行之后就出现了20的值。但是宏是直接替换,也就是把定义的东西直接搬过来,这个之后写。
五、指针
指针的讲解需要用到内存的概念。
int main()
{
int a = 10;
int* p = &a;//int*是p的类型,p是指针变量,p里储存着a的地址。如果想找到a,那么:*p 这里用到了之前没有说过的*。*是解引用操作符,*p就可以找到p指向的变量。*p = 20 意思就是a被赋值了20.
*p = 20;
return 0;
}
这样就可以理解*操作符了。
结束。