第一部分:C语言知识点
1. C语言的官方标准
- ANSI C / C89标准:1989年,美国国家标准协会(ANSI)发布了第一个官方标准(简称 C89标准 或 ANSI C)。
- C99标准:1999年,国际标准化组织(ISO)和国际电工委员会(IEC)发布了C语言的新标准。
- C11标准:2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC)再次发布了C语言的新标准。
2. C语言程序的运行过程
- 编译:编译是由编译器来执行的,主要做的是语法检查工作;将C源程序翻译成计算机能识别的0和1;编译会把.c后缀的文件生成.o后缀的目标文件。
- 链接:将编译好的.o目标文件和C程序库函数组合在一起,生成可执行文件(即.exe后缀的文件)。
- 运行:直接双击打开.exe文件
3. 变量
概念:是内存中的一片空间,用来存放经常变化的数据。
- 代码示例1:
#include <stdio.h>
int main() {
// 1.定义变量(即在内存中开辟一块空间,空间的大小跟定义的类型有关,空间的名字就是变量名)
int age;// 这一行代码就在内存中开辟一块4个字节的存储区域,它名字叫age;这块存储区域是用来存放int类型的数据的
// 2.初始化:变量第一次赋值称为初始化。
age = 22; //这行代码是把22,放到名字为age的这块存储区域中
return 0;
}
- 代码示例2:
#include <stdio.h>
int main() {
int a; // 定义一个变量:a是变量名,int是变量的类型
// 定义一个变量,未初始化;a的值的可能性:1) 系统的数据;2) 上一个程序遗留的数据;3) 垃圾数。
printf("a=%d\n", a); // a=1606422582,垃圾数据。
// 初始化:就是清空(赋初值,0/NULL)的意思,清理一些垃圾数据。
a = 10; // 将10保存到a中,第一次赋值相当于初始化
a = 20; // 将20保存到a中,以后就叫给变量赋值
int b = 30; // 定义变量的同时初始化
printf("b=%d\n", b);
return 0;
}
- 变量类型:分为字符型char、整数型int、浮点型float/double、指针、结构、联合等类型。
- 变量的作用域
局部变量(内部变量):在函数的内部或者代码块的内部定义的变量。 - 代码示例:
#include <stdio.h>
int main() {
int age = 20;
//代码块代表一个空间,与age的空间并列
{//代码块开始
int a = 5;
printf("a=%d\n", a); //a=5
int age = 25; //在代码块内部可以定义和代码块外部同名的变量
printf("age=%d\n",age); //age=25;
}//代码块结束(此时代码块内部所有的空间会释放)
printf("age=%d\n", age);//age=20;
return 0;
}
-
变量分析题:(注意变量的作用域)
变量的命名:命名需要符合标识符语法要求。
① 必须以 字母 或 下划线 开头(注意:不能以数字开头)
② 包含 字母、下划线 或 数字
③ 大小写敏感的
④ 不能与 关键字 冲突
⑤ 标识符理论上讲,长度无限制,但太长了会被编译器截断-
变量的存储:
① 变量所占的存储空间(字节数):跟变量的类型和编译器环境有关。
② 变量存储单元的第一个字节的地址就是该变量的地址(详细地址/首地址)
③ 任何变量在内存中都是以二进制的形式存储:一个负数的二进制形式,其实就是对它的正数的二进制形式进行取反再加1。
-
【了解】变量在内存中怎么储存?(变量为什么要有类型)
① 只要定义变量,系统就会开辟一块存储空间给我们的变量存储数据
② 越先定义的变量,内存地址越大(从字节地址最大的开始找)
③ 内存寻址是从大到小,高位放在高字节上,低位放在低字节上
④ 变量的地址就是变量所占的存储空间最小的字节地址(即首地址:&变量名称)
计算机中最小储存单元是字节,每个字节都有一个地址。
4. 常量
- "量"表示数据。常量,则表示一些固定的数据,也就是不能改变的数据。
- 定义常量的方法:
const int a=100;
- 易错示例:
10.1;// 双精度 double
5.2f; //单精度 float(注意:与double类型进行区分,小数点后面有 f 才表示是 float 类型,否则是 double 类型)
// 字符型常量(易考点:要注意下面几种写法)
'a'; // 正确写法:占用一个字符
'ab'; // 错误写法:不是单个字符
'李'; // 错误写法:因为一个汉字占2个字符
'\n'; // 正确写法:因为转义字符,表示单个字符
// 字符串常量(注意:字符串常量结尾默认都有一个'\0')
"a"; // 占用两个字符,结尾有一个'\0';
"中国"; // 占用 4 + 1 = 5 个字符;一个汉字占用两个字符,'\0'占用一个字符
【掌握】常用ASCII值: 'A'== 65 、 'a' == 97 、 '0' == 48
5. printf 函数
- printf函数称为格式输出函数,其关键字最末一个字母f即为“格式”(format)之意
- 代码示例:
#include <stdio.h>
int main() {
int hour = 2, minute = 6;
printf("%d:%d\n", hour, minute); // 2:6
// %02d 表示输出的数字宽度为2,如果宽度不够就用0补齐左边
printf("%02d:%02d\n", hour, minute); // 02:06
float value = 3.14f;
// %f 默认会保留6位小数
printf("%f\n", value);//3.140000
// 指定保留多少位小数: %.nf, 其中n就是需要保留多少位小数
printf("%.2f\n", value);//3.14
return 0;
/*
知识点1:指定位宽
%0nd:在%与d之间,0n,n表示输出的数字的宽度,如果不够就用0补齐左边【重点】
%nd:在%与d之间n,n表示输出的数字的宽度,如果不够就用空格补齐左边
%-nd:在%与d之间 -n,n表示输出的数字的宽度,如果不够就用空格补齐右边
知识点2:指定位数
%m.nf:在%与f之间,可以有m.n,m表示输出数字所占的宽度,m表示小数点后面的位数,如果不够会用空格补齐左边
*/
}
-
格式控制符:
其它知识点:
// printf函数打印 % 字符要用 %% 才能打印
printf("5%%2=%d\n",5%2);//结果为:5%2=1
&
是C语言中的一个地址运算符,可以用来获取变量的地址
&
可以获取变量的地址,例如:&num
*
可以根据地址数据找到变量,例如:*(&num)
6. scanf 函数
scanf函数是一个阻塞式函数,程序会停在scanf函数出现的地方,直到接收到数据才会执行后面的代码。
// 使用scanf接收用户从键盘上输入
scanf("%d", &a);//注意: 必须告诉scanf函数变量的详细地址, 才能存储用户输入的数据
// %与d之间可以有数字n,这个n表示接受数据的宽度
scanf("%2d",&a);
-
scanf 函数要点:
① 键盘输入的数据与格式化字符串中的要匹配, 不匹配时scanf函数就会自动终止。
② scanf接收多个数据时,为了防止出错一般加一个分隔符(例如:逗号或空格)
(空格、回车、Tab可以做%c除外的分隔符,因为空格、回车、Tab是字符)
③ 不能在scanf的格式化字符串末尾写上\n。
比如:scanf("%d\n", &a); 这将导致scanf函数无法结束。
7. 运算符
- 算术运算符:+、-、*、/、%
【掌握】% 取模(取余)运算符的注意事项:
① 取模运算符的操作数只能是整数(5%3=2;)
② 余数的符号与最前面的被除数一致(-5%3=-2; 5%-3=2; -5%-3=-2)
③ 如果取模运算的左边小于右边, 那么结果就是左边(2%9=2;) - 自增自减写在前面和后面的区别:
① 如果++写在变量的前面, 那么会先将变量自增再用自增之后的结果参与运算
② 如果++写在变量的后面, 那么会先将变量的值参与运算再将变量自增
③ 总结一句话: ++在前, 先自增再运算, ++在后, 先运算再自增
int b = ++a; //等价于 a=a+1; b=a;
int b = a++; //等价于 b=a; a=a+1;
//【掌握】运算规则:先左++,再赋值,最后右++。
- sizeof()运算符:sizeof可以用来计算 一个变量 、 一个常量 、或 一种数据类型 所占的内存字节数。
- 代码示例:
#include <stdio.h>
int main() {
char c='a';
printf("%d, %d\n", sizeof(10), sizeof(c)); //4, 1
printf("%d, %d\n", sizeof 10, sizeof c); //4, 1
printf("%d\n", sizeof(int)); //4
return 0;
}
- 逻辑运算符:&& 逻辑与 、|| 逻辑或 、! 逻辑非
【掌握】&& 和 || 都具有短路特征:如果前一个表达式能决定最终结果则不会计算后一个表达式。 - 代码示例:
#include <stdio.h>
int main() {
// 逻辑运算符 && 和 || 的短路特征
int a=3,b=4,c=5,r;
r=(a>b)&&(++b>c); // 因为 (a>b) 为假;所以 && 后面的表达式不管是真还是假,最终结果也一定是假。根据短路特征,&& 后面的表达式会自动忽略不参与计算。
printf("%d,%d,%d,%d\n", a, b, c, r); //3, 4, 5, 0
r=(a<b++)||(b<c++);
printf("%d,%d,%d,%d\n", a, b, c, r); //3, 5, 5, 1
// 非短路的逻辑运算符 & 和 |
a=3;b=4;c=5;
r=(a>b)&(b>++c);
printf("%d,%d,%d,%d\n", a, b, c, r); //3, 4, 6, 0
r=(a<b)|(b<c++);
printf("%d,%d,%d,%d\n", a, b, c, r); //3, 4, 7, 1
return 0;
}
8. 进制
- 进制:是一种计数的方式,数值的表示形式
① 十进制:逢十进一 (如:13 == 1 * 10 + 3)
② 八进制:逢八进一 (如:15 == 1 * 8 + 5)
③ 二进制:逢二进一 (如:1101 == 1 * 2 * 2 * 2 + 1 * 2 * 2 + 0 * 2+ 1)
④ 十六进制:逢十六进一 (0 ~ 9 A ~ F d == 13) - 常见的进制转换
① 10进制 转 2进制:除2取余法,把10进制数除以2,然后取得余数的序列,再倒序。
例如:将十进制(97)转换为二进制数(1100001)
② 2进制 转 10进制:所有位的位权相加:101=120+021+1*22
③ 2进制 转 16进制:4合1法。把一个二进制数,整数部分从右向左(小数部分从左向右)4位结合成1位,不足部分补0。
④ 16进制 转 2进制:1拆4法,16进制的1位拆成二进制的4位。
⑤ 2进制 转 8进制:3合1
第二部分:C语言编程练习
1. 加减乘除计算器
/** 加减乘除计算器 (提示:录入1+2输出1+2=3)*/
int test() {
int num1,num2; //定义两个变量,表示操作数
char operator; //定义一个char类型变量,表示要进行操作
printf("请按照如下格式录入(1 + 1)\n"); //提示用录入
scanf("%d %c %d", &num1, &operator, &num2);
int result;
switch (operator) {
case '+':
result = num1 + num2;
break;
case '-':
result = num1 - num2;
break;
case '*':
result = num1 * num2;
break;
case '/':
if (num2 == 0) {
printf("除数不能为零\n");
return 0;
}
result = num1 / num2;
break;
default:
printf("你录入格式错误\n");
break;
}
printf("%d %c %d = %d\n", num1, operator, num2, result);
return 0;
}
2. 编写程序,要求从键盘键入n, 求 1/3+2/4+3/5+...+n-2/n的值
#include<stdio.h>
void main()
{
int n = 0;
double sum = 0;
scanf("%d", &n);
for (int i = 3; i <= n; i++)
{
sum += (double)(i - 2) / i;
}
printf("%lf\n", sum);
}
3. 输入一个年份,判断该年是否为闰年,若为闰年则输出“yes”,否则输出“no”。
#include<stdio.h>
void main()
{
int year = 0;
printf("请输入一个年份:");
scanf("%d", &year);
// 能被4整除,且不能被100整除;或者能被400整除
if((year % 4 == 0 && year % 100 != 0) || year % 400 == 0){
printf("yes\n");
} else {
printf("no\n");
}
}
4. 从键盘输入3个数,将其从小到大排序后输出
#include<stdio.h>
#define ARR_LEN 3 // 数组长度为3
void main()
{
int arr[ARR_LEN] = { 0 };
int temp = 0;
// 读入三个数
for (int i = 0; i < ARR_LEN; i++)
{
printf("请输入第%d个数的值:", i + 1);
scanf("%d", &arr[i]);
}
// 排序
for (int i = 0; i < ARR_LEN - 1; i++)
{
for (int j = 0; j < ARR_LEN - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
// 输出结果
for (int i = 0; i < ARR_LEN; i++)
{
printf("arr[%d] = %d\n", i, arr[i]);
}
}
5. 从键盘输入两个数,交换两个数的值,再输出至屏幕
#include<stdio.h>
void main()
{
int a = 0;
int b = 0;
int temp = 0;
printf("请输入两个整数:");
scanf("%d%d", &a, &b);
printf("交换之前的值是:a=%d, b=%d\n", a, b);
temp = a;
a = b;
b = temp;
printf("交换后的值是:a=%d, b=%d\n", a, b);
}
6. 编写一个函数,通过指针实现两个数的交换
#include<stdio.h>
void swap(int *pa, int *pb)
{
int temp = 0;
temp = *pa;
*pa = *pb;
*pb = temp;
}
int main()
{
int a = 3;
int b = 5;
int *pa = &a;
int *pb = &b;
void(*pswap)() = swap;
// 交换之前
printf("交换之前 a=%d, b=%d\n", a, b);
// 交换
pswap(pa, pb);
// 交换之后
printf("交换之后 a=%d, b=%d\n", a, b);
return 0;
}
7. 输入一串字符,统计其中的字母、空格、数字及其他字符的个数
#include<stdio.h>
#define STR_LEN 100
int main()
{
char str[STR_LEN] = { '\0' };
int alpha = 0;
int blank = 0;
int digit = 0;
int other = 0;
// 输入一串字符
printf("请输入一个字符串:\n");
gets_s(str, STR_LEN);
// 计算每种类型的字符的个数
for (int i = 0; i < STR_LEN && str[i] != '\0'; i++)
{
if (('a' <= str[i] && str[i] <= 'z') || ('A' <= str[i] && str[i] <= 'Z'))
{
alpha++;
}
else if ('0' <= str[i] && str[i] <= '9')
{
digit++;
}
else if (str[i] == ' ')
{
blank++;
}
else
{
other++;
}
}
// 输出统计的结果
printf("字母有 %d 个\n空格有 %d 个\n数字有 %d 个\n其他有 %d 个\n", alpha, blank, digit, other);
return 0;
}
8. 求最大公约数
/** 1.【掌握】直接遍历法 */
int maxCommonDivisor(int a, int b) {
int max = 0;
for (int i = 1; i <=b; i++) {
if (a % i == 0 && b % i == 0) {
max = i;
}
}
return max;
}
/** 2.辗转相除法 */
int maxCommonDivisor(int a, int b) {
int r;
while(a % b > 0) {
r = a % b;
a = b;
b = r;
}
return b;
}
// 扩展:如何求最小公倍数
// 最小公倍数 = (a * b)/最大公约数
9. 冒泡排序
冒泡排序是从小到大排序,可以形象的理解为:水中的气泡从下往上冒出,越往上气泡越大,即冒泡排序是从小到大排序。
/**
* 【冒泡排序】:相邻元素两两比较,比较完一趟,最值出现在末尾
* 第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n个元素位置
* 第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n-1个元素位置
* …… ……
* 第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第2个元素位置
*/
void bublleSort(int *arr, int length) {
for(int i = 0; i < length - 1; i++) { //趟数
for(int j = 0; j < length - i - 1; j++) { //比较次数
if(arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}