C语言第02天学习笔记
内容提要
- 变量
- 基本数据类型
- 常量
- 标识符
- 各类数据在内存中的存储形式
数据类型
C语言的数据类型可分为基本类型、构造类型、指针类型和空类型。
基本类型(C语言内置)
整型
- 短整型:short(2字节)
- 基本整型:int(4字节)
- 长整型:long(32位系统4字节/64位系统8字节)
- 长长整型:long long(8字节,C99新增)
浮点型
- 单精度型:float(4字节)
- 双精度型:double(8字节)
- 长双精度型:long double(如GCC通常是16字节,MSVC可能是8字节),取决于编译器。
字符型
char(1字节),C语言中1字节 = 1字符,因为C语言没有字节对应的类型,所以一般用char表示字节。
构造类型(C语言提供了构建语法,由程序员自己创建类型)
- 结构体:struct
- 共用体/联合体:union
- 枚举:enum
指针类型
空类型
void
常量
定义
程序运行过程中值不变的量。
存储位置
常量存储在数据段(字符串常量)或代码段,具体由编译器来决定。变量默认存储在栈内存,但静态变量和全局变量存储在数据段。(后续深入)
常量的分类
- 整型常量:十进制整型常量、八进制整型常量、十六进制整型常量
- 浮点型常量:十进制表示法、指数表示法
- 字符型常量
- 字符串常量
- 符号常量:宏定义
变量
概念
- 定义:在程序运行过程中值可变的量,称之为变量。
- 变量代表内存中(栈内存)具有特定属性的一个存储单元,它是用来存储数据的,也就是存放变量的值。
- 变量应该有一个名字,以便于通过名字访问变量。
格式
数据类型 变量名列表;
举例(demo01.c)
#include <stdio.h>
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 - 系统预定义的标识符:如系统提供的函数名(如printf、scanf等)以及预处理指令(如include、define等)。C语言中有12个预处理指令:define、endif、elif、error、line、include、ifdef、ifnodef、pargma、undef、if、else。注意:以上预处理指令使用的时候,在前面需添加#,如#include。
- 用户标识符:用户定义的标识符,用来给变量、自定义函数、符号常量、数组、构造类型、文件等命名。
小贴士
在C语言中,要求对所用到的变量,在使用前必须强制定义,先定义,后使用。
基本类型变量的定义与使用
基本类型(整型、浮点型、字符型)我们创建变量的同时,在内存中开辟对应的内存空间。
同类型变量的定义方式
- 一个类型说明语句定义多个变量
- 优点:简洁
- 缺点:可读性较差
int a, b, c, d; // 定义多个同类型变量 int a1 = 3, b1 = 4, c1 = 5, d1 = 6;
- 多个类型说明语句定义多个同类型变量
- 优点:可读性强
- 缺点:繁琐
int aa; int bb; int cc; int dd; int aa1 = 3; int bb1 = 4; int cc1 = 5; int dd1 = 6;
未初始化变量的使用
同类型说明语句定义的变量只是为这些变量分配了内存空间,在未对这些变量赋值前,这些变量中的值是随机的(具体的随机由内存状态来决定)。在C标准中访问未初始化的变量是“未定义行为(UB)”。
案例
#include <stdio.h>
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,此时=左右两边类型不一致
- 由于各种整型变量所占的字节数有限,因此,它们存储的整数有一定的范围。
整型数据
定义
用来存放整型数据的变量,定义一个整型的变量,C编译系统实际是在内存中分配了能够存储一个整型数据的存储空间,并用变量名来标识这个空间,对该空间的使用可以通过变量名来访问。
查看变量存储空间大小
如果不知道所用的C编译系统对变量分配的存储空间的大小,可以通过sizeof运算符查询,如:
int a = 10;
printf("%d,%d\n",sizeof(int), sizeof(a)); // 4,4 sizeof返回的大小是以字节为单位,返回类型long unsigned int
整型数据在内存中的存储形式
数据在内存中是以二进制形式存放,整型数据在内存中是以补码的形式表示的。使用补码主要是为了统一加减法运算,使CPU可以使用同一套加法电路处理所有整数运算(包括正数和负数),同时消除了+0和-0的歧义,简化硬件设计。
- 正数的补码和原码(该数的二进制形式)相同,三码(原码、反码、补码)唯一。
- 负数的补码是将该数绝对值的二进制形式,针对数据位按位取反+1(反码+1为补码)。
示例
内存数据的读写
① 向内存存数据(二进制形式):程序原始数据 → 二进制原码 → 二进制反码 → 二进制补码
② 从内存取数据(二进制形式):二进制补码 → 二进制反码 → 二进制原码 → 程序原始数据
案例
整型变量的分类使用
#include <stdio.h>
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类型举例)
类型 | 分类 | 二进制范围 | 十进制范围 | 备注 |
---|---|---|---|---|
有符号 | 正数(原码) | 00000000-01111111 | 0至127 | 最高位0表示正 |
正数(补码) | 00000000-01111111 | 0至127 | 实际计算机使用方式 | |
负数(原码) | 10000001-11111111 | -1至-127 | 最高位1表示负 | |
负数(补码) | 10000000-11111111 | -128至-1 | -128是特殊表示 | |
无符号 | 正数 | 00000000-11111111 | 0至255 | 所有位表示数值 |
- 有符号char的范围:-128 ~ 127
- 无符号char的范围:0 ~ 255
浮点型数据
定义
用来表达实数(小数)的数据类型。
浮点型变量的分类
- 单精度浮点型(float):在64位系统中尺寸是4字节,精确到小数位6位,格式化符号%f
- 双精度浮点型(double):在64位系统中尺寸是8字节,精确到小数位15~16位,格式化符号%lf
- 长双精度浮点型(long double):在Linux下64位尺寸是16字节。
示例
float f1 = 12.25f; // 单精度
double f2 = 12.5; // 双精度
long double f3; // 长双精度
浮点型数据在内存中的存储形式
浮点型数据在内存中按照指数形式(IEEE 754标准)存放。系统将一个浮点型数分成尾数部分和指数部分,分别存放。
类别 | 字节数 | 符号位 | 指数位(指数部分) | 尾数位(小数部分) | 指数偏移量 |
---|---|---|---|---|---|
单精度浮点数 | 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*2⁴
- 指数:4 + 127 = 131,转换为二进制:10000011
- 尾数:10111,需要补够23位,右侧补0:10111000000000000000000
- 最终表示:符号位(1位)、指数位(8位)、尾数位(23位)组成的二进制数。
浮点型数据转二进制方法
- 整数部分:采用辗转除2直到商为0为止。
- 小数部分:采用辗转乘2直到整数位出现1,小数位出现0为止,但是有时候会出现乘不尽的情况,此时需根据内存大小来决定保留位数。
科学计数法(指数法)
C语言中一般浮点数有两种表示形式:
- 十进制形式:指的是数字必须以整数形式+小数形式组成,比如10.0、3.14等。
- 指数形式(科学记数法):以字母e或者E来表示指数,并且要求字母e或者E的前面必须有数字,字母e或者E的后面必须为整数。如-3.14E-2、2E-3、0.3E4。
错误示例
- E3:错误,因为E的前面没有数据
- 3.1e5.6:错误,因为E的后面必须是整数
字符型数据
概念
字符型变量用来存放字符常量。
定义
char 变量列表;
示例(demo04.c)
#include <stdio.h>
int main(int argc,char *argv[])
{
char c1,c2;
c1 = 'A';
c2 = 'B';
printf("c1=%c\n",c1);// %c是字符的格式化符号
printf("c1=%c\n",c2);
printf("c1=%d\n",c1);
printf("c2=%d\n",c2);
return 0;
}
运行结果:
c1=A
c1=B
c1=65
c2=66
存储特点
一个字符变量只能存放一个字符。C编译系统规定以一个字节的存储空间来存放一个字符,因此,一个字符变量在内存中的大小是1个字节(1字节=1字符)。
字符数据在内存中的存储形式
将一个字符常量存入一个字符变量,实际上是将该字符对应的ASCII码存入内存单元。
范例
char c1 = ‘a’, c2 = ‘b’;'a’的ASCII为97,'b’的ASCII码为98,它们在内存中的存储形式为97和98对应的二进制形式:01100001和01100010。
字符型数据和整型数据的通用性
在内存中字符数据的存储形式与整型数据类似,这就使得字符型数据和整型数据之间可以通用,也就是1个字符数据既可以以字符形式输出,也可以以整数形式输出。
- 以字符形式输出时,编译系统将内存单元中的ASCII码转换为相应的字符,然后输出。
- 以整型输出时,直接将ASCII码作为整数输出。
因此,可以对字符数据进行算数运算,这时相当于对其ASCII码进行算数运算。
案例:给字符变量赋整数值(demo03.c)
#include <stdio.h>
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在内存的存储形式以及不同形式的输出,如下图所示:
案例:大小写字母转换(demo06.c)
#include <stdio.h>
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;
}
总结
变量、数据类型、常量、标识符以及各类数据在内存中的存储形式等内容的学习,我们对 C 语言的基础要素有了更清晰的认识。这些知识是后续深入学习 C 语言编程的重要基石,希望大家能扎实掌握,为应对更复杂的编程问题和完成章节作业打下坚实基础。
注:后续笔记将围绕C语言知识展开,建议每日实操时长不少于3小时