文章目录
数据类型
①整型
char //字符数据类型 (1字节)
short //短整型 (2字节)
int //整形 (4字节)
long //长整型 (4字节)
long long //更长的整形 (8字节)
char:
char c1='T';//字符单引号
printf("c1=%c,c1对应的ASCII码=%d \n",c1,c1);//%c 解析为字符本质上就是整数
char c2=99;//0~127(一个字节的正数部分就是0-127)
printf("c2=%c,c2对应的ASCII码=%d \n",c2,c2);
可以看出char的本质就是ASCII表中的数字,所以既可以用字母也可以用数字(但注意数字要是0-127范围)
short:
短整型字节数:2
格式符号:%hd
数据范围-2^15 ~ 2^15-1
宏:最小值SHRT_MIN 最大值宏SHRT_MAX
short s1=1000;
printf("short s1=%hd\n",s1);
int:
整型字节数:4
格式符号:%d
数据范围-2^31 ~ 2^31-1
宏:最小值INT_MIN 最大值宏INT_MAX
int i=9999999;
printf("i1=%d\n",i);
long
长整型windows字节数:4
格式符号:%ld
数据范围-2^31 ~ 2^31-1
宏:最小值 LONG_MIN 最大值宏 LONG_MAX
long l=1;
printf("l=%ld\n",l);
long long
长长整型字节数:8
格式符号:%lld
数据范围-2^63 ~ 2^63-1
宏:最小值 LLONG_MIN 最大值宏 LLONG_MAX
long long ll1=99999999999L;//注意此处是加了L,因为默认不加L整数为int类型
printf("ll1=%lld\n",ll1);
②bool型
在C语言标准(C89)没有定义布尔类型,所以C语言判断真假时以0为假,非0为真
//定义一个int类型变量,当变量值为0时表示false,值为1时表示true
int flag;
flag = 0;
flag = 1;
C语言标准(C99)解决了布尔类型的问题。C99提供了Bool 型,true代表1为真,false代表0为假,
bool flag = false;
flag = true; //本质也是1,0
printf("%d, %d\n", true, false); // 1, 0
③浮点型
float //单精度浮点数(4字节)
double //双精度浮点数 (8字节)
变量(全局变量与局部变量)
变量:有些值是可变的(比如:年龄,体重,薪资)。
变量的定义:
int age = 150;
float weight = 45.5f;
char ch = 'w'
变量的分类
①局部变量②全局变量
#include <stdio.h>
int global = 2019;//全局变量
int main()
{
int local = 2018;//局部变量
//下面定义的global会不会有问题?
int global = 2020;//局部变量
printf("global = %d\n", global);
return 0;
}
void test()
{
printf("global = %d\n", global);
}
上述代码最终结果会输出2020
这是因为当局部变量和全局变量同名时,出现了就近原则,在main中,局部变量会遮蔽全局变量:
在 main 函数内部,名字 global 指的是局部变量
全局变量 global 在函数内部变得不可见(但依然存在)
但是对于test()函数,此时的global会打印出2019,因为就近原则,只看得到全局变量的global
此时内存分布如图

注意
局部变量,在{}里面,没有默认值,使用的时候必须要初始化,否则可能会出现脏数据
全局变量,在函数{}外面,所有函数都可以使用,有默认值
常量
常量:生活中的有些值是不变的(比如:圆周率,性别,身份证号码,血型等等)
C语言中的常量分为以下以下几种:
字面常量
const 修饰的常变量
#define 定义的标识符常量
枚举常量
3.14;//字面常量
1000;//字面常量
const int i = 3; //这里的i是const修饰的常变量
i = 5;//❌是不能直接修改的!!!!!
#define MAX 100
printf("max = %d\n", MAX);//此时其实本质就是写的100,因为会在预处理时替换掉
转义字符
由 \ 和特定字符组合而成,称之为 转义字符
比如入我们要在屏幕上打印一个目录: D:\code\test.c
此时在C语言中应该用转义字符
如果你直接使用
printf("D:\code\test.c");//将不能正确打印出路径
涉及到转义字符,需要在 / 前面再加一个 /
printf("D:\\code\\test.c");这样反斜杠\才能正确表达出来
常见的有
‘\n’ 这是 代表 回车换行(回车键)。
‘\’ 用于表示一个反斜杠,防止它被解释为一个转义序列符。
‘\t’ 也是 1 个字符。代表 制表符 (Tab键)
基础库函数
①putchar()
C 库函数putchar把参数 char 指定的字符(一个无符号字符)写入到标准输出 stdout 中。
int main ()
{
char ch;
for(ch = 'A' ; ch <= 'Z' ; ch++)
{
putchar(ch);
}
return(0);
} //最终答应得到的结果是26个字母ABC...XYZ
②getchar()
C 库函数getchar从标准输入 stdin 获取一个字符(一个无符号字符)
int main ()
{
char c;
printf("请输入一个字符:");
c=getchar();
getchar(); //缓冲区还有个\n
printf("打印该字符:");
putchar(c);
return 0;
}
注意:上述代码的中间getchar()函数如果不加,运行出来的效果是一样的,因为我们输入在控制台输入一个字符后,按下回车键,其实就是一个换行符\n,但是这里的c只接受一个字符,所以只会接收我们输入的字符,但是为了更加规范,所以我们通过一个getchar()拿到缓冲区的\n,然后再输入c
③scanf()
C 库函数 scanf(const char *format, …) 从标准输入 stdin 读取格式化输入。
说白了就是通过键盘给程序中的变量赋值
举一个例子清楚了解scanf(),比如接收字符串:
#include <stdio.h>
int main()
{
char str1[20], str2[30];
printf("请输入用户名:");
scanf("%s", str1);
printf("请输入您的网站:");
scanf("%s", str2);
return(0);
}
结果为:
请输入用户名:KKloveCode
请输入您的网站:www.sino.com
如果同时接收多个输入,那要复杂一些,如:
#include<stdio.h>
int main(void)
{
int a,b,c;
printf("请输入三个数字:");
scanf("%d%d%d",&a,&b,&c);
printf("%d,%d,%d\n",a,b,c);
return 0;
}
注意:scanf中,双引号的内容与控制台输入的内容要保持格式一致,比如
scanf("%c,%c,%c",&a,&b,&c),你的控制台输出时,也要写a,b,c;
而不能输入abc或者 a b c,必须要保持一一对应,因为如果不保持对应,scanf()函数很有可能会出现其他不可预见的问题,比如值不一样或者根本打印不出值
并且&是一个取地址运算符,&后面加变量名表示“该变量的地址”,所以&就表示变量 i 的地址。&又称为“取地址i”
类型转换
自动类型转换(隐式)
自动类型转换是由编译器自动完成的,这种转换通常发生在不同类型的数据进行混合运算时。
1.当一个表达式中包含不同的数据类型时,较小的类型会自动转换为较大的类型。
2.所有的浮点运算都是以双精度进行的,即使运算中只有 float 类型。
3.在赋值操作中,右边表达式的类型将自动转换为左边变量的类型。
在赋值运算中,赋值号两边的数据类型不同时,需要把右边表达式的类型转换为左边变量的类型,这可能会导致数据失真,或者精度降低;**所以说,自动类型转换并不一定是安全的。**对于不安全的类型转换,编译器一般会给出警告。
以下给出一些例子:
//类型转化:
//自动隐式转换
int a=3;
double b=3.141592;
//不同类型数据参与运算,将小的类型转换大的类型
printf("%lf\n",a*b);
//赋值的时候也会自动转换!!!!!!!!
int c=b;
printf("c=%d\n",c);//c来接收自动转换后的结果
总结:可以看出隐式类型转换既可以小类型转大类型(运算时),也可以大类型转小类型(赋值时,与此同时会损失精度)这也是上面的第3点,赋值时右值类型自动转换为左值类型,无论大小(我之前忽略的)。
强制类型转换(显式)
强制类型转换是程序员明确指定的类型转换,它可以用来覆盖默认的类型转换规则,或者在自动转换不可行的情况下进行转换。强制类型转换的语法是在要转换的表达式前面加上用括号括起来的目标类型。
三个例子:

#include <stdio.h>
int main()
{
int a = 30, b = 7;
float result;
result = a / b; // 整数除法
printf("没有转换结果: %f\n", result);
//这一部分其实还是隐式类型转换的知识:
//两个 int 相除,结果还是 int,小数部分被截断,发生在除法阶段,赋值 result=3,
//根据赋值规则:右值(int)自动转为左值类型(float),这是赋值时的隐式转换
result = (float)a / b; // 此处就是强制类型转换
printf("有了转换的结果: %f\n", result);
return 0;
}
对比两者得到的结果:

自动转换是编译器给你的"便利",只在不危险时才提供;强制转换是你的"权力",但使用时要小心。
运算符
算术运算符

规则:
- 先 乘除,后 加减。
- 除法
- 两整数相除,结果为整数
- 两浮点数相除,结果 double 浮点数,默认保留6位小数
- 整数和实数相除,自动类型转换为实型
- 不允许 除 0(与数学一样)
- 取模(取余)
- 运算数必须是整型
- 对负数取余,结果为余数的绝对值
- 不允许 除 0(与数学一样)
赋值运算符
包含基本赋值运算符 和 复合赋值运算符( = 与算数运算符 组合而成),给变量赋值。
对于复合运算符:

比较运算符

优先级:算数>比较>赋值
#include <stdio.h>
int main(void)
{
int a = 10
// 成立 1, 不成立 0
printf("10 > 10: %d\n", 10 > 10); //0
printf("10 >= 10: %d\n", 10 >= 10);//1
printf("a == 10: %d\n", a == 10); //1 (不要将 == 错写成 =)
// 整体,低于 算术运算符
printf("a >= 3 + 7: %d\n", a >= 3 + 7); //1 // 先算 +, 再判断 >=
// 整体, 高于 赋值运算符
int b;
b = 4 > a; // 先算 4 > a, 再赋值
printf("b = %d\n", b);//结果为0
// > >= < <= 高于 == !=
printf("%d\n", 7 > 5 != 3 <= 4); //结果为0
return 0;
}
自增自减
- 自增 ++, 放在变量前面或后面,对变量自身值+1。
- 自减 --, 放在变量前面或后面,对变量自身值-1。
- 方便实现变量的 +1 或 -1 运算。
详细阐述:关于自增自减运算的详细教程
逻辑运算符

int main(int argc, char const *argv[])
{
// !取反 0->1,1->0
printf("%d\n",!0);//1
printf("%d\n",!9527);//0
//C语言不支持数学逻辑上的连续判断
printf("%d\n",30>20>10);//0
printf("%d\n",30>20 && 20>10);//1
//优先级
// !取反 优先级高于 && ||
printf("%d\n",1 && 2 && !0);//1
//比较运算符 优先级高于 逻辑运算符
printf("%d\n", 3>2 && 4>3); //1
//比较运算符的 优先级高于 赋值运算符
//逻辑运算符 优先级高于 赋值运算符
int res=4 && 3;
printf("res=%d\n",res);//1
return 0;
}
还包括短路运算:
逻辑表达式 运算过程中,只运算左边表达式,就能直接得出整个逻辑表达式的结果。
int main()
{
//短路运算
int a=0;
int b=0;
//&&运算符 见false则false,发生短路,后续不执行
printf("%d\n",a++ && b++);//0
printf("a=%d,b=%d\n",a,b);//a=1,b=0
// ||运算符 见true则true,发生短路,后续不执行
printf("%d\n",a++ || b++);//1
printf("a=%d,b=%d\n",a,b);//a=2,b=0
}
逗号运算符
C语言中,逗号也是一种运算符。 用逗号将多个表达式连接起来,组成逗号表达式。一般形式为:表达式 1,表达式 2,表达式 3,…,表达式 n
求解过程是 自左向右 依次求解。 整个逗号表达式的值,为依次运算到最后一个表达式后, n 的值。
int main()
{
//逗号表达式
int a=1,b=2,c=3;
printf("a=%d,b=%d,c=%d\n",a,b,c);
//逗号运算符的优先级比赋值运算符还低
// () 提升优先级
int res=(a++,++a,a+5);//结果为逗号表表达式 最后一个表达式的值
printf("res=%d\n",res); //结果为8
}
逗号运算符的优先级,是所有运算符中最低的。
三目运算
语法:表达式1 ?表达式 2:表达式 3
-
表达式 1 一定起 判别 作用。表达式 2 和 表达式 3 只能有一个执行。
-
判断 表达式 1 的结果:
- 为真,整个三目运算的结果为 表达式 2
- 为假,整个三目运算的结果为 表达式 3
位运算
位运算是C语言中一种直接对二进制位进行操作的运算方式。
位运算符号包括: &(与)、|(或)、^(异或)、取反(~)、左移(<<)、右移(>>)
#include <stdio.h>
int main(int argc, char const *argv[])
{
//位运算
// &(与):两个位都为1时,结果才为1,否则为0
int a=5; //二进制 00000000 00000000 00000000 00000101
int b=3; //二进制 00000000 00000000 00000000 00000011
int res=a&b;//二进制 00000000 00000000 00000000 00000001
printf("res=%d\n",res);//1
// |(或):两个位只要有一个1,结果就为1,否则为0
res=a | b;//二进制 00000000 00000000 00000000 00000111
printf("res=%d\n",res);//7
// ^(异或):两个位相同为0,不同为1
res=a ^ b;//二进制 00000000 00000000 00000000 00000110
printf("res=%d\n",res);//6
// 取反(~):将每一位二进制取反,0变1,1变0
res=~a;//二进制 11111111 11111111 11111111 11111010
printf("res=%d\n",res);//-6
// 左移(<<):将二进制向左移动指定的位数,右边补0
res=a<<4;//二进制 0000 00000000 00000000 000001010000
printf("res=%d\n",res);//80
// 右移(>>):将二进制向右移动指定的位数,左边正数补0,负数补1
res=res>>3;//二进制 0000000 00000000 00000000 000001010
printf("res=%d\n",res);//10
res=~a;//二进制 1111111111 11111111 11111111 111110
res=res>>2;
printf("res=%d\n",res);//-2
return 0;
}
位运算对于嵌入式来说非常重要,对其他语言方向不怎么常用,因为嵌入式会更多接触底层,使用二进制.
内存分布模型
如下

栈(Stack)
栈是用来存储局部变量和函数调用信息的区域。栈的内存分配由编译器自动完成,并在函数返回时自动释放。
特点
①自动分配和释放: 局部变量和函数调用信息由编译器自动管理。
②后进先出: 栈是一种后进先出的数据结构。
#include <stdio.h>
void function() {
int local = 10; // 局部变量,存储在栈中
printf("Local = %d\n", local);
}
int main() {
function();
return 0;
}
- local 是在函数 function 中定义的局部变量,它存储在栈中。
- 当 function 被调用时,local 被分配内存并初始化为 10。
- 程序通过 printf 函数输出 local 的值。
- function 执行完毕后,栈帧被释放,local 的内存也被回收。
- local存储在栈中,当function函数返回时,这块内存被自动释放。
堆(Heap)
堆是用来动态分配内存的区域。程序在运行时可以使用malloc、calloc、realloc等函数在堆上分配内存,并在不需要时使用free函数释放内存。
特点
①动态分配: 使用malloc、calloc等函数在运行时分配内存。
②手动管理: 需要程序员手动管理内存的分配和释放。
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int) * 10); // 在堆上分配内存
if (ptr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
for (int i = 0; i < 10; i++) {
ptr[i] = i;
}
free(ptr); // 释放内存
return 0;
}
未初始化的数据段(Uninitialized data segment)
未初始化的数据段也称为 .bss 段(Block Started by symbol),用于存储所有未初始化的全局变量、局部变量和外部变量。
特点
①未初始化: 包含未初始化的全局变量和静态变量。
②自动初始化为零: 程序开始时自动将这些变量初始化为零。
#include <stdio.h>
int uninit_global_var; // 未初始化的全局变量
int main() {
static int uninit_static_var; // 未初始化的静态变量
printf("Uninit Global: %d, Uninit Static: %d\n",uninit_global_var, uninit_static_var);
return 0;
}
//输出:Uninit Global: 0, Uninit Static: 0
已初始化的数据段(Initialized data segment)
数据段用来存储初始化的全局变量和静态变量。这些变量在程序开始时就已经分配了内存,并且在整个程序运行期间保持其值。
特点
①初始化: 包含初始化的全局变量和静态变量。
②读写: 允许读写操作。
#include <stdio.h>
int global_var = 42; // 初始化的全局变量
int main()
{
static int static_var = 99; // 初始化的静态变量
printf("Global: %d, Static: %d\n", global_var, static_var);
return 0;
}
代码段(Text/Code Segment)
代码段,也称为文本段,是用来存储程序的机器指令的。这个区域是只读的,防止程序意外地修改其指令。
特点
①只读: 防止程序修改指令。
②共享: 在多进程环境中,多个进程可以共享同一个代码段,节省内存。
.exe的"代码部分"被加载到这里执行时从这里读取指令
7748



被折叠的 条评论
为什么被折叠?



