【比特鹏哥C语言_1.初识C语言】
第一节 第一个C语言项目
C是一种通用的计算机编程语言,在底层开发中得到了广泛应用。设计目标:提供了一种能够采用简便的编译方法,并在高效地处理低级存储器的同时生成少量机器码,并且无需依赖外部运行环境的支持。
在Visual Studio 2013至2019版本中可以进行C语言程序的开发与处理。
建立项目:依次执行以下步骤——创建新项目文件→输入源代码内容→进行代码编写→运行并调试程序。



- 新建源文件:xxx.c 源文件 xxx.h 头文件




![//写代码
//1.写出主函数(main函数)
//C语言是从主函数的第一行开始执行的
//printf-库函数-在屏幕上打印信息
//printf需要引用头文件stdio.h
#include <stdio.h>
int main()//int是 int是函数的返回类型,int是整型//main是函数名
{
printf("谦子木\n");//\n是换行
return 0;
}//{}是函数体
//编译+链接+运行代码
//快捷键:Ctrl+F5 或 菜单→调试→开始执行不调试
//如果程序执行太快,看不到窗口
//则:解决方案资源管理器→右键项目→属性→链接器→系统→子系统改成控制台
/*重点*/
//一个工程中可以有多个.c文件,但多个.c文件中只能有一个主函数]()
工具→选项→可以调节代码字体大小
第二节 数据类型
一、常见数据类型
计算机语言通过编写程序作为连接工具用于解决生活中的实际问题。
计算机必须具备描述生活中的问题的能力。
例如:商品价格45.6元属于小数,在C语言中被称作浮点型变量。
年龄作为整数,在C语言中被称为整型变量。

//2.数据类型
int main()
{
//字符型
char ch = 'a';
//整型
int age = 20;
//短整型
short num = 10;
//单精度浮点型
float weight = 55.5;
//双精度浮点型(精度更高)
double d = 0.0;
return 0;
}


二、printf的输出格式
| printf的输出格式 | 释义 |
|---|---|
| %d | 按整型实际长度输出(十进制) |
| %md | m为指定的输出字段的宽度。若数据的位数<m,则左端补空格;若>m,则突破m的限制按实际位数输出 |
| %ld | 长整型 |
| %u | 无符号十进制输出整型,长整型%lu;指定字段宽度%mu |
| %o | 无符号八进制输出整型,长整型%lo;指定字段宽度%mo |
| %x | 无符号十六进制输出整型,长整型%lx;指定字段宽度%mx |
| %c | 输出一个字符 |
| %s | 输出字符串 |
| %ms | 字符串占m列,若数据的位数>m,全部输出,<m,左补空格(没有-右对齐) |
| %-ms | (有-左对齐) |
| %m.ns | 输出占m列,但只取字符串左端n个字符。这n个字符右对齐,左补空格 |
| %-m.ns | 和上面相比,变成左对齐。若n>m,则自动取n值,保证n个字符正常输出 |
| %f | 单浮点型(对应float)。整数部分全输出,小数部分输出6位 |
| %lf | 双浮点型(对应double) |
| %m.nf(%-m.nf) | 输出占m列,其中n位小数,右对齐(左对齐) |
| %e | 指数形式输出 |
三、计算机中的单位:
bit-比特位;byte-字节-8bit;

计算机为什么要划分多种数据类型:为了划分多种类别以便更好地实现信息管理与数据处理功能。由于各类别所需存储空间量各有差异,在合理分类后能够有效提升存储资源的使用效率。
第三节 初识常量变量
生活中的一些数据:
- 有些数据不能变:血型,性别,圆周率
- 有些可变:工资,年龄,体重
一、C语言怎么描述常量和变量:
1.常量:不能改变的量
2. 变量:能被改变的量
//创建的一个变量
//类型 变量的名字=20//20是初始值
int age = 20;//也可以不设置初始值,但不推荐这种方法
double weight = 75.3;
age = age + 1;
weight = weight - 10;
printf("age=%d\n", age);//%d对应整型
printf("weight=%lf\n", weight);//%f对应float %lf对应double
return 0;
}
二、变量的分类:
1.局部变量 :在main函数的{}内的定义的变量
2.全局变量:在主函数main的{}外定义的变量
在局部变量与全局变量名称发生冲突时,在这种情况下优先采用局部变量。避免让全局和局部变量的名称相同。
int a = 10;//全局变量
int main()
{
//局部变量- {}内部定义的
int a = 100;
printf("%d\n", a);
return 0;
}

这个报错通常建议避免使用scanf_s函数,因为它仅适用于特定的编译环境(如vs),而未遵循C语言标准的规范性库函数要求;因此不建议采用这种方法。



第四节 变量的作用域和生命周期
一、作用域
在什么地方起作用也就是它的作用域所在的位置
1. 局部变量作用域举例
图一中将变量先于输出进行声明是可以显示的;而图二中将后续的变量放在前面进行定义则无法显示;此外还需考虑图三和图四的情况




2. 全局变量作用域举例

二、生命周期
该变量在其创建与销毁之间所持续的时间段内发挥作用。当进入局部作用域时开始其生命,在退出该作用域时结束其生命。全局变量在整个程序运行过程中保持有效。

第五节 常量
一、常量的分类
- 字面常量
- const 修饰的常变量
- #define定义的标识符常量
- 枚举常量
1.字面常量
//1.字面常量(就是直接常量)
//直接常量的书写形式决定了它的类型
int main()
{
3.14;//实数型常量
10;//整型常量
"abcdef";//字符串常量
'a';//字符常量
return 0;
}
2.const修饰的常变量
“值”不能变的变量




3.#define定义的标识符常量


4.枚举常量
可以一 一列举的常量


第六节 字符串+转义字符+注释
一、字符串
由一对‘双引号’包围的一串字符构成字符串。注意:字符串的结束标志为ASCII码值零(\0),但其长度不包含该结束符本身。在构造字符数组时需特别留意这一特性。







提示:只需点击某一行并使用快捷键组合 Ctrl+C 后再按快捷键 Ctrl+V 即可完成复制操作。
二、转义字符
转义字符:就是转变了字符原来的意思
1. 常见转义字符
| 转义字符 | 释义 |
|---|---|
| " | 用于表示一个字符串内部的双引号 |
| \ |用于表示一个反斜杠,防止它被解释为一个转义序列符 | |
| \n | 换行 |
| \b | 退格符 |
| \r | 回车 |
| \t | 水平制表符,相当于按Tab键 |
| ? | 在书写连续多个?时,防止被解析为三字母词 |
| ` | 用于表示字符常量` |
| \ddd | ddd表示1~3个八进制 数字。如\130表示x |
| xdd | dd表示2个16进制数字。如、x30表示字符0 |
假如我们要在屏幕上打印一个目录:c:\code\test.c
若将代码写成:
int main()
{
printf("c:\code\test.c");
return 0;
}
实际运行结果为:

为了使目录列表正常显示,在导致计算机误认为有转义字符的地方添加一个反斜杠符号于该位置,并避免被系统解析为转义字符。
int main()
{
printf("c:\ code\ test.c");
return 0;
}

2.笔试题
//程序输出什么?
#include <stdio.h>
int main()
{
printf("%d\n",strlen("abcdef"));
//\32被解析为一个转义字符
printf("%d\n",strlen("c:\test\328\test.c"));
return 0;
}
//答:输出6和14
//因为\t是一个字符,\32是一个字符,注意\ddd中的d是从1-3,所以不在里面,没有被转义
三、注释
//属于C++式的注解风格
/**/代表的是C语言风格;其主要特点为不具备内联注解功能;当遇到以下情形时会报错
/*
/*hhh*/
fff
*/
第七节 初识选择语句&&循环语句
一、选择语句
#include <stdio.h>
int main()
{
int coding=0;//输入的值
printf("你会去敲代码吗? (选择1 or 0):>");
scanf("%d",&coding);
if(coding==1)//如果输入的值为1
{
printf("坚持,你会有好offer\n");
}
else//如果输入的值为0
{
printf("放弃,回家卖红薯\n");
}
return 0;
}

二、循环语句
//30000代码-找个不错工作
int main()
{
int line=0;//定义变量line
while(line<30000)
{
printf("写代码:%d\n",line);//如果line<30000,执行语句,不满足,则退出循环
line++;
}
if(line==30000)
{
printf("好offer\n");
}
return 0;
}

第八节 初识函数和数组
一、函数
int main()
{
int num1 = 0, num2 = 0, sum = 0;
printf("输入两个操作数:>");
scanf("%d%d", &num1, &num2);
sum = num1 + num2;
printf("sum=%d\n", sum);
return 0;
}
上述代码,写成函数如下:
int add(int x, int y)//创建整型x和y,来接收传递过来的num1和2//add前面的int是返回值的类型
{
int z = 0;
z = x + y;
return z;//将z的值返回给函数add
}
int main()
{
int num1 = 0, num2 = 0;
printf("输入两个操作数:>");
scanf("%d%d", &num1, &num2);
int sum = add(num1, num2);//将num1和2的值传递给x和y,将函数add的值传递给sum
printf("sum=%d\n", sum);
return 0;
}
二、数组
要存储1-10的数字,怎么存储?
数组:一组相同类型元素的集合
数组定义:
int arr[10]={1,2,3,4,5,6,7,8,9,10};//定义一个整型数组,这个数组最多放10个元素
char ch[5]={'a','b','c'};//字符型数组,不完全初始化,剩余的默认为0

数组访问规则:数组是由下标来访问的
第九节 初识操作符
一、算数操作符:+ - * / %(取模,或称取余,取余数)
二、移位操作符:>>(右移) <<(左移)
int main()
{
int a=2;//将a存放为整型变量
int b=a<<1;//定义b为a左移一位
return 0;
}
左移操作符:移动的是二进制位
a的值为2,换算成二进制就是10,又因为a是放在整型 里,整型是4个字节 ,一个字节是8个比特 位,所以要写出a的二进制序列 ,要写出32个比特位,即:
00000000 00000000 00000000 00000010
向左移动一位
0 00000000 00000000 00000000 00000100 //注最左端的移出,最右端补了一个0
补完之后就是100,换算成十进制就是4,所以b=4
三、位操作符:& 按位与 | 按位或 ^ 按位异或
四、赋值操作符:= += -= *= /= &= ^= |= >>= <<=
int main()
{
int a = 1;
a = a + 5;//相当于:
a += 5;
a = a - 3;//相当于:
a -= 3;
a = a % 3;//相当于:
a %= 3;
return 0;
}
五、单目操作符
什么是双目操作符:比如a+b; +有2个操作数
单目操作符:只有一个操作数
1. 单目操作符总结
| 单目操作符 | 作用 |
|---|---|
| ! | 逻辑取反 ,把真变成假,把假变成真;0为假,非0为真 |
| - | 负值 |
| + | 正值 |
| & | 取地址 |
| sizeof | 操作数的类型长度(以字节为单位) |
| ~ | 对一个数的二进制 按位取反 |
| – | 前置、后置– |
| ++ | 前置、后置++ |
| * | 间接访问操作符(解引用操作符) |
| (类型) | 强制类型转换 |
2. !的讲解
int main()
{
// !的语法讲解
int a=112;
printf("%d\n",!a);//输出0
int b=0;
printf("%d\n",!b);//输出1
//应用场景
if(a)
{
//如果a为真,做事
}
if(!a)
{
//如果a为假,做事
}
return 0;
}
3. + -的讲解
int main()
{
// + -的讲解
a=-5;//将负五赋值给a
a=+a;//将a的绝对值赋给a
return 0;
}
4. sizeof的讲解
int main()
{
//sizeof是一个操作符,不是函数
//它是用来计算类型、变量、数组等的大小的(以字节为单位,一个字节8比特)
int a=10;
printf("%d\n",sizeof(int));//打印出4,因为整型是4的字节
printf("%d\n",sizeof(a));//打印出4,因为整型是4的字节
int arr[10]={0};
printf("%d\n",sizeof(arr));//结果是40,因为每个元素是一个int,4*10=40
//计算的是数组的总大小
printf("%d\n",sizeof(arr[0]));//结果是4,因为计算的是元素0的大小
//计算元素的个数:
int sz=sizeof(arr)/sieof(arr[0]);
printf("%d\n",sz);//输出10,因为一共有10个元素
return 0;
}
5. ~的讲解
int main()
{
// ~是按位取反(二进制);把所有二进制中的数字,1→0,0→1(符号位同样对待)
int a=0;
printf("%d\n",~a);//结果为-1
//整数a=0,则二进制:
//00000000 00000000 00000000 00000000 a
// ~ 是按位取反,则二进制变为:
//11111111 11111111 11111111 11111111 ~a(补码)
//printf输出的是原码,即:
//10000000 00000000 00000000 00000001(即十进制是负一)
return 0;
}
(1)整数在内存中存储规则:整数在内存中存储的是补码(注:补码的补码为原码)
一个整数可用三种编码方式表示:原码、反码和补码;-1的原编码为1(二进制),其对应的十进制值为-1;该方法仅针对负数设计;其中符号位位于最左端:当数值为正时符号位设为**' ' ' ' ' ' ' ' ' ' ', 负值则设为·**;对于一个整数值N,在转换成相应的编码之前需先确定其绝对值部分的所有二进制位数目以确保正确性
6.前置、后置++ 前置、后置–
(1) 前置++:先++,后使用; 后置++:先使用,后++
int main()
{
int a=10;
int b=++a;//前置++
int c=10;
int d=c++;//后置++
printf("%d\n",b);//输出11
printf("%d\n",a);//输出11
printf("%d\n",c);//输出11
printf("%d\n",d);//输出10
return 0;
}
7.强制类型转换

int main()
{
//怎么消除这个警告
//强制类型转换
int a = (int)3.14;
printf("%d\n", a);//输出3
return 0;
}
六、关系操作符
| 关系操作符 | 名称 |
|---|---|
| > | 大于 |
| >= | 大于等于 |
| < | 小于 |
| <= | 小于等于 |
| != | 不等于 |
| == | 等于 |
七、逻辑操作符
| 名称 | 作用 | ||
|---|---|---|---|
| && 逻辑与 | 两个都为真,才为真 | ||
| 逻辑或 | 两个都为假,才为假 |
int main()
{
int a=3;
int b=5;
int c=a&&b;
printf("%d\n",c);//输出1,因为a为真,b为真,两个都为真,所以c为真
return 0;
}
八、条件操作符(三目操作符):exp1 ? exp2 : exp3 表达式1 ? 表达式2 : 表达式3
其意义在于:当条件 exp1 得到满足时(即 exp1 成立),系统将执行特定的操作以计算 exp2;而当条件 exp1 未得到满足(即 exp1 不成立)时,则采取不同的操作以计算 exp3。整个流程中涉及的操作步骤及其相应的处理逻辑均按照给定的条件进行执行。
int main()
{
int a=0,b=3,max=0;
if(a>b)
max=a;
else
max=b;
//上述if语句和下面一行代码相等
max=a>b?a:b;//a>b若成立,将a的值给max;否则将b的值给max
return 0;
}
九、逗号表达式:exp1, exp2, exp3, …expN
定义:由用逗号分隔的一组表达式构成的序列,在求值时将从左侧开始逐步计算,最终结果由最后一个操作数决定。对于数学公式E = mc^2这样的单个变量情况,则直接返回该变量的值;而对于像a, b, c这样的多元素列表,则逐个计算后取c的值。
int main()
{
int a=0;
int b=3;
int c=5;
//a=5 c=1 b=3
int d=(a=b+2,c=a-4,b=c+2);
printf("%d\n",d);//结果为3
return 0;
}
十、下标引用、函数调用和结构成员
1. [ ] () . - >
[ ]是下标引用操作符
//下标引用操作符
int main()
{
int arr[5]={0};
printf("%d\n",arr[5]);
return 0;
}
( )是函数调用操作符
int main()
{
//调用函数的时候,函数名后面的()就是函数调用操作符
printf("hello\n");
return 0;
}
2.**& * . ->**这四个操作符在接下来课程中再详细讲述
第十节 初识常见关键字
一、C语言提供的关键字的特点
- C语言提供的,不能自己创建关键字
- 变量名不能是关键字
二、关键字总结
| 关键字 | 作用 |
|---|---|
| char | 字符类型 |
| double | 双精度 |
| float | 单精度浮点型 |
| int | 整型 |
| long | 长整型 |
| short | 短整型 |
| signed | 有符号的,如10 -20 |
| unsigned | 无符号的 |
| void | 空 |
| auto | 是自动的意思;每个局部变量都是auto修饰的 |
| break | 在 循环 、Switch语句中会用到 |
| case | Switch case语句中会用 |
| switch | Switch语句 |
| const | 常变量 |
| continue | 继续 |
| default | 默认 ,在Switch case语句中会用 |
| do | do while循环会用 |
| while | while循环 |
| else | if else语句 |
| enum | 枚举 |
| extern | 声明外部符号的;比如在同一工程下另一个.c文件中声明全局变量或函数 |
| for | for循环 |
| goto | goto语句 |
| if | if语句 |
| register | 寄存器关键字,用register创建的变量建议放在寄存器中 |
| return | 返回 |
| sizeof | 求大小 |
| static | 静态的 |
| struct | 结构体 |
| typedef | 类型重定义别名,可以将类型重新定义成一个新的名字 |
| union | 联合体(共用体) |
| volatile | c语言中暂时不讲 |
三、关键字详讲
1. auto
int main()
{
{
int a=10;//是自动创建(进{}),自动销毁的(出{})——自动变量
auto int a=10;//两串代码相同(auto通常省略)
//auto在新的C语言语法中也有其他用法,暂时不考虑
}
return 0;
}
2. register
int main()
{
//大量频繁读取的数据,存放在寄存器中,提升效率
register int num=100;//建议num的值存放在寄存器中
return 0;
}
计算机中数据可以存放在哪里:
- 寄存器规模较小向上增长
- 高速缓存容量约几十MB | 费用更高
- 内存容量在8GB至16GB之间 | 运行速度更快
- 硬盘容量为500GB | 存储空间减少
- 网盘容量达到2TB |
注:define和include是预处理指令 , 不是关键字
3.typedef的讲解
typedef unsigned int u_int;//将unsigned int定义成u_int,然后这两个就等价了
int main()
{
unsigned int num=100;//相当于
u_int num=100;
return 0;
}
4.static的讲解
① .static修饰局部变量
static修饰局部变量时,在影响了其存在时间(实质上是转换为较为复杂的存储结构)。


②. static修饰全局变量
所有工程中的全局变量均可自由调用。
通过static修饰符限定,在当前包含它的.c文件内调用该静态变量。
由于被限定为静态后不再具备外部链接属性,“外部链接属性”的丧失使得其他.c文件无法引用该静态的全球性问题。


③. static修饰函数
通过关键字static对函数进行修饰,在编译器层面static关键字的作用是将函数标识符绑定到本地代码段内,并实现仅在当前源文件范围内作用域限制


第十一节 #define定义常量和宏
define是一个预处理指令
一、define定义标识符常量
#define MAX 1000
int main()
{
printf("%d\n",MAX);//输出1000
return 0;
}
二、 define定义宏
#define ADD(X,Y) X+Y //注意:宏定义没有分号
int main()
{
printf("%d\n",ADD(2,3));//输出5
return 0;
}
#define ADD(X,Y) X+Y //注意:宏定义没有分号
int main()
{
printf("%d\n",4*ADD(2,3));//输出11,而不是20
//4*2+3=11(因为宏是变量的替换)
return 0;
}
怎么让宏定义按要求输出结果?答:加括号
#define ADD(X,Y) ((X)+(Y)) //注意:宏定义没有分号
int main()
{
printf("%d\n",4*ADD(2,3));//输出20
return 0;
}
第十二节 指针
一、内存
对于现代计算机系统而言,在其核心组件中包含着一系列称为存储设备的部分。所有程序的操作均在其内部运行。
通过合理规划与优化配置的方法, 我们可以实现对这些存储区域的有效管理, 从而提高系统的整体性能。
为了让访问每个内存量更加便捷且高效可靠的方式是在其上赋予特定序列号或标识符作为其地址信息。
理解内存:在现实生活中对每个空间分配了一个唯一的标识符以便精准定位;同样地,在计算机系统中内存也遵循这一原则。
电脑有32位和64位的区别在于它们使用不同数量的地址线来标识存储位置:其中32位表示物理线上具备足以区分最多 3.× ¹⁰⁸ 个独特状态。
接着将电信号转化为数字信号的形式——通过二进制编码(由 1 和 0 组成)来表示数据。
因此能够支持多达 ²³² 个不同的存储位置。
那么一个 ³² 位的内存单元具体有多大呢:
计算下来相当于大约 ≈ ≈ ≈ ≈ ≈ ≈ 。
换算关系如下:每增加一个层级乘以相应的单位(如 × 8 = \text{byte} ×\, ×\, ×\, ×\, ×\, ×\, = \text{PB} )。
实际上每个比特位代表一个最小的存储单元。

int main()
{
int a = 10;//a在内存中要分配空间的--4个字节
printf("%p\n", &a);//%p专门用来打印地址的
//&a是a的地址,地址也是要存放在内存里的
int* pa = &a;//pa是用来存放地址的,在c语言中pa叫指针变量
// *说明pa是指针变量
// int说明pa执行的对象是int类型
char ch = 'w';
char* pc = &ch;
return 0;
}
我们用pa存放a的地址,是为了能找到a,使用a,所以需要解引用来找到a
int main()
{
int a =10;
int* pa=&a;//将a的地址存放在pa变量中
*pa=20;// * 是解引用操作 *pa就是通过pa里面的地址,找到a
printf("%d\n",a);//输出20(借助pa实现对a的操作)
return 0;
}
示意图:

指针 就是 地址
二、指针变量的大小



采用x86(32位平台),结果变为4 结论:指针长度在32位平台为4个字节,在64位平台为8个字节。
第十三节 结构体
1.结构体可以让C语言创建新的类型出来
例如描述学生
//结构体
//创建一个学生
struct Stu
{
char name[20];//人名
int age;//年龄
double score;//分数
};
//创建一个书
struct Book
{
char name[20];//书名
float price;//价格
char id[30];//编号
};
int main()
{
struct Stu s = { "张三",20,85.5 };//结构体的创建和初始化
printf("1:%s%d%lf\n", s.name, s.age, s.score);//结构体变量.成员变量
//%lf打印双精度浮点型
struct Stu* ps = &s;//结构体指针类型:*说明ps是指针变量,ps的类型是s的类型
printf("2:%s%d%lf\n", (*ps).name, (*ps).age,(*ps).score);
//ps是s的地址 *ps解引用后就是s 所以(*ps).name相当于s.name
printf("3:%s%d%lf\n", ps->name, ps->age, ps->score);//这三种表达方式等价
//结构体指针->成员变量
return 0;
}
