定义:程序运行过程中值不变的量。
存储位置:
常量的分类:
定义:在程序运行过程中值可变的量,称之为变量。
变量代表内存中(栈内存)具有特定属性的一个存储单元,它是用来存储数据的,也就是存放变量的值。
变量应该有一个名字,以便于通过名字访问变量。
数据类型 变量名列表;
#include
int main(int argc,char *argv[])
{
// 1. 声明变量并初始化
// 解释:向内存申请一块存储空间,并给这个空间添加一个初始数据
// 命名:变量名就是标识符的一种,命名规则:以字母或者下划线开头,只能包含数字、字母、下划线,举例:set_user
// 演示变量名的命名:_int,num2
int age = 21; // 注意:= 不是关系运算符等于,这里称作赋值运算符,意思是将右边的常量21赋值给左边的变量。顺序:由右到左
// 2. 先声明,再赋值
int count;
count = 10;
// 3. 初始化和赋值并行
int number = 12;
printf("%d\n", number); // 12 %d:以十进制int的方式格式化输出
number = 22; // 此时会覆盖掉number原本的值
printf("%d\n", number); // 22
// 4. 我们可以为多个变量定义同一类型,多个变量之间使用逗号分隔
int a, b = 10, c; // 此时我们定义了3个变量,只有b初始化了,其他两个变量未初始化
// 访问未初始化的变量?
// 访问一个未初始化的变量不会报错,但是不建议访问,因为此时变量空间中的数据是系统分配的随机值。
printf("%d,%d,%d\n",a,b,c);
return 0;
}
基本类型:(整型、浮点型、字符型)我们创建变量的同时,在内存中开辟对应的内存空间。
以字母或者下划线,后面跟字母、数字、下划线。举例:_int, __int, number, num1
不能与关键字(如:if…int…float等)或者预定义标识符(如:printf…main…)重名。
变量名、变量值、变量所占存储单元之间的关系:
**定义:**用来表示对象名字的有效字符。对象包括变量、常量、函数、数组、构造类型等。也就是标识符是这些对象的名字。
构成:
C语言规定的标识符只能由字母、数字、下划线三种字符构成。且只能以下划线或者字母开头。如:_int,num
编译系统对字母的大小写敏感(区分大小写)。变量名和函数名一般用小写字母+下划线,常量名和枚举名一般用大写字母+下划线,举例:
变量:set_count setCount(C语言也支持,但是不推荐)
函数:set_count() setCount()(C语言也支持)
常量、枚举:SET_COUNT
C语言中有3类标识符:
关键字:有32个,系统由特定的含义和用途,不能用作自定义标识符:
auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while
**系统预定义的标识符:**如系统提供的函数名sim,printf,main,scanf等以及预处理指令include,define等。
C语言中有12个预处理指令:
define endif elif error line include ifdef ifnodef pargma undef if else
注意:以上预处理指令使用的时候,在前面需添加#
,如#include
**用户标识符:**用户定义的标识符,用来给变量、自定义函数、符号常量、数组、构造类型、文件等命名。
小贴士:
在C语言中,要求对所用到的变量,在使用前必须强制定义。先定义,后使用
一个类型说明语句可以同时定义多个类型的变量,各个变量之间使用逗号分隔,多个同类型的变量也可以用多个类型说明语句定义:
// 写法1:一个类型说明语句可以同时定义多个类型的变量
// 优点:简洁
// 缺点:可读性较差
int a, b, c, d; // 定义多个同类型变量,这是一种简略写法
int a1 = 3, b1 = 4, c1 = 5, d1 = 6;
// 写法2:多个同类型的变量也可以用多个类型说明语句
// 优点:可读性强
// 缺点:繁琐
int aa;
int bb;
int cc;
int dd;
int aa1 = 3;
int bb1 = 4;
int cc1 = 5;
int dd1 = 6;
同类型说明语句定义的变量只是为这些变量分配了内存空间。以便以存放与之相同类型的数据。在未对这些变量赋值前,这些变量中的值是随机的(具体的随机由内存状态来决定),在C标准中访问未初始化的变量是“未定义行为(UB)”
#include
int main(int argc,char *argv[])
{
int a, b, c, d;
printf(“%d,%d,%d,%d\n”,a,b,c,d);// 此时的变量访问,属于 未定义行为(UB),不建议这样作
return 0;
}
// 第一次运行结果:-1543118880,32767,0,0
// 第二次运行结果:243305312,32766,0,0
C语言允许定义变量的同时为变量赋初始值。
在为长整型变量初始化或者赋值时,如果被复制的数据为基本整型常量,则C编译系统自动将被赋值数据转换为与之相依变量类型一致的数据。
long a = 12L; // 将一个long类型的常量数据12赋值给long类型的变量a,=左右两边类型一致
long b = 12; // 将一个int类型的常量数据12赋值给long类型的变量b,此时=左右两边类型不一致
由于各种整型变量所占的字节数有限,因此,它们存储的整数有一定的范围。
举例:unsigned char a
:1个字节,存储范围:0~255;char b
:1个字节,存储范围:-128~127
我们定义一个整型的变量,C编译系统实际是在内存中分配了能够存储一个整型数据的存储空间。并用变量名来标识这个空间。对该空间的使用也就是可以通过变量名来访问。(我们可以通过变量名访问变量空间)
如果不知道你所用的C编译系统对变量分配的存储空间的大小,可以通过sizeof运算符查询,如:
int a = 10;
printf("%d,%d\n",sizeof(int), sizeof(a)); // 4,4 sizeof返回的大小是以字节为单位,返回类型long unsigned int
数据在内存中是以二进制形式存放
如果有以下语句:
short i; // 定义i为一个存储整型的变量 2字节,默认是有符号数
i = 10;
printf("%d\n",i);
则十进制数10的二进制形式为1010(十进制转二进制,使用辗转相除法),则整数10在内存中的实际存储形式为:
实际上,整型数据在内存中是以补码的形式表示的,使用补码主要是为了==统一加减法运算==,使CPU可以使用同一套加法电路处理所有整数运算(包括正数和负数),同时消除了+0和-0的歧义,简化硬件设计。
我们程序中的数据直接转换出来的二进制是原码形式的。
正数的补码和和原码(该数的二进制形式)相同,(反码是原码和补码转换的过程产物)三码唯一。
负数的补码是将该数绝对值的二进制形式,针对数据位按位取反+1(反码 + 1 为补码)
内存数据的读写:
① 向内存存数据(二进制形式):程序原始数据 → 二进制原码 → 二进制反码 → 二进制补码
② 从内存取数据(二进制形式):二进制补码 → 二进制反码 → 二进制原码 → 程序原始数据
需求:整型变量的定义和使用
#include
int main(int argc,char *argv[])
{
// 定义有符号整型变量,可以存放正数、0、负数,默认就是有符号数
int a,b,c,d; // 等价于 signed int a,b,c,d;
// 定义无符号整型变量,可以存放正数、0,不能存放负数,必须添加关键 unsigned
unsigned int u;
// 赋值
a = 12;
b = -24;
u = 10;
c = a + u, d = b + u;
printf("a+u=%d,b+u=%d\n",c,d); // a+u=22,b+u=-14
// 注意:计算机硬件支持加法运算,不支持减法运算,我们程序中的减法运算,实际上是有负数参与的加法运算,比如5-2实际上5+(-2)
return 0;
}
有符号和无符号数在内存中的存储形式(以char类型举例):
有符号char的范围:`-128 ~ 127`
无符号char的范围:`0 ~ 255`
### 浮点型数据
**定义:**用来表达实数(小数)的数据类型。
#### 浮点型变量的分类
- 单精度浮点型(float):在64位系统中尺寸是4字节,精确到小数位6位,格式化符号 %f
- 双精度浮点型(double):在64位系统中尺寸是8字节,精确到小数位15~16位,格式化符号 %lf
- 长双精度浮点型(long double):在Linux下64位尺寸是16字节。
注意:占用内存越多,能表示的精确度越高。
```c
float f1 = 12.25f; // 单精度
double f2 = 12.5; // 双精度
long double f3; // 长双精度
浮点型数据在内存中按照指数形式(IEEE 754标准)存放。系统将一个浮点型数分成尾数部分和指数部分,分别存放。
举例:
1234.5→1.2345∗103→1.2345e31234.5 → 1.2345*10^3 → 1.2345e31234.5→1.2345∗103→1.2345e3
类别 | 字节数 | 符号位 | 指数位(指数部分) | 尾数位(小数部分) | 指数偏移量 |
---|---|---|---|---|---|
单精度浮点数 | 4 | 1位[31] | 8位[30~23] | 23位[22~00] | 127 |
双精度浮点数 | 8 | 1位[63] | 11位[62~52] | 52位[51~00] | 1023 |
float内存示意图:
需求:写出27.5f在内存中的存放形式
解析:
27.5f的二进制为11011.1
转换为指数形式:11011.1→1.10111∗2411011.1 → 1.10111*2^411011.1→1.10111∗24
指数:4 + 127 = 131,转换为二进制:1000 0011
尾数:10111,需要补够23位,右侧补0:1011 1000 0000 0000 0000 000
浮点型数据如何转二进制?
① 整数部分:采用辗转除2直到出现小数为止。
② 小数部分:采用辗转乘2直到整数位出现1,小数位出现0为止,但是有时候会出现乘不尽的情况,此时需根据内存大小来决定保留位数。
用二进制表示就是符号位(1位)、指数位(8位)、尾数位(23位),最终显示:
※ 科学计数法(指数法):
C语言中一般浮点数有两种表示形式:
十进制形式:指的是数字必须以整数形式+小数形式组成,比如 10.0 、3.14 …符合规则,以10为底数,并且科学计数法要在范围值之内。
指数形式(科学记数法):一般数学上对于小数可以以10为底进行表示 比如3.14 * 10²,但是在使用英文半角输入法的时候没办法输入上底或者下底,所以C语言规定以字母e或者E来表示指数,并且要求字母e或者E的前面必须有数字,字母e或者E的后面必须为整数。 -3.14E-2 2E-3 0.3E4
printf("%e", 10000000.00);
所以:E3 错误 因为E的前面有数据 比如说 3.14E3
3.1e5.6 错误 因为 E的后面必须是整数
3.1e-2 对的 相当于 $3.1 * 10^{-2} $
1.01e-5 等价于 1.01∗10−51.01*10^{-5}1.01∗10−5
**概念:**字符型变量用来存放字符常量。
定义:
char 变量列表;
举例:
char a = 'A';
char b = a;
举例:
#include
int main(int argc,char *argv[])
{
char c1,c2;
c1 = 'A';
c2 = 'B';
printf("c1=%c\n",c1);// A %c是字符的格式化符号
printf("c1=%c\n",c2);
printf("c1=%d\n",c1);
printf("c2=%d\n",c2);
return 0;
}
运行结果:
**说明:**
一个字符变量只能存放一个字符。C编译系统规定以一个字节的存储空间来存放一个字符,因此,一个字符变量在内存中的大小是1个字节(==1字节 = 1字符==)
#### 字符数据在内存中的存储形式
将一个字符常量存入一个字符变量,实际上并不是把该字符本身存入内存中,而是将该字符对应的ASCII(系统内置)码存入内存单元。
范例:
- 需求:`char c1 = 'a', c2 = 'b';`
- 分析:
- `'a'`的ASCII为`97`,`'b'`的ASCII码为`98`,它们在内存中的存储形式:
进一步理解,实际上是按照ASCII码的二进制形式存放,形式为:
由此可见,在内存中字符数据的存储形式与整型数据类似,这就使得字符型数据和整型数据之间可以通用,也就是1个字符数据既可以以字符形式输出,也可以以整数形式输出。
- 以字符形式输出时,编译系统将内存单元中的ASCII码转换为相应的字符,然后输出。
- 以整型输出时,直接将ASCII码作为整数输出。
- 因此,可以对字符数据进行算数运算,这时相当于对其ASCII码进行算数运算。
##### 案例
- 要求:给字符变量赋整数值
```c
/*************************************************************************
> File Name: demo03.c
> Author: FPF
> Description: 字符型数据
> Created Time: 2025年01月21日 星期二 16时05分09秒
************************************************************************/
#include
int main(int argc,char *argv[])
{
char c1,c2;
c1 = 97;// char既能赋值字符,也能赋值整数(ASCII码)
c2 = 98;
printf("%c %c \n",c1,c2);// a b, 以字符形式输出
printf("%d %d \n",c1,c2);// 97 98, 以整数形式输出
return 0;
}
说明:char类型可以赋值为字符,也可以赋值为整数(char a = 128,显示的结果范围:-128~127),演示案例:
c1在内存的存储形式以及不同形式的输出,如下图所示:
需求:大小写字母转换
代码:
#include
int main(int argc,char *argv[])
{
char c1,c2;
c1 = 'm';
c2 = 'Y';
printf("转换前:%c,%c\n",c1,c2);
// 转换
c1 = c1 - 32;
c2 = c2 + 32;
printf("转换后:%c,%c\n",c1,c2);
return 0;
}
*/
#include
int main(int argc,char *argv[])
{
char c1,c2;
c1 = 'm';
c2 = 'Y';
printf("转换前:%c,%c\n",c1,c2);
// 转换
c1 = c1 - 32;
c2 = c2 + 32;
printf("转换后:%c,%c\n",c1,c2);
return 0;
}