【初阶三】认识C语言—下
1.函数
函数就像一个工厂,通过输入原料(传递参数),在工厂(函数)内加工(代码执行)后返回一个成品(return一个值)
- 我们使用的scanf和printf都是C语言的库函数(C语言提供的内部函数,需要引用头文件才能使用)
- 还有一种函数是我们自己实现的函数,接下来举例说明
这是一个求a+b的和并打印的代码,在main函数中实现:
这是使用自己实现的函数求a+b的和并打印:
代码进行优化,不用定义变量num直接返回a+b也可以达到一样的效果:
形式参数和实际参数:实际上是将实际参数的值临时拷贝一份给形式参数,二者除了值相同以外,没有任何联系,他们拥有不同的存储空间,所以形参的改变对实参没有影响
大家可能有一个疑问,自己实现的函数add比main函数内实现更加繁琐,为什么还要使用自己的函数呢?
1.在main函数外实现函数会使代码层次分明,更易读
2.当我们以后实现一个大型项目时,一个功能难免会被多次使用,这时只要将这个功能写成自己的函数,就可以通过调用函数来使用这个功能,比每次都写一遍更方便快捷
2.数组
当我们想要存储100个数据时,我们不可能定义100个变量,所以C语言提供了数组
数组即为一组相同类型元素的集合,我们可以将所有相同类型的数据存储在数组里
数组规定每个元素都拥有下标,下标从0开始计数,可以通过下标来访问数组
int arr[10] = {1,2,3,4,5,6,7,8,9,10};//存储了10个整形元素的数组
// 0 1 2 3 4 5 6 7 8 9 //整型1的下标是0,整型2的下标为1,以此类推
通过下标来访问数组:
3.操作符
3.1算数操作符
分别时加、减、乘、除、取余
加减乘与数学中一样,但乘的符号换成了 *
重点讲解 除 和 取余 两种算术操作符
C语言中除法分为整数除法和浮点数除法
整数除法:
#include<stdio.h>
int main()
{
int a = 7;
int b = 2;
int c = a / b;//整数除法中除号两边的变量必须都为整型
printf("%d", c);//结果为3,而不是3.5,是因为C语言的除法是整除之后得到商
return 0;
}
浮点数除法:
int main()
{
int a = 7/2;
printf("%d\n", a);//这里的结果为3,因为它是整数除法
printf("%lf\n", 7.0 / 2);//结果为3.500000
printf("%lf\n", 7 / 2.0);//结果为3.500000
printf("%lf\n", 7.0 / 2.0);//结果为3.500000
return 0;
只要除号左右两边至少有一个为浮点型,那它就是浮点数除法
printf中的%lf表示要打印float类型的数据(精读到小数点后6位)
取余:
#include<stdio.h>
int main()
{
int a = 7;
int b = 4;
int c = a % b;
printf("%d", c);//这里得到的结果为3,1来自于7除4得1余3.
return 0;
}
操作符%左右两边必须为整形
3.2移位操作符和位操作符
这里移位操作符和位操作符都是操作的二进制位,>>是右移操作符,<<是左移操作符
#include<stdio.h>
int main()
{
int a = 10;//10的二进制表示是00001010,补码是00001010
a = a << 1;//补码左移1位后是00010100,右边自动补0.
printf("%d", a);//a的二进制位左移后值变了,十进制对应二进制0010100是20,所以打印结果20
return 0;
}
&叫做按位与, |叫按位或,^叫按位异或
#include<stdio.h>
int main()
{
int a = 10;//a的二进制00001010,补码是00001010
int b = 5; //b的二进制00000010,补码是00000010
int c = a & b;//补码按位与,都是1才是1,得00000010
int e = a | b;//补码按位或,都是0才是0,得00001010
int d = a ^ b;//补码按位异或,相同为0,相异为1,得:00001000
return 0;
}
3.3赋值操作符
赋值操作符很多,但用法相近,它们的涵义是将等号右边的值赋值给等号左边的变量
先拿 = 举例:
上面这段代码会把两段话都打印出来,因为这里的if判断内容是赋值而不是相等,只是一个容易忽视的错误
=是赋值运算符,==才是比较
更改之后就可以看到如下结果:
再用 += 举例:
#include<stdio.h>
int main()
{
int a = 1;
int b = 2;
a += 1;//此代码等同于a = a + 1
a += b;//次代码等同于a = a + b
return 0;
}
这时再去理解其他操作符就很好理解了
3.4单目操作符
这些操作符只作用在一个操作数上,比如我们的算术操作符 / ,它作用在除号两边的两个操作数,所以它不是单目操作符
这些单目操作符中,我们把 & 和 * 操作符放到后面的章节详细讲解
逻辑反操作符 ! ,在一个变量前面加上一个逻辑反!,它的逻辑就会颠倒
++和–可以作用在操作数的左边或者右边,两种形式表达的意思是不相同的,例:
#include<stdio.h>
int main()
{
int a = 1;
printf("%d\n", a++);//当++放在操作数右边时,是先使用操作数本来的值再把操作数+1.打印结果为1
printf("%d\n", a);//这个地方,a在前面已经自增过1了,所以打印2
int b = 2;
printf("%d\n", ++b);//当++放在操作数左边时,是先把操作数的值+1再使用+1后的值.打印3
return 0;
}
目操作符(类型),指一个括号里面放一个类型,比如(int),(char),它的作用是强制类型转换,例:
#include<stdio.h>
int main()
{
int a = 5;
int b = 2;
float c = (float)a / b;//我们说当除号左右至少都一个操作数为浮点型,那就是浮点型除法.
int d = a / b;
printf("%lf\n", c);//强制类型转换a的int类型为float类型后,a被当作浮点型,所以进行浮点型除法
printf("%d\n", d);//这里没有强制类型转换,为整型除法
return 0;
}
3.5关系操作符
C语言中的大于等于用的是>=,小于等于用<=
一个=是用来赋值的
双等号==才是用于判断是否相同的
3.6条件操作符
它的意思是如果exp1的逻辑为真,就返回exp2的值,如果exp1的逻辑为假
exp1 ? exp2 : exp3
例:
#include<stdio.h>
int main()
{
int a = 5;
int b = 2;
int c = a > b ? 10 : 5;//这里a大于b为真,所以我们执行冒号前面的值,打印10
printf("%d", c);
return 0;
}
3.7逗号表达式
int main()
{
int a = 3;
int b = 2;
int c = 5;
//逗号表达式,是从左向右依次计算的,逗号表达式的结果是最后一个表达式的结果
int d = (a+=3, b=5, c=a+b, c-4);
// a=6 b=5 c=11 7
printf("%d\n", d);//d的值为最后一个表达式的值为7
printf("%d\n", a);//我们再把a,b,c的值打印出来,会发现它们不是本来的3,2,5了,而是在逗号表达式
printf("%d\n", b);//中经过计算后重新得到的数
printf("%d\n", c);
return 0;
}
4.常见关键字
4.1关键字typedef
typedef 是类型定义,这里理解为类型重命名
#include<stdio.h>
typedef unsigned int unit;//将unsigned int 重命名为unit, 所以uit也是一个类型名
int main()
{
//观察num1和num2,这两个变量的类型是一样的,相当于把unsigned int改了一个名字
unsigned int num1 = 0;//改名后,改名前的类型也可以使用
unit num2 = 0;
return 0;
}
现在学习到的int,char等类型长度比较短,使用typedef可能没有那么明显的好处,但是后期我们接触顺序表和链表后,typedef的作用就能体现出来了
4.2 关键字static
static是用来修饰变量和函数的
- 修饰局部变量:称为静态局部变量
- 修饰全局变量:称为静态全局变量
- 修饰函数:称为静态函数
#include<stdio.h>
void test()test函数中创建的变量a是局部变量,所以它在我们调用一次函数结束后会把内存还给系统销毁
{
int a = 5;
printf("%d ", a);
a++;
}
int main()
{
int i = 0;
while (i < 10)//调用10次test函数
{
test();//每次调用test函数进来就重新定义一个变量a=5,这时的a和前一次调用的a没有关系,因为上一个a已被销毁
i++;
}
return 0;
}
这段代码不断打印5,因为调用一次test函数后a就被销毁了,a++后a变成6后的值不会保存起来,下一次调用函数时a的初始值还是5
我们再来看看用static修饰a后会出现什么变化:
#include<stdio.h>

void test()
{
static int a = 5;//static修饰a后,a就不在栈区了,a就被存放在静态区了!
printf("%d ", a);//打印5,6,7......
a++;
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
5. define定义常宏
define定义宏和我们之前的定义常量有一点相似
//define定义宏
#define ADD(x, y) ((x)+(y))//Add后面括号里的x和y为参数,后面的((x)+(y))叫做宏的实现体
#include <stdio.h>
int main()
{
int sum = ADD(2, 3);//遇见Add()时就会自动把Add和里面的变量替换为后面的宏的实现体
printf("sum = %d\n", sum);
sum = 10*ADD(2, 3);//遇见Add()时就会自动把Add和里面的变量替换为后面的宏的实现体
printf("sum = %d\n", sum);
return 0;
}
6.指针
6.1内存
C语言中指针就是用来访问内存的,内存是电脑上特别重要的存储器,计算机中的程序都是在内存中运行的(一个比特位对应一个二进制位也就是存放一个二进制数)
为了有效地使用内存,内存被划分成一个个小内存单元,每个内存单元的大小是1个字节
为了能够有效的访问到内存的每个单元,就给内存单元进行编号,这些编号被称为该内存单元的地址
所以C语言引出了指针和地址的概念,方便我们寻找变量在内存中的位置,变量是创建内存中的(在内存中分配空间),所以变量也是有地址的
在计算机中有地址线(物理的电线),它将高低电平信号转换为数字信号1和0(二进制信息),我们的32位电脑上有32根地址线,每根地址线传达的数字信息要么为0要么为1,所以我们的一个内存单元(一个字节)可以存储二进制00000000000000000000000000000000到11111111111111111111111111111111内的任何数据,转换为10进制也就是存储到0到4,294,967,296
6.2取地址操作符&
#include <stdio.h>
int main()
{
int num = 10;
#//取出num的地址
//注:这里num的地址大小是4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
printf("%p\n", &num);//打印地址,%p是以地址的形式打印
return 0;
}
char temp = 'w';
char* p = &temp;//p是char类型的指针,可以存放变量temp的地址
*p = 'z'//*p是解引用,解引用后就是temp变量,可以赋值成z
//判断自己是多少位的机器
//观察指针变量的大小
#include <stdio.h>
//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
printf("%d\n", sizeof(char *));
printf("%d\n", sizeof(short *));
printf("%d\n", sizeof(int *));
printf("%d\n", sizeof(double *));
return 0;
}
7.结构体
结构体关键字:struct
结构体使得C语言有能力描述复杂类型,比如我们想描述学生,学生包含了名字、年龄、性别、学号这几项信息,如果我们用传统的方法是不能解决的,这里就能使用结构体来描述了:
struct Student
{
char name[20];//名字
int age; //年龄
char sex[5]; //性别
char id[15]; //学号
};
结构体初始化:
struct Student
{
char name[20];//名字
int age; //年龄
char sex[5]; //性别
char id[15]; //学号
};
int main()
{
//初始化结构体后,name里面放入的是张三,age里面为20,sex为男,id是20180101,中间用逗号隔开.
struct Student s = { "张三", 20, "男", "20180101" };//这里定义结构体变量名为s
printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id); //.为结构成员访问操作符,是结构体成员的访问
struct Student* ps = &s;
printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps -> id);//->操作符相当于结构体中的解引用操作
return 0;
}
我们还可以在程序中定义多个结构体:
#include<stdio.h>
struct Student
{
char name[20];//名字
int age; //年龄
char sex[5]; //性别
};
struct Book//描述一本书
{
char name[20];//书名
char author[20];//作者
float price;//价格
};
int main()
{
struct Student s1 = { "张三", 20, "男" };
struct Student s2 = { "李四", 20, "男" };
struct Student s3 = { "王五", 20, "男" };
struct Book b = { "《STL源码剖析》", "Hou Jie", 79.0f };
printf("%s %s %f\n", b.name, b.author, b.price);
return 0;
}