C语言学习笔记

第一节课 常见概念(1)

Q&A:

1. C语言发展史, 学习C语言的必要性?

  C语言最初是作为Unix系统的开发工具而发明的;

2. 编译和链接是什么? 过程是什么?

  编译就是将每个源文件(.c)通过编译器变成对应的目标文件(.obj), 链接是将这些文件与链接库通过连接器处理生成可执行程序(.exe);

3. 各个编译器我该使用什么? vs的优势?

  使用VS2022, 因为它是一个主流的集成开发环境, 且在企业中较为普遍, 基本不用额外配置环境, 功能强大上手容易;

4. main函数究竟是啥?

  main函数是程序的入口, 有且仅有一个, 在一个工程中有多个源文件, 但main函数只能有一个;

5. 关键字有哪些? 要背诵吗?

  关键字是留给C语言使用的, 较为主流的关键字有32个, 无需背诵;

5d306121ea88417b99b8212acf62aef3.png

6. 字符? ASCII码? 字符计算?

  在键盘上可以敲出各种字符, 如: a, q, @, #等, 这些符号被称为字符, 通常用单引号括起来, 用asc编码代表字符, 字符计算就是用ASCII码值进行计算;

7. C语言的字符串? 字符和字符串有何区别?

  使用双引号括起来的一串字符被称为字符串, 字符串在结尾隐藏着一个\0, 它是字符串结束的标志, 如"abc", 它其实是一个4个元素的数组, \0也可自己添加, 表示这个数组的结束;

8. sizeof和strlen? 竟然有坑?

  sizeof是统计类型或表达式的长度的, 若为表达式, 则表达式不计算, 且结果为size_t类型, 占位符为%zd;
  strlen是统计字符串中\0之前的元素的个数, 如strlen("abc"), 它的值就为3, 若要使用则需头文件string.h;

9. 转义字符是什么? 平时怎么用呢?

  转义字符可以转变字符的意思, 通常使用的就是

  \n 换行 \t 制表 \某个字符(可以表示那个字符, 以免产生歧义);

第二节课 常见概念(2)+数据类型和变量(1)

Q&A:

1. 两种特殊的转义字符:\ddd 和 \xdd是什么?

  分别是八进制和16进制, ''和\就类似于求导与积分的关系, 两者结合在一起就抵消了, 如'\130'表示的就是字符x, 即表示ASC码: 1*8^2+3*8+0=84的字符, 就是x; 

2. 指出里面哪些是转义字符,并给出运行结果printf("%zd\n", strlen("c:\\test\128\abcd.c"));

  \\斜杠号, \12, \a蜂鸣符; 结果是15;

3. C语言是结构化语言,他有哪几种结构?控制语句是什么?

  三种结构: 顺序结构, 选择结构, 循环结构; 控制语句可以控制程序的流程,
  条件判断语句: if, switch;
  循环执行语句: do-while, while, for;
  转向语句: break, goto, continue, return;

4. 为什么要写注释? 编译器是如何看待注释掉的代码的呢?VS2022如何批量注释代码呢?

  注释是给程序员看的, 编译器会直接去除被注释掉的代码, \**\来批量注释多行代码;

5. C语言也有布尔类型?如何使用?

  有, 得包含头文件stdbool.h, 类型是_Bool或bool, true就是1, false就是0;

6. sizeof不是函数? sizeof还有返回值?? sizeof是在编译器工作的哪个时期运算的?

  是关键字, 操作符, 返回值是size_t类型, 是字节大小, 在编译期间就把sizeof处理掉了, 所以在运行期间就不会执行表达式, 即sizeof表达式, 表达式是不真实参与运算的,根据表达式的类型来得出⼤⼩;

7 .下面这段代码为什么不输出3 ?为什么?
 

#include <stdio.h>

int main()
{
    int a = 2;
    sizeof(++a);
    printf("%d", a); 

    return 0;
}

  sizeof后的表达式不参与运算, 即sizeof(++a)算出来为4(int), ++a不运算, a就是2, 所以输出2;

8. 了解一下头文件 limits.h 和 float.h 

  limits.h整数类型的取值范围;float.h浮点数类型的取值范围;

9. C语言的数据类型有哪四种? 不同的数据类型有不同取值范围?  char类型是有符号还是无符号?

  字符型, 整形, 浮点型, 布尔类型;char类型根据当前系统, 有可能有符号, 有可能没符号;

10. 变量创建的局部有限性原则究竟是什么?

  在自己生存的大括号里面的;比如创建在main函数(main函数大括号内, 其他所有大括号外)里的变量, 在main函数里都可用;

11. 局部变量和全局变量分别存在内存中的哪个位置?堆内如何存储变量?

  局部变量存在于栈区, 全局变量存在于静态区; 动态内存管理;

第三节课 数据类型和变量(2)

Q&A:

1. 除号(/)的玄机——浮点数除法和整型除法的注意事项?

  浮点数除法: 两个运算数必须⾄少有⼀个浮点数, 返回结果为浮点数;
  整型除法: 两个整数的除法, 只会返回整数部分,丢弃⼩数部分;

2. 前置后置自增自减究竟怎么算?

  比如++a, 就是先自增1再使用a; a++, 就是先使用a再自增1;

#include <stdio.h>

int main()
{
    int a = 10;
    
    printf("%d\n", ++a);
    printf("%d\n", a++);

    return 0;
}

  如图代码, 第一个打印出来的就是11, 第二个打印出来的是10;

3. 强制类型转化是什么?

  语法形式是: (类型)
  比如有一个float类型变量a = 20.0, 我们可以int b = (int) a; 这样当我们打印出b的值时, 就是20, 只取整数部分, 不会四舍五入;

【详解printf函数!】

4. 占位符是个啥?多个占位符怎么一起使用?

  占位符就是一个占住位置的符号, 它可以被其他的值代入; 一个输出文本中可以放多个占位符, 它们与参数是一一对应的关系;

5. 如何限定输出最小宽度?

#include <stdio.h>

int main()
{
    int a = 123;

    printf("%5d", a);
    printf("%-5d", a);

    return 0;
}

  如上面这段代码, 第一个输出的是"  123", 第二个输出的是"123  "; %5d表示占5个位置, 不足的使用空格补齐;

  对于小数, 同样成立, 但是小数点.也占一个位置; 可以使用%.mf来限制小数点后的个数, m就是小数点位数;

  对于字符串, 同样成立, 例如, %.5s, 表⽰只输出字符串“hello world”的前5个字符, 即“hello”;

  若超过这个最小宽度, 还是会全部都输出;

6. printf输出还能控制左对齐或者右对齐?

  默认右对齐, 在百分号%后面加一个-即可实现左对齐;

7. %*.*f  是个啥?

  *可以代替最小宽度和小数位数, 如%6.2f就是表示最小宽度为6, 小数点位数为2的数, 若printf("%*.*f", 6, 2, 1.445); 打印的结果就是"  1.45", 对于小数位数, 会四舍五入;

【详解scanf!】

8. 什么?vs里不让用scanf?该怎么办!

  vs认为scanf不安全, 需要在文件头处#define _CRT_SECURE_NO_WARNINGS 1;

9. 缓冲区是啥?竟和scanf有关系?

  scanf() 处理数值占位符时, 会⾃动过滤空⽩字符, 包括空格、制表符、换⾏符等;

  缓冲区是用户键盘输入存放的地方, scanf()处理用户输入的时, 当等到按下回⻋键后,按照占位符对缓存区进⾏解读; 解读⽤⼾输⼊时, 会从上⼀次解读遗留的第⼀个字符开始, 直到读完缓存, 或者遇到第⼀个不符合条件的字符为⽌;

#include <stdio.h>

int main()
{
    int x;
    float y;

    scanf("%d", &x);
    printf("%d\n", x);
    scanf("%f", &y);
    printf("%f\n", y);
    
    return 0;
}

  如若用户输入" -13.45e12# 0", 那么第一次会读取到-13, 第二次会读取到.45e12 , 这是采⽤科学计数法的浮点数格式; 后⾯的 # 不属于浮点数的有效字符, 所以会停在这⾥;
  第一次会输出-13, 第二次会输出44999999880;

10. 浮点数怎么计算机还不能精确存储?

  见上图代码, 第二次会输出44999999880, 因为0.45不能精确保存, 大概是0.44999999880;

11. scanf返回值表示什么?有什么含义?

  scanf的返回值表⽰成功读取的变量个数; 

  如果没有读取任何项, 或者匹配失败, 则返回 0;
  如果在成功读取任何数据之前, 发⽣了读取错误或者遇到读取到⽂件结尾, 则返回常量EOF(-1);

#include <stdio.h>
 
int main()
{
    int a = 0;
    int b = 0;
    float f = 0.0f;
    int r = scanf("%d %d %f", &a, &b, &f);

    printf("a = %d b = %d f = %f\n", a, b, f);
    printf("r = %d\n", r);
    
    return 0;
}

  情况一: 正常输入三个数, 如"1 2 3.14", 那么最后就会输出r = 3;
  情况二: 正常输入两个数, 第三个数的时候按ctrl + z, 最后就会输出2;
  情况三: 在vs环境中, 连续按三次ctrl + z, 最后就会输出-1;

12. 多组数据输入如何用scanf实现?

  while (scanf("%d", &n) != EOF) 这种写法叫做多组输入;(在vs环境中, 只有连按三次ctrl + z才能输出EOF停止输入;)

13. scanf里使用占位符%c,要多留个心眼——小心空白字符被吃掉

  空白字符也算一个字符, 若使用%c需要注意空白字符, 它会存储空白字符;
  若要跳过字符前的空白字符, 可以使用scanf(" %c", &ch) , 即 %c 前加上⼀个空格, 表⽰跳过零个或多个空⽩字符;

14. scanf里使用%s,遇到空白字符会停止吗?——存储到变量里后会自动存储'\0'

  scanf中使用%s, 从当前第⼀个⾮空⽩字符开始读起, 直到遇到空⽩字符(即空格、换⾏符、制表符等)为⽌;

  另外, scanf()将字符串读⼊字符数组时, 不会检测字符串是否超过了数组⻓度; 所以, 储存字符串时 , 很可能会超过数组的边界, 导致预想不到的结果; 为了防⽌这种情况 ,, 使⽤读⼊字符串的最⻓⻓度, 即写成 %[m]s , 其中的 %s 占位符时, 应该指定 [m] 是⼀个整数, 表⽰读取字符串的最⼤⻓度, 后⾯的字符将被丢弃;

#include <stdio.h>
int main() 
{
    char name[11];

    scanf("%10s", name);

    return 0;
}

  例如, 在上图中, 若我输入了"abcdefghijklmn", 数组中, 0-9这十个字符分别是abcdefghij, 第十一个字符是'\0'; 

15. 赋值忽略符有什么用?

  只要把 * 加在任何占位符的百分号后⾯, 该占位符就不会返回值, 解析后将被丢弃。

#include <stdio.h>

int main()
{
    int year = 0;
    int month = 0;
    int day = 0;

    scanf("%d%*c%d%*c%d", &year, &month, &day);

    return 0;
}

  例如, 在上图中, 我可以输入"2024 6 10"或者"2024-6-10", 都可以成功的将它们分别正确的读入;

第四节课 分支和循环(1)

Q&A:

【if语句】

1. 真假与01的关系?

  0代表假, 非0即为真, 即1为真;

2. 这个写法正确吗:else (a < 10)

  不正确, else后面不能跟条件;

3. if的分支有多条语句怎么办?

  复合语句, 用一个大括号括起来;

4. 悬空else问题?

  else总是跟最近的那个if匹配, 无关于缩进;

5. 要注意代码规范,善用tab键,增强代码可读性噢

  对;

【关系操作符】 >、<、>=、<=、==、!=

6. 关系操作符的优先级和结合性?

  关系运算符是从左到右计算; 优先级高的结合;

7. a = 5 和 a == 5 的区别?

  a = 5是给a赋值为5, a == 5是判断a的值是否是5, 若为真返回1, 若为假返回0;

8. x < y < z和 x < y && y < z是等价的吗?为什么推荐 5 == a 这种写法?

  不等价, x < y < z会从左往右计算, 左边计算结果为0或1, 通常不符合我们的意思; x < y && y < z才与数学上一致; 因为若写为5 = a, 编译器会检查出我们的错误, 若写为a = 5, 编译器会编译通过, 我们也很难查出来错误;

【条件操作符】

9. 三目运算符是什么?(可以用,但不要嵌套太多噢)

  exp1 ? exp2 : exp3, 先判断exp1, 若成立, 则计算exp2, 且它的结果是整个表达式的值; 若不成立, 则计算exp3的值, 结果为整个表达式的值;

【逻辑运算符】

10. 有哪些逻辑操作符?你能画出他们的真值表吗?

  与(&&), 或(||), 非(!);
  与: 有假即假, 全真为真;
  或: 有真即真, 全假为假;
  非: 真变假, 假变真;

11. 短路是什么?有哪些情况会短路?

  若左边的表达式满⾜逻辑运算符的条件, 就不再对右边的表达式求值, 这种情况称为“短路”。
  在与操作符中, 左侧的表达式为假, 就不再进行右侧表达式的计算;
  在或操作符中, 左侧的表达式为真, 就不再进行右侧表达式的计算;

12. C语言如何实现判断闰年呢?

  有两种闰年, 1. 该年为4的倍数但不是100的倍数;
                      2. 该年是400的倍数;

#include <stdio.h>

int main()
{
    int year = 0;

    if ((year % 4 == 0 && year % 100 != 0)
        || year % 400 == 0)
    {
        printf("%d年为闰年", year);
    }

    return 0;
}

【switch语句】

13. 如何把if-else语句改写为switch语句?

  switch语句是if-else语句的特殊形式, ⽤于判断条件有多个结果的情况;

switch(整形表达式)
{
    case 整型常量表达式: 
    case 整型常量表达式:
    default 整形常量表达式: 
}

14. switch后的表达式类型可以是什么?

  整型, 例如short, int, long, long long;

15. case后面值的类型可以是什么? (要注意case和值之间一定要有空格噢)

  整型常量表达式,如1+2或者是整数;

16. default是什么? switch的运行顺序?(要理解switch的匹配逻辑)

  若所有case情况都不满足, 就会执行default; swtich语句就像公交车, case就是很多个站口, 我们可以从某一站上车, 它会一直开下去, 若是不使用break语句, 我们就会从进入的那个case, 一直执行到最后的case, default就是没上车就会执行;

第五节课 分支和循环(2)

Q&A:

【while循环】

1. 区分if和while的异同

  if和while都是判断条件满足, 但if只会执行一次, while是若条件满足就会一直循环;

2. 逆序打印一个正整数(进阶: 用函数递归实现)

#include <stdio.h>

// 逆序输出整数

int main()
{
    int n = 0;
    
    scanf("%d", &n);
    do
    {  
        printf("%d", n % 10);
        n /= 10;
    } while (n > 0);

    return 0;
}
#include <stdio.h>

// 递归倒序输出整数

void rev(int n)
{
    if (n > 0)
    {
        printf("%d", n % 10);
        rev(n / 10);
    }
}

int main()
{
    int n = 0;

    scanf("%d", &n);
    rev(n);

    return 0;
}

【for循环】

3. 理解for循环的执行流程【重要】, 可以画图展示。

  b2b22d2c73a84cf0bcce95a30d6dd88a.png

  for (exp1; exp2; exp3)
        初始化; 判断; 调整

4. while循环和for循环对比?如何选择使用哪个?(已知迭代次数或者需要遍历可以先选for。一般for和while可以转换,但主要原则是优先考虑代码可读性,且避免过于复杂的条件表达式)

  已知迭代次数或者需要遍历可以先选for, 不知道要循环几次选择while, 如判断一个数的位数或者是逆序输出; while循环也能做到for循环的效果, 但是不如for循环简洁明了, 同时也方便代码的维护;

5. do-while和while的区别?如何选用?(最大的区别就是do-while一定会循环一次,如何选择就看是否需要保底先执行一次)

  do-while是先执行一次代码块, 再判断是否满足条件, while是先判断是否满足条件再执行; 一般根据题目的边界情况来判断是否要用do-while, 如判定数的位数, 0这个边界情况用do-while判定;

6. break和continue如何理解?作用范围?

  break是打破整个循环; continue是结束当次循环, 但是循环还会继续运行; 作用范围就是所在代码块(大括号)的循环;

7. 实战: 素数判断代码实现

#include <stdio.h>
#include <math.h>

int main()
{
    int m = 0, n = 0; // 区间
    int flag = 1; // 判定是否是素数, 假定是素数

    scanf("%d%d", &m, &n);
    if (m == 2)
    {
        printf("2是素数\n");
    }
    for (int i = (m % 2 == 0? m + 1: m); i <= n; i += 2) // 偶数不可能是素数
    {
        flag = 1; // 每个数都要重置flag, 确保一开始判定为素数
        for (int j = 2; j < sqrt(i); j++)
        {
            if (i % j == 0)
            {
                flag = 0; // 不是素数
                break;
            }
        }
        if (flag == 1)
        {
            printf("%d是素数\n", i);
        }
    }

    return 0;
}

8. goto语句的缺陷(大量使用goto语句,会降低代码可读性,加大维护成本,与结构化编程的原则相违背。大多数情况下,使用结构化编程是可以替代goto的)(还记得啥是结构化编程吗哈哈)

  结构化编程: 顺序, 分支, 循环; 理论上仅使用这三种结构, 如果处理的好是可以解决所有问题的;

9. 实战:用goto语句实现一个关机小程序

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
	char input[20] = { 0 };
	system("shutdown -s -t 60");

	while (1)
	{
		printf("你的电脑将在1分钟后关机, 请输入我是猪来取消关机\n");
		scanf("%s", input);
		if (strcmp(input, "我是猪") == 0)
		{
			system("shutdown -a");
			break;
		}
	}

	return 0;
}

10. 实战: 最大公约数和最小公倍数代码实现

#include <stdio.h>

int main()
{
	int a = 0, b = 0;
	int rem = 0;
    
    scanf("%d%d", &a, &b);
	while(rem = a % b) // 辗转相除法
	{
		a = b;
		b = rem;
	}
    printf("gcd = %d\n", b);
    printf("lcm = %d\n", a * b / gcd);
	
	return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值