语句
C语言的语句结构,从执行流程的角度可以划分出三种基本结构,顺序结构、分支(选择)结构、循环结构。
顺序结构
顺序结构就是从上往下的顺序依次执行语句的结构。
#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
int c = 0;
c = a;
a = b;
b = c;
printf("a=%d b=%d\n",a,b );
return 0;
}
分支(选择)结构
分支(选择)结构, 给定判断条件,根据判断条件的结果是否为真,来选择执行什么语句。
C语言中0代表false,一切的!0 代码true。
分类
- if语句
- if else 语句
- switch 语句
#include <stdio.h>
int main()
{
int input = 0;
printf("上学去\n");
printf("你要好好学习吗?(1/0)>:");
scanf("%d", &input);
if (input == 1) 条件式为 // if(true)---> 好offer
printf("好offer\n");
else // else --> 卖红薯
printf("卖红薯\n");
return 0;
}
循环结构
循环结构,依据判断条件来重复执行循环体内部的语句,为假时退出循环。
分类
- while 循环
- for 循环
- do while 循环
int main()
{
int line = 0;
printf("加入我们");
while (line < 20000)
{
printf("敲""%d\n""代码\n", line);
line++;
}
if(line >= 20000)
printf("好offer!~");
return 0;
}
函数
函数是C语言的基本模块,一块独立的代码,可以将C程序看作由各种函数组成的。
函数分为两种: 库函数和自定义函数
函数的功能要相对独立,即每个函数只完成自己特定的任务。
库函数
C语言提供给开发人员的函数,将常用功能的函数封装入库,供开发人员随时调用,提高了开发效率。
使用库函数需要引头文件。
上面经常用到的printf();和scanf();就是库函数。
printf()格式化打印函数,可以跟两个参数;
sancf()输入函数
#include <stdio.h> // 头文件 standard input and output 标准输入输出
int main()
{
int a = 0;
printf("请输入值:>"); // 调用函数,输出内容到标准输出
scanf("%d",&a); // 接收用户收入的十进制数,存放在a存放的地址所指向的那块内存区域。
printf("%d\n",a);
return 0;
}
自定义函数
为了开发人源自己封装的函数,用于完成不同的任务。
函数使用时需要调用函数。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
// 自定义函数
// 函数的返回类型 函数名 (para1, para2)
int Add(int x, int y)
{
int z = x + y; // 函数体
return z;
// 返回的z是整型,Add前面要写int
}
int main()
{
int num1 = 10;
int num2 = 30;
int sum = 0;
sum = Add(num1, num2); // 调用函数
printf("sum = %d\n", sum);
return 0;
}
数组
一组相同类型元素的集合
数组声明
int main()
{
// 语法格式
// 数据类型 变量名+[] ,[] 内部来设置数组内的元素个数。
int arr[5]; // 数据中可以存放5个整形元素
char str[5]; // 数组中可以存放5个字符元素
// 注意 char数组和int数组 元素个数相同,数组大小不相同
// 因为char类型占用的空间是1byte,而int占用的空间是4byte。
printf("arr=%d str=%d",sizeof(arr),sizeof(str));
// arr = 20 str = 5
return 0;
}
数组初始化
- 整形数组在初始化时使用大括号{},每个元素用逗号分隔;
#include <stdio.h>
int main()
{
int arr[10]= { 0,1,2,3,4,5,6,7,8,9 }; // 定义一个存放10个整型的数组
char str1[] = {'a','b','c','d','e','f','\0'};
char str2[] = "abcdef"; // 字符串数组在初始化时可以用""不加{};
printf("str1 = %s, str2 = %s", str1,str2);
// 结果相同
return 0;
}
- 数组在初识化时 [] 中不指定元素个数,数组的元素个数由初始化的元素个数决定。
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6 };
char str[] = "abcdef";
// 计算元素个数的方法 sizeof(arr) / sizeof(arr[0]);
int sz1 = sizeof(arr) / sizeof(arr[0]);
int sz2 = sizeof(str) / sizeof(str[0]);
printf("sz1 = %d, sz2 = %d\n", sz1,sz2);
return 0;
}
3. 初始化的元素个数不能大于[]中的值。
int main()
{
int arr[5] = {1,2,3,4,5,6};
return 0;
}
数组下标
数组里每个值都有下标,从0开始,依次递增。
最后一个元素的下标 == 数组元素个数 - 1。
#include <stdio.h>
int main()
{
int arr[10]= { 0,1,2,3,4,5,6,7,8,9 };
printf("%d\n", arr[4]); // 第5个值 为4
return 0;
}
打印数组
如果想打印数组只有通过循环的方式打印,字符数组可以通过%s来打印字符串。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
char str[] = "abcdef";
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
// 利用循环遍历数组
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
// %s 打印字符串
printf("\n %s", str);
return 0;
}
操作符
算数操作符
// + - * / %
// 加 减 乘 除 取模
- 两个整形进行除法运算的得到是整数,即使用浮点型变量接收结果依然是整数
int main()
{
double res = 5 / 2;
printf("%lf\n", res); //2.000000 商2余1
return 0;
}
- 进行运算的两个数其中一个为浮点型,得到的也为浮点型。(前提接收结果的变量为浮点型)
int main()
{
double res = 5 / 2.0;
printf("%lf\n", res);
return 0;
}
移位操作符
移二进制位,将数的二进制形式整体左移或者右移。
移位操作符并不会改变变量本身的值,与算数操作符一致,a+1并不会改变a的值,除非a=a+1。
// >> 右移 << 左移
int main()
{
int a = 3;
// int 占 4byte = 32bit
// 所以 3 = 00000000000000000000000000000011;
int b = a << 1; // 向左移1位
// b = 00000000000000000000000000000110;
// b = 6; 1*2^2 + 1*2^1 + 0*2^0 = 6
int c = a << 2;
// c = 00000000000000000000000000001100;
// c = 12
printf("%d\n", b); // 6
printf("%d\n", c); // 12
printf("%d\n", a); // 3 , a不变
// 当对变量进行移位操作时,变量本身不变,除非将值再赋给原变量
return 0;
}
位操作符
位操作符操作的是相同二进制位,将两个10进制数转换位2进制的形式,相同的每一位二进制数进行运算。
按位与&
两个二进制位都为1时结果为1,否则为0。
1
=
11
,
0
=
01
/
10
/
00
1=11,0=01/10/00
1=11,0=01/10/00
int main()
{
int a = 8;
int b = 10;
int res = a&b;
printf("%d\n", res); // 8
return 0;
}
这里不把32位全部列出,因为是正整数,1的前面全部为0。
按位或 |
两个二进制位中至少存在一个1则结果为1,全0结果为0;
1
=
11
/
10
/
01
,
0
=
00
1=11/10/01,0=00
1=11/10/01,0=00
int main()
{
// 按位或
int a = 8;
int b = 10;
int res = a | b;
printf("%d\n", res); // 10
return 0;
}
按位异或^
两个二进制位不同时结果位1,相同时结果为0。
1
=
10
/
01
,
0
=
11
/
00
1=10/01,0=11/00
1=10/01,0=11/00
int main()
{
// 按位异或
int a = 8;
int b = 10;
int res = a ^ b;
printf("%d\n", res); // 2
return 0;
}
赋值操作符
赋值操作符 | 作用 |
---|---|
= | 将=右边得值赋给左边,与关系操作符 == 不同 |
复合赋值符 | |
+= | 进行加运算,同时在赋值给原来的变量 |
-= | 进行减运算,同时在赋值给原来的变量 |
*= | 进行乘运算,同时在赋值给原来的变量 |
/= | 进行除运算,同时在赋值给原来的变量 |
&= | 进行按位与操作,同时在赋值给原来的变量 |
|= | 进行按位或操作,同时在赋值给原来的变量 |
^= | 进行按位异或操作,同时在赋值给原来的变量 |
>>= | 进行右移位操作,同时在赋值给原来的变量 |
<<= | 进行左移位操作,同时在赋值给原来的变量 |
// 例:
int main()
{
int a = 10; // 创建变量时给变量赋值叫做初始化
a = 20; // = 赋值
// 复合赋值符
a += 10; // 等价于 a = a + 10;
a -= 20; // 等价于 a = a - 20;
a *= 20; // 等价于 a = a * 20;
a /= 20; // 等价于 a = a / 20;
a &= 2; // 等价于 a = a & 2;
a >>= 1; // 等价于 a = a >> 1;
return 0;
}
单目操作符
单目操作符只有一个操作数
单目操作符 | 作用 |
---|---|
! | 逻辑反操作 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 计算变量/类型所占空间的大小(以字节为单位) |
~ | 对一个数的二进制按位取反 |
– | 前置、后置– |
++ | 前置、后置++ |
* | 间接访问操作符(解引用操作符) |
(类型) | 强制类型转换 |
!逻辑反操作
取真值的相反值,C语言中 0 == False,一切的非0 ==true。
#include <stdio.h>
int main()
{
int a = 10;
printf("%d\n", !a); // 0
// C语言中一切 0 = false 一切的!0 = true
// a = 10 = true , 所以!a = false = 0;
// !0 的固定返回值为1;
int b = 0;
printf("%d\n", !b); // 1
// b = 0 = false , !0 = 1 (固定值)
return 0;
}
sizeof
计算变量或类型所占内存的大小,单位为byte
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(a)); // 4 变量a 属于整型占4个字节
printf("%d\n", sizeof(int)); // 4
printf("%d\n", sizeof a ); // 4 计算”变量“时可以省略括号
printf("%d\n", sizeof int); // int的括号不能省略会报错
// sizeof 可以计算数组大小
int arr[10] = {0}; // 创建了10个整型元素的数组
printf("%d\n", sizeof arr); // 40, 每个整型4 * 10
// 计算数组的元素个数
// 数组总大小 / 每个元素的大小
int res = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", res); // 10
return 0;
}
~ 按位取反
- 对一个数的二进制数的每一位进行按位取反,1变0,0变1;
#include <stdio.h>
int main(){
int a = 0;
printf("%d\n", ~a); // -1
return 0;
}
分析计算过程
原码,补码,反码的概念
-
计算机中存储的数组都是二进制数,而C语言中数值默认为有符号数值。即数值的二进制最高位为0时为正数,为1时为负数。其余二进制数为10进制数的绝对值。
-
有符号数的可以用原码、补码、反码的形式来表示。
正数的原码=补码=反码
int main()
{
int a = 10;
// 原码 00000000000000000000000000001010
// 反码 00000000000000000000000000001010
// 补码 00000000000000000000000000001010
// 最高位的0表示正号
return 0;
}
负数的原码 = 十进制数的绝对值的二进制数,符号位为1;
负数的反码 = 在原码的基础上符号位不变,其他位按位取反;
负数的补码 = 负数的反码 + 1 ;
int main()
{
int num = -10;
// 原码 10000000000000000000000000001010
// 反码 11111111111111111111111111110101
// 补码 11111111111111111111111111110110
return 0;
}
-
内存中以补码的形式存储数值,计算时采用的也是补码,因此在计算时需要将得到得结果从补码转换为原码
回看最上面的代码
#include <stdio.h>
int main(){
int a = 0;
// a 的值存储在内存中的补码:
// 00000000000000000000000000000000
// ~a 按位取反 得到补码,符号位为1是负数
// 11111111111111111111111111111111
// 转换为反码
// 11111111111111111111111111111110
// 转换为原码
// 10000000000000000000000000000001 = -1
printf("%d\n", ~a); // -1
return 0;
}
自增++自减–
前置++、前置–
先自运算,再使用
#include <stdio.h>
int main()
{
int a = 2;
int b = ++a; // 先自用算再使用
printf("a=%d,b=%d\n",a, b); // a = 3 b = 3
int c = --b;
printf("b = %d, c = %d\n", b,c); // b =2 c = 2
return 0;
}
后置++、后置–
先使用,再自运算
#include <stdio.h>
int main()
{
int a = 2;
int b = a++;
printf("a=%d,b=%d\n", a, b);
int c = b--;
printf("b = %d, c = %d\n", b, c);
return 0;
}
强制类型转换
#include <stdio.h>
int main()
{
int dd = 3.14 // 给整型赋值浮点数会发出警告
return 0;
}
使用强制类型转换 (类型),可以对操作数的类型进行强制转换。
int main()
{
int dd = (int)3.14;
printf("%d\n", dd); // 3
// 此时输出控制台不会报警告
return 0;
}
关系操作符
比较操作符两边操作数的大小关系
> 大于
>= 大于等于
< 小于
<= 小于等于
!= 用于测试“不相等”
== 用于测试“相等”
逻辑符操作符
逻辑与 &&
逻辑与为真,当且仅当,被操作数全为真,否者为假
int main()
{
int a = 3;
int b = 5;
if(3 == a && 5== b) // 当 a == 3 b == 5 都为真时, 进入条件表达式
{
printf("逻辑与");
}
return 0;
}
逻辑或 ||
逻辑或为真,当且仅当被操作数至少有一个为真,被操作数全为假式,逻辑或结果为假
int main()
{
int a = 4;
int b = 5;
int c = 1;
if (3 == a || 5 == b) // 当 a == 3 b == 5 至少有一个为真, 进入条件表达式
{
printf("逻辑或");
}
if (3 == a || 4 == b || 2 == c) // 全为假时逻辑或结果为假
{
printf("123");
}
return 0;
}
条件操作符(三目操作符)
条件操作符有三个操作数
exp1 ? exp2 : exp3
// 表示 如果exp1 为真 返回exp2 否者返回 epx3
// Is exp1 true? if exp1 is true then return exp2 else return exp3.
逗号表达符
exp1, exp2, exp3, …expN // 逗号可以隔开一串表达式
int main()
{
int a = 0;
int b = 3;
int c = -1;
int d = (a = b - 5, b = a + c, c = a + b, c -= 5);
// 逗号表达式会从左向右依次计算
// 整个逗号表达式的结果是最后一个表达式的结果
printf("%d\n", a); // -10
printf("%d\n", d); // -10
return 0;
}
下标引用、函数调用和结构成员
下标引用操作符[]
int main()
{
// [] 的操作数是变量名和[]里面的整型常量
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
// 下标从0开始:0,1,2,3,4,5,6,7,8,9 (元素对应的下标)
arr[4]; // 5 访问下标为4的数组元素
return 0;
}
函数调用操作符()
// () 函数名和里面的参数为操作数
// 把实参的数据传给形参
int Add(int x,int y) // 声明函数 (para1,para2,...) 形式参数
{
int z = 0;
z = x + y;
return z; // return的值 调用函数接收
}
int main()
{
int a = 10;
int b = 20;
int sum = Add(a, b); // 调用函数,(para1,para2,...) 实际参数
}
常见关键字
下面介绍一些常见关键字的使用
auto
自动,在C语言用于自动变量
int main()
{// 进作用域
auto int a = 10 // 局部变量a
// 局部变量,进入作用域自动创建,出作用域自动销毁,所以也叫自动变量。
// auto 被默认省略掉了,因为局部变量都是自动变量
return 0;
}// 出作用域
register 寄存器关键字
计算机存储数据的位置,速度由快到慢,容量由大到小
寄存器–>高速缓存–> 内存 --> 硬盘
数据存储在硬盘中,cpu在调用数据时要依次从
硬盘–> 内存 --> 高速缓存 --> 寄存器 --> cpu
CPU先从寄存器提取数据,当寄存器拿不到数据再依次往下提取数据
int main ()
{
// 给a定义为寄存器变量(建议),结果取决于编译器;
register int a = 10;
return 0;
}
signed 与 unsigned
C语言中数值分为有符号位数(signed)和无符号位数(unsigned)
signed 二进制最高位表示正负 0 正 1负
unsigned 没有符号位,不存在正负之分
int main ()
{
signed int a = -2;
// int 定义的变量是有符号的, + -
// signed 通常被省略掉
unsigned int num = -10; // 无符号数,没有符号位,不存在正负之分
}
typedef 类型重命名
自定义类型名,方便下次调用
// 例如将无符号位类型重命名
typedef unsigned int u_int;
int main()
{
u_int num = 20;
printf("%d\n", 20);
return 0;
}
static
static 用来修饰 局部变量、全局变量和函数。
static在修饰未初始化的变量时,会给变量赋值为0。
static 修饰局部变量
局部变量得生命周期变长,但不会改变局部变量得作用域。
// 不使用static 修饰时
void test()
{// 进作用域 局部变量a创建
int a = 1; // 局部变量a 初始化为1
a++;
printf("%d ", a);
// 每次都是进入函数都是重新创建变量并初始化
// 因此打印结果为 2 2 2 2 2
}// 出作用域 局部变量a销毁
int main()
{
int i = 0;
while(i<5)
{
test();
i++;
}
}
// 使用static 修饰局部变量时
void test()
{// 进作用域 局部变量a创建
static int a = 1; // 局部变量a 初始化为1,下次进来不会再重新初始化
a++; // 再上一次计算的结果上继续自增
printf("%d ", a);
// 打印结果为 2 3 4 5 6
}// 出作用域时static改变了a的生命周期,不会被销毁,值会被保存,下次继续使用。
int main()
{
int i = 0;
while(i<5)
{
test();
i++;
}
}
static 修饰全局变量
全局变量默认存在外部链接属性,使用static修饰全局变量时,将全局变量的外部链接属性改编为内部链接属性,此时static修饰的全局变量只能再本源文件中使用。
// test01.c 源文件
static int global = 2021;
// test.c 源文件
extern int global;
int main()
{
printf("%d\n", global);
return 0;
}
static 修饰函数
static 修饰函数与修饰全局变量相同,都是将其的外部链接属性改为内部链接属性,使得其只能在本源文件中使用。
// test01.c 源文件
static int Add(int x, int y)
{
return x + y;
}
// test02.c 源文件
extern int Add(int, int);
int main()
{
int a = 10;
int b = 20;
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
#define 定义常量和宏
定义标识符常量
// 语法格式, 不用加 =
#define MAX 100;
int main ()
{
int a = MAX;
printf("%d\n", a);
return 0;
}
定义宏
// 函数的实现
int Max(int x, int y)
{
int z = (x > y) ? x : y;
return z;
}
// 宏的定义
#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);
// 宏的方式
max = MAX(a,b);
// 通过main函数上面宏的定义,这个表达式会变成
// max = (a>b?a:b);
printf("max = %d\n", max);
return 0;
}