函数
数组
操作符
常见关键字
define 定义常量和宏
指针
结构体
数学中的函数
f ( x ) = 2 ∗ x + 1 , f ( x , y ) = x + y f(x)=2*x+1, f(x,y)=x+y f(x)=2∗x+1,f(x,y)=x+y
实现一个简单的c语言函数
假如说我要实现两个数的加法并输出,那么代码应该如下
#include
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = num1 + num2;
printf("sum = %d\n", sum);
return 0;
}
如果我们把它写成函数
#include
int Add(int x, int y)
{
int z = x+y;
return z;
}
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = Add(num1, num2);
printf("sum = %d\n", sum);
return 0;
}
又比如这里的比大小,取大数
int compare(int x, int y)
{
return x > y ? x : y;
}
int main()
{
int n = 0;
int m = 0;
scanf("%d%d", &m, &n);
int ret = compare(m ,n);
printf("%d", ret);
return 0;
}
总结:
函数的特点就是简化代码,代码复用。
一个函数需要函数名,括号中需要包含函数的形参,以及返回类型
如果我们要存储1-10的数字,怎么存储?
C语言中给了数组的定义:一组相同类型元素的集合
什么时候我们会用到数组呢?
比方说如果我要int 100个整形,我直接声明100个整形,未免太多变量,所以我们可以放入数组
int arr[10] = {
1,2,3,4,5,6,7,8,9,10};//定义一个整形数组,最多放10个元素
char ch[5]={
'a','b','c','d','e'};
C语言规定:数组的每个元素都有一个下标,且下标是从0开始的。
数组可以通过下标来访问的。
比如:
int arr[9] | 0 | 10 | 0 | 10 | 0 | 10 | 0 | 10 | 0 |
---|---|---|---|---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
比如我要打印一个数组中的值
#include
int main()
{
int i = 0;
int arr[10] = {
1,2,3,4,5,6,7,8,9,10};
for(i=0; i<10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
总结:
数组为同时存储很多变量提供了方便,但是c语言的数组会有一个问题,以后展开
c语言包含了非常丰富的操作符,因此c语言是非常灵活的,甚至能直接操作内存的数据,可以直接操作二进制位。
+ - * / %
int main()
{
int n = 7 / 2;
printf("%d\n", n);// 出来是商
double ret = 7 / 2.0;
printf("%lf\n", ret);//除一个小数就变成浮点型
int n = 7 % 2;// 7/2 商3,余1 % 取模(余)
printf("%d\n", n);
}
%的例子:
int n = 0;
scanf("%d", &n);
if (n % 2 == 1)
printf("奇数\n");
else
printf("偶数\n");
注:% - 取模操作符只能作用于整型
int n = 7.0 % 2; //err
>> <<
左移和右移操作符,本质都是将你给出的整数转化为二进制位,然后按一个比特位实现往左移或者是往右移。
这里的b就相当于a的二进制位向左移动1位并在后面补一个0,计算之后得到的是b=6
解释正数和负数以及反码补码问题将放在后面再仔细进行阐述。
& ^ |
注:^c语言中这个符号肯定不是次方,不能搞错
同样的解释这个操作符也在后面详细再展开
= += -= *= /= &= ^= |= >>= <<=
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
!我们要知道其实,c语言中规定0它是表示假,那么非0就是真
&放在后面指针再展开
sizeof 之前已经提到过一点,它的单位是字节
~
我们举个例子
同样的我们在实现多组输入的时候同样用到
int n=0
while(~scanf("%d",&n))
{
......;
}
while(~scanf("%d",&n))
的功能等同于while(scanf("%d",&n)!=EOF)
前置和后置++
int a = 10;
int b = ++a;//前置++,先++,后使用
int b = a++;//后置++,先使用,后++
int b = --a;//前置--,先--,后使用
int b = a--;//后置--,先使用,后--
(类型) 强制类型转换
int n = (int)3.14;-->3
int n = int(3.14);//注意不要写错成这样
>
>=
<
<=
!= 用于“不相等”
== 用于“相等”
总结:
描述清楚关系,要点在于=与==的区别
&& 逻辑与
|| 逻辑或
for example
if ((a == 1) && (b == 1))
{
printf("富强\n");
}
if ((a == 2) || (b == 3))
{
printf("民主\n");
}
避免连用,一个逻辑操作符只管左右两个式子
exp1 ? exp2 : exp3
利用三目操作符取代比大小的写法
//比较
if (a > b)
max = a;
else
max = b;
//三目操作符
max = (a > b ? a : b);
exp1, exp2, exp3, …expn
逗号表达式会从左向右依次计算
整个逗号表达式的结果是最后一个表达式的结果
int a = 3;
int b = 5;
int c = 10;
int d = (a + 2, b = a - 3, c = b + 4);//这句用到了逗号表达式
// b=0 c = 0+4
printf("%d\n", d);---->输出的是c也就是4
[] () . ->
[3]这样的写法正是下标引用操作符
int arr[10] = {
1,2,3,4,5 };
printf("%d\n", arr[3]);
函数调用操作符
int Add(int x, int y)
{
int z = x + y;
return z;
}
int main()
{
int sum = Add(3, 5);//------>这个正是函数调用操作符
printf("%d\n", sum);
return 0;
}
结构成员操作符这个在后面结构体一起展开
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 | typedef | union | unsigned | void | volatile | while |
注:简单介绍几个关键字
typedef 字面意思就是类型定义,这里我们理解为类型重命名。
//假如我们将unsigned int 重命名为AKA, 所以AKA也是一个类型名
typedef unsigned int AKA;
int main()
{
//观察num1和num2,这两个变量的类型是一样的
unsigned int num1 = 0;
AKA num2 = 0;
return 0;
}
相当于起一个昵称,继续学习下去,会发现typedef和无符号数有很大作用.
register是寄存器关键字 --可以提供建议作用
假如a是寄存器变量我们就用register修饰变量,一般修饰的这个变量是不可以使用&操作符的
我们其实在基本上每一本操作系统的参考书第一章都能看到一个金字塔图
基本在顶端的就是寄存器了
图1:存储器金字塔层次结构
图源来自:(https://www.cnblogs.com/binarylei/p/12588928.html)
cpu速度很快,一般都先从寄存器来拿数据进行处理
在C语言中:
static是用来修饰变量和函数的
- 修饰局部变量-称为静态局部变量
- 修饰全局变量-称为静态全局变量
- 修饰函数-称为静态函数
总结:
static修饰局部变量改变了变量的生命周期
让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
举个例子:
void test()
{
int a = 1;
a++;
printf("%d", a);
}
//最后打出来10个2 每次循环打出来一个2
int main()
{
int count = 0;
while (count< 10)
{
test();
count++;
}
//循环10次
return 0;
}
最后我打出来肯定是10个2,因为局部变量每次出了生命周期之后就销毁
但是假如我在test函数里面的原局部变量a用了一个修饰static
最后打出来就是
由此结果可知,每次调用test函数都是上一次函数调用后留下的,因此就是打印了2-11
理解原因:
我们说,要了解原因应该知道,有三个重要的内存存放区域
静态区,堆区和栈区
栈区 主要存放局部变量等 堆区 主要管动态内存分配 malloc free calloc realloc 静态区 主要存放全局变量,静态变量等
其中全局变量和静态变量放在了静态区,而局部变量放在了栈区
static实质上改变了a的位置(栈–>静态),从而使得静态的局部变量出了其作用域也不销毁,那生命周期也改变,当程序结束之后该变量才会被销毁。
我们说,假如我们要用来自其它部分的文件中的全局变量,是要先行声明的
用extern
之前提到过的关键字,专门用来声明外部符号
extern int g_val = 8848;
int main()
{
printf("%d\n", g_val);
return 0;
}
其它部分文件的情况就是
倘若我有两个源文件时,这里就涉及到了外部文件
此时我用static修饰这个全局变量的话,就会报错
看起来似乎好像是作用域变小了,实则本质上并不是。
一个全局变量能在其他文件内部能被使用,是因为全局变量有特殊的属性,即可以被外部链接。但是被static修饰后效果是外部链接属性变成了内部链接属性,因此该全局变量在其他文件中不能被使用。
总结:
static 修饰全局变量不会改变生命周期,但是会改变一个全局变量的链接属性。
修饰函数和修饰全局变量很相似
首先要在两个文件分别在一个文件利用另一个文件里写的函数,我们需要extern
同理如果我们对另外一个文件里面的函数如果修饰了static,那么外部链接属性也会被失效,因此报错无法解析外部符号。
总结:
效果同static修饰全局变量
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
1.定义标识符常量
#define YEAR 2021、
int main()
{
printf("%d",YEAR);
return 0;
}
可以输出2021
2.定义宏
#define ADD(X,Y) ((X)+(Y))
int main()
{
int a = 0;
int b = 0;
int ret = ADD(a, b);
printf("%d\n", ret);
}
看似宏和函数很像,可以实现函数的功能,但是其本身有着区别,且使用范围不同,之后再细细展开。
谈指针我们肯定要说内存
简单了解一下内存
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。
凡是我创建了一个变量,我就申请了空间
int main()
{
int a = 10;
return 0;
}
我说我要找到a怎么找?就是用&符号来找—>
&a
注:&a取出的是所占空间四个字节的其中第一个地址
在vs上就是启用调试可以取出a的地址
**我说我要打印地址怎么办? **
用%p就可以了
int main()
{
int a = 10;
printf("%p\n", &a);
return 0;
}
但是我说我要存储地址怎么办?
int* p = &a;
而这个p就是我们的指针变量,这个*就是在告诉我们p是指针变量,而int在告诉我们p是指向整形变量的
同理我如果要指向字符那就是char *
那我要如何改p里面的值呢?
*p=20;
这个叫解引用操作,*就是解引用操作符号,*p
的意思就是通过p中的值,找到p所指向的对象,也就是*p
就是a
那我问指针变量大小都是一样吗又是多少呢?
那么在32位下都是4个,也就是说在32位下管理一个字节内存单位我们用四个字节大小的指针
那么在64位下都是8个
总结:
指针可以理解为指向这个地址,这样理解就形象一点了,而指针变量就是用来存放地址的。
结构体是C语言中特别重要的知识点,结构体可以用来描述复杂类型。
比如描述一个职员,用来描述职员的属性包含: 名字+年龄+性别+职工号这几项信息。
这里只能使用结构体来描述了。
以下是一个例子:
struct Pokemon
{
char name[20];//名字
int id;//图鉴编号
float height; //身高
float weight;//重量
char fighting_type[20]; //属性
char species[15]; //类型
};
实现结构体还需要初始化:
struct Pokemon pikachu = {
"Pikachu",25,0.4,6.0,"electric","mouse pokemon" };
打印结构体信息,这里又涉及到之前的操作符 表示结构成员的使用
//.为结构成员访问操作符
printf("name = %s id = %d height = %.1f weight = %.1f\n", pikachu.name, pikachu.id,pikachu.height, pikachu.weight);
//->操作符
struct Pokemon* ps = &pikachu;
printf("name = %s id = %d height = %.1f weight = %.1f\n", ps->name, ps->id,ps->height, ps->weight);
其实->
这个操作符是简化如果用原来的方法写的话,未免过于复杂
printf("name = %s id = %d height = %.1f weight = %.1f\n", (*ps).name, (*ps).id, (*ps).height, (*ps).weight);
于是用->
表达就会变得简单一些,表示这个指针变量指向这个地址。
到这里,初始c语言就结束了,后面主要会以更细的方式和更细的代码来解释每一个版块的概念。
谢谢支持,希望大家能够一键三连~