C语言学习|最最最详细的C语言教程笔记零起步(3)小白必备 同笔者一起学习
C语言教程笔记
- 五.整型数据类型
-
- 1. 整型数据类型
- 2. 用sizeof关键词来测量大小
- 3. 三位二进制表示的数值范围
- 4. 数值补码表示法
- 5. 各种整型类型的数值范围是多少(包括无符号整型)
- 六.浮点数据类型
-
- 1. 处理带小数的数值(float)
- 2. 浮点类型:double
- 3. 浮点类型所占字节大小
- 七.C语言的变量与常量
-
- 1. 变量与常量
- 2. 声明变量
- 3. 变量名命名规则
- 4. 变量初始化和赋值
-
- 4.1 初始化和赋值
- 4.2 初始化和赋值的区别
- 5. 常量
-
- 5.1 字面常量
- 5.2 字面常量类型
- 5.3 符号常量
- 八.字符常量及字符变量
-
- 1. 字符常量
- 2. 探究字符类型占用空间
-
- 2.1 字符常量的大小
- 2.2 字符变量的大小
- 3. 错误导致的意外收获
- 4. 整数与字符的对应关系——ASCII
- 5. 字符类型与字符变量
- 6. 探究字符串常量占用空间
- 7. 字符零与数值零
- 8. 转义字符
五.整型数据类型 1. 整型数据类型 在上一节当中,我们遇到了用int关键词(整数integer的缩写)来表示一个整数的数据类型。 这一节中,我们来详细讨论一下C语言里面的整数数据类型。
类型名称 | C语言中的关键词 |
---|---|
字符型 | char |
短整型 | short |
整型 | int |
长整型 | long |
长长整型 | long long |
要表示更大的数据范围就需要更多晶体管。要知道在发明C语言的年代,计算机存储资源是非常珍贵而 稀缺的。对存储资源,程序员可能恨不得把一块掰成两块来用。如果只想表达0到100以内的数值,那么 一个字节就足够了,何必用两个字节来存储呢?
而如今,即使存储资源已经较为丰富了,但是大部分的强类型语言,都延续了这个传统。它们均提供了 丰富的类型以供选用。而程序员在编写代码时,通常能预想到需要使用到的数据范围的大小。这样在处 理一个数据时,可以从语言所提供的类型中选用最合适的类型来承载数据。
那么接下来,你可能想知道这些类型中,它们分别占用几个字节,具体的数据范围是多少?从而合适地 选择数据类型。很遗憾,我不能准确地告诉大家。因为C语言标准并未规定这些数据类型的大小范围, 具体的实现交由了编译器和平台决定。那我们怎样知道在visual studio 2019中,各种整型数据类型能够 表示的数据范围呢?
我们需要sizeof关键词来帮我们测量。
2. 用sizeof关键词来测量大小 和int一样,sizeof 是C语言中的一个关键词。它是英文 size of 连起来的合成词。翻译成中文就是什么东西的大小的意思。它能够测量C语言各种实体所占用的字节大小。
如果我们想看int所占用的字节大小,可以这样写sizeof(int)。执行后这段代码后,它的测量结果是一个 整型。我们可以借助printf函数将测量结果显示在控制台上。我们现在可以假设sizeof返回的结果是int类型的,在printf函数中使用占位符**%d**。而更准确地用法,应该用**%zu**。
测量int类型所占用的字节大小,并将结果打印在控制台上的代码如下:
printf("%d\n", sizeof(int));
sizeof 后面既可以跟类型,也可以跟变量、常量。
- 跟类型,测类型所占用字节的大小。
- 跟变量,测变量的类型所占用字节大小。
- 跟常量,测常量的类型所占用字节大小。 下面是以上三种情况的示例代码。
int a;
printf("sizeof int = %d\n", sizeof(int));
//1.测类型所占用字节的大小
printf("sizeof a = %d\n", sizeof(a));
//1.测变量的类型所占用字节大小
printf("sizeof 123 = %d\n", sizeof(123));
//1.测常量的类型所占用字节大小
文章图片
现在可以在自己的电脑运行一下,看看C语言中提供的各种整型类型的大小。
printf("sizeof char=%d\n", sizeof(char));
printf("sizeof short=%d\n", sizeof(short));
printf("sizeof int=%d\n", sizeof(int));
printf("sizeof long=%d\n", sizeof(long));
printf("sizeof long long=%d\n", sizeof(long long));
文章图片
运行一下,看到了结果
char,short,int,long,long long 分别占用了1,2,4,4,8个字节。至此,我们已经得知了它们所占 字节大小,并且验证了可以表示越大范围的数据类型所占用的字节越多。
值得注意的是在Visual Studio 2019中,int和long均占用4个字节。这并未违反C语言标准,C语言标准规定高级别的类型取值范围不得小于低级别的类型,但是它们可以是一致的。
3. 三位二进制表示的数值范围 TIPS:
如果暂时无法理解关于整型表达范围的原理分析,请先记住sizeof的使用和各种整型变量的表达范围的 结论即可。不理解整型表达范围的原理不影响对C语言的使用。
char,short,int,long,long long分别占用了1,2,4,4,8个字节。而每个字节由8个晶体管组成, 每个晶体管状态我们称之为位。那么char,short,int,long,long long分别占用了8,16,32,32,64 位。
太多的位不利于我们理解原理,我们暂时把问题简化一下,试试看位数减少到3。然后,分析3位的组 合,它能表示多大范围的数值呢?
文章图片
三位二进制组成的数据类型,可以表达2的3次方也就是8个数值。如果从0开始,那么可以表达从0到7的 数据范围。那么可以得出以下结论:
如果不考虑负数,那么整型数据类型可以表达的数据范围是:
假设,位数为n,则数据范围从【0】开始,到【2的n次方-1】的数值范围。
负数怎么办?那就需要拿出一个位来作为符号位。用来表示这个数据是 正数 还是 负数 。在IEEE标准中,这个符号位存在于二进制的最高位。让我们用三位二进制来示范这种情况。
文章图片
加上符号之后,现在取值范围变为负4到3了。红框中的为最高位,最高位为1的表示负数。你可能会觉 得有点奇怪,为什么3的二进制是011,而负3却是101呢?如果简单的加一个符号位,为什么不用111 呢?那我们看看如图中所示的3与负3相加的运算结果。
文章图片
4. 数值补码表示法
文章图片
补码表示法既通过最高位,区别了正数和负数。
并且,巧妙地应用了溢出,所得到的计算结果结果也是正确的。计算机的电路设计中,也只需要设计加法电路。极大地简化了计算机内部电路的复杂程度。
求一个正数对应的负数的补码的第二种办法:
- 先写出这个正数的二进制。
- 从二进制的右边开始,遇到第一个1之前,全都填0。
- 遇到第一个1之后,把1填下来。
- 1 之后的全部取反。
文章图片
5. 各种整型类型的数值范围是多少(包括无符号整型)
文章图片
六.浮点数据类型 1. 处理带小数的数值(float) 类似于1.234567,0.00001,这类非整数的数据。int能不能装这些数据呢?
#include
int main()
{int a = 1.234567;
int b = 0.00001;
int c = 365.12345;
printf("%d\n", a);
printf("%d\n", b);
printf("%d\n", c);
return 0;
}
文章图片
小数部分都丢失了
这时候就需要引入新的数据类型了——浮点类型。
把上面代码中的整型 int ,用单精度浮点型 float 替代。
之后,再将 printf("%d\n",a); 中的占位符是 %d ,所以我们用 %f 替换。( %d 占位符用于整型, %f 占 位符用于浮点型)
替换完成之后,再来试试看
#include
int main()
{float a = 1.234567;
float b = 0.00001;
float c = 365.12345;
printf("%f\n", a);
printf("%f\n", b);
printf("%f\n", c);
return 0;
}
文章图片
将 int 替换成 float 之后,大部分的数据都是正确的。但是 365.12345 变成了 365.123444 ,很明显精度出 现了误差。
这是因为,浮点数并不能表示无限的精确,它会存在着一定的误差。
C标准规定,float类型必须至少能表示6位有效数字,并且取值范围至少是10^-37~10+37。
所以,使用float来装365.12345时,前面六位数值是准确的,但是后面的数值略有误差。
2. 浮点类型:double 有没有比float,精度更高的类型呢?有的,叫做双精度浮点型 double 。把上面代码中的 float 换 成 double 。
那么 printf函数 中使用的占位符需要修改吗?
答案是不需要,我们先记住float和double均可以使用 %f 来作为占位符。
#include
int main()
{double a = 1.234567;
double b = 0.00001;
double c = 365.12345;
printf("%f\n", a);
printf("%f\n", b);
printf("%f\n", c);
return 0;
}
文章图片
这次 365.12345 也是正确的了。
但是,请注意 double 类型也是有精度范围的。如果是更高精度的数据, double 也会出现误差。 我们日常的应用中,不会苛求一个精度完美的数值,是会允许存在一定误差范围的。但是,如果涉及高精度领域的计算时,需要额外使用特殊的方法进行数值计算,以尽量减少误差。
3. 浮点类型所占字节大小 按照之前对整型的经验,越大范围的整型类型所占的空间越大。 那么对于浮点类型来说,越高精度、越大范围的浮点类型,应该也会占用越大的空间。 用sizeof来测量一下float和double分别占用多大的空间。
#include
int main()
{printf("sizeof float = %d\n",
sizeof(float));
printf("sizeof double = %d\n",
sizeof(double));
return 0;
}
文章图片
float , double 分别为4,8个字节。验证了之前对于越大范围,越高精度的类型所占空间越大的设想。
七.C语言的变量与常量 1. 变量与常量 在整个程序的运行过程中,没有变化且不能变化的,我们称之为常量。
在程序运行期间,可以改变并且有可能会改变的,我们称之为变量。
2. 声明变量 在前几节中,已经接触过如何声明一个变量:
short s;
int n;
long l;
float f;
double d;
声明变量的公式:类型 + 标识符 + 分号
标识符:由我们自己命名的一个标识,用于表示一个变量、函数或其他实体的名称。 例如:上面的代码 中s,n,l,f,d是由我们自己命名的一个标识,用于表示一个变量。
关键词:在语言标准中规定词汇,并且在代码中有特殊意义和用途。 例如:short,int,long,float, double都是表明变量的类型,它们是由语言标准中提供的词汇。
【C语言学习|最最最详细的C语言教程笔记零起步(3)小白必备 同笔者一起学习】标识符必须经过声明或定义才能正常被编译器识别,而关键词可以直接使用。 因此,要让标识符被编译器看做是一个变量,必须将标识符在使用前声明为一个变量。
接下来举几个例子:
正确, 变量使用前被声明:
//正确
#include int main()
{int a;
printf("%d\n", a);
//正确, 变量使用前被声明了。return 0;
}
错误, 变量a未声明:
//错误
#include
int main()
{printf("%d\n", a);
//错误, 变量未声明。return 0;
}
错误, 变量不应该在声明前使用:
//错误
#include int main()
{printf("%d\n", a);
//错误, 变量在声明前使用。
int a;
return 0;
}
3. 变量名命名规则 声明变量的公式:类型 + 标识符 + 分号
既然变量名是一个标识符,因此变量名必须符合标识符的命名规则。
标识符命名规则:标识符由大小写字母,数字和下划线组成。标识符不能以数字开头,并且必须与现有 的关键词不同。
short apple;
正确
int 88fruit;
错误,不能以数字开头
long _pencil;
正确,可以以下换线或字母开头
float love_you;
正确,字母开头,标识符可以使用下划线
double int;
错误,不能与现有关键词相同
4. 变量初始化和赋值 4.1 初始化和赋值
下面的代码,它的打印出来的数值是多少呢?
#include
int main()
{int a;
printf("%d\n", a);
return 0;
}
突然发现,它居然报错了!
文章图片
因为 变量a 没有被装入任何确定的值,它就被使用了。现在它的值是一个随机值,这样的随机值会导致程序产生错误的结果。 因此,在Visual Studio 2019中默认设置下,使用这种变量是无法通过编译的。 我们需要为变量a装入确定的值,可以通过两种方法实现:
- 变量声明后立刻初始化。
- 变量先声明,其后再为变量赋值。
#include
int main()
{int a = 100;
//变量声明后,立即装入100。
printf("%d\n", a);
return 0;
}
写法2:变量声明后,不初始化。后续使用赋值运算符赋值。
#include
int main()
{int a;
//变量声明后,为一个随机值
a = 100;
// 这里我们使用赋值运算符,将100给了a。
printf("%d\n", a);
return 0;
}
4.2 初始化和赋值的区别
赋值:
a = 100;
赋值运算符左边为已经声明过的变量,右边为数值。这种写法为给变量赋值。
初始化:
int a = 100;
赋值运算符左边为一个变量声明,右边为数值,这种写法为变量声明并初始化。
可以用赋值运算符左边的不同来区分赋值与初始化。
另外,变量可以多次赋值,但是不能被多次初始化。
多次初始化的写法相当于将变量a 声明了两次,在第二次声明变量a 时,编译器发现标识符a已经被使用过了,便会报告一个编译错误。
//正确,将输出100,200。
#include
int main()
{int a;
a = 100;
printf("%d\n", a);
a = 200;
printf("%d\n", a);
return 0;
}
//错误,变量a被重复声明。
#include
int main()
{int a;
a = 100;
printf("%d\n", a);
int a = 200;
printf("%d\n", a);
return 0;
}
目前看来,初始化好像就是把变量声明和赋值写在一行了,对于整型和浮点变量确实如此。 但是,对于后面将见到的其他类型,会略有不同。请区别对待初始化和赋值,这两个概念。
5. 常量 常量是程序中,一开始就被写死在代码中,且不能被改变的量。
5.1 字面常量
例如:100,200,1.3344,“HelloWorld”,被称之为字面常量。 字面常量不需要声明,并且编译器通过它的写法可以立即判断出它的类型。
100 = 101;
错误,常量不能被更改
100 = a;
错误,常量不能被更改
被双引号包裹的称之为字符串字面常量。
"HelloWorld""HelloWorld" = "你好";
错误,常量不能被更改
5.2 字面常量类型
一个变量的类型在声明的时候被确定。而一个常量的类型,在这个常量写在代码中时也能被确定。
整数字面常量通常是 int 类型的,除非这个整数字面常量数值过大,超过了 int 类型的范围,那么编译器会尝试将它看做 unsigned int 类型。如果更大,那么依次类推为更大范围的整型类型,例如 long , unsigned long , long long , unsigned long long 。
类型 | 范围 |
---|---|
int | -2147483648 ~ 2147483647 |
unsigned int | 0 ~ 4294967295 |
long | -2147483648 ~ 2147483647 |
unsigned long | 0 ~ 4294967295 |
long long | -9223372036854775808 ~ 9223372036854775808 |
unsigned long long | 0 ~ 18446744073709551615 |
带小数的字面常量为 double 类型。
5.3 符号常量
#include
#define PRICE 3
int main()
{int num = 10;
int total;
total = num * PRICE;
printf("total:%d", total);
return 0;
}
定义了一个符号常量 PRICE 。这样做有什么好处呢?
比如,这个程序很庞大,商品的价格不仅仅被用在 main 函数中,还被用在其他很多函数里面。
如果有一天,这个商品的价格改变了。那么我们就需要到每一个使用过的地方进行修改,这样修改起来会非常麻烦。
但是,我们把商品的价格定义为符号常量,这样只需要修改这个符号常量所代表的值即可。
定义符号常量的公式:
#define PRICE 3
八.字符常量及字符变量 1. 字符常量 字符串是由什么组成的呢? 字符 如果我单独用字符来打印 HelloWorld ,将代码按照如下方式修改:
#include
int main()
{printf("H");
printf("e");
printf("l");
printf("l");
printf("o");
printf("W");
printf("o");
printf("r");
printf("l");
printf("d");
return 0;
}
这样看上去,的确是在一个个地输出字符。但是,要注意,字符串是用双引号包括的。也就是说, 这上面输出的仍然是字符串,只不过每一个字符串仅包含一个字符。
那怎样,用单个字符的形式来输出呢?
定义:字符常量由单引号包括。
类似于’a’ , ‘b’ , ‘c’ , ‘1’ , ‘2’。这样的都是字符常量。 字符常量通常只写一个字符,如果需要多个字符,请使用字符串"ab"。 既然单引号包括的是字符,这样打印行不行?
#include
int main()
{printf('H');
printf('e');
printf('l');
printf('l');
printf('o');
printf('W');
printf('o');
printf('r');
printf('l');
printf('d');
return 0;
}
答案是不行的,这样会导致编译报错。因为, printf的第一个参数必须是字符串 。 既然如此,我们考虑是否能用 printf 函数的占位符来给字符占位呢?
整数int类型占位符为 %d
浮点double类型占位符为 %f
字符类型的占位符为 %c
#include
int main()
{printf("%c%c%c%c%c%c%c%c%c%c%c", 'h', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\n');
return 0;
}
2. 探究字符类型占用空间 现在用 sizeof 来探究一下,字符类型在计算机内部所占用的空间大小。
2.1 字符常量的大小
#include
int main()
{printf("sizeof a= %d\n", sizeof('a'));
printf("sizeof b= %d\n", sizeof('b'));
printf("sizeof c= %d\n", sizeof('c'));
printf("sizeof d= %d\n", sizeof('d'));
printf("sizeof e= %d\n", sizeof('e'));
return 0;
}
文章图片
在Visual Studio中,如果你使用后缀名c为后缀名,将使用C编译器。而使用cpp为后缀名,将使用 C++编译器。
字符类型常量在C编译器将常量看成整型,因此占4个字节。而在C++编译器中只占1个字节。
2.2 字符变量的大小
#include
int main()
{char c = 'A';
printf("sizeof char= %d\n", sizeof(char));
printf("sizeof c= %d\n", sizeof(c));
return 0;
}
文章图片
字符类型的变量仅占用1个字节。
3. 错误导致的意外收获 在前面的代码中,使用 %c 作为字符类型的占位符。假如不小心用错了占位符,使用了整型占位符 %d ,会发生什么呢?
#include
int main()
{printf("%d %d %d %d %d", 'a', 'b', 'c', 'd', 'e');
return 0;
}
文章图片
观察结果,字符’a’,‘b’,‘c’,‘d’,‘e’,使用整型占位符进行打印,结果居然为一些整型数值,并且数值 居然是连续的。所以有理由推测,字符和数值,存在某种联系。
4. 整数与字符的对应关系——ASCII 计算机没必要直接存储字符,可以用某个数值来代表某个字符。 而这个映射关系,被称作美国信息交换标准代码(American Standard Code for Information Interchange)。我们一般将其简称为 ASCII 。
ASCLL百度百科
ASCII 设计的时候,仅仅考虑了拉丁字符,0到127分别对应一个字符。而一个字节最多可以表示256个 数,所以,字符类型仅需要一个字节就能正常存储。刚刚用 sizeof 计算出字符类型占用一个字节 的大小也应验了这个设计。
5. 字符类型与字符变量 字符在计算机内部用整数表示,并且只需要用到0至127的整数范围。
若要显示成实际的字符,由printf函数根据占位符 %c 将其转换为真实的字符并打印在控制台上。
既然这样,没必要为字符专门新设计一种类型,字符类型与只占用一个字节的整数类型完全一致。
之前学习过仅占一个字节的整型类型— char 类型。为了表示这个类型也能用于字符,这种类型使用英语 character 的缩写 char 来表示。
定义一个字符变量
char c1 = 'a';
char c2 = '\n';
char c3 = '1';
用字符占位符,打印这三个变量。输出结果为三个字符。
#include
int main()
{char c1 = 'a';
char c2 = '\n';
char c3 = '1';
printf("c1=%c c2=%c c3=%c", c1, c2, c3);
return 0;
}
文章图片
**\n 为换行符,它能结束这一行的打印,从下一行开始新的打印。**它是一个字符,而不是两个,在下面的转义字符中,将讨论它。
用整型占位符,打印这三个变量。输出结果为这三个字符在ASCII中所对应的数值。
#include
int main()
{char c1 = 'a';
char c2 = '\n';
char c3 = '1';
printf("c1=%d c2=%d c3=%d", c1, c2, c3);
return 0;
}
文章图片
字符本身是一种整型,让printf打印出字符本身还是字符所对应的数值,就看使用哪一种占位符。
6. 探究字符串常量占用空间 接下来,我们来探究一下字符串所占用的字节空间的大小。
#include
int main()
{printf("sizeof HelloWorld = %d\n" , sizeof("HelloWorld"));
return 0;
}
文章图片
字符串结尾处,为了标记这个字符串已经结束了,会在字符串会多占用一个字节,并在这个字节里面填数值0,用于标识字符串结束。
7. 字符零与数值零 既然零表示字符串结束,我们尝试在字符串里面故意添加零,强行让字符串结束。
#include
int main()
{printf("Hello0World");
return 0;
}
零被当成字符正常输出了。其实,这也是可以遇见的一个结果。如果零不能被正常输出,那就 连"100"这种字符串都无法正常打印。
让我们再仔细观察一下ASCII表中的字符 0 ,它对应的数值其实是十进制的 48 。
文章图片
我们需要的是数值 0 ,而不是字符 0 。
8. 转义字符 那怎样表示数值 0 呢,需要再次尝试代码。
#include
int main()
{printf("Hello\0World");
return 0;
}
文章图片
HelloWorld字符串被 \0 强行截断了,printf函数只打印出Hello就认为字符串已经结束了。
\数值 被称作转义字符 。
转义字符虽然写法上有多个字符,实际上它对应ASCII表中的一个字符。
由于直接在字符串中写0会被认为是字符0,而 \数值 这种写法,可以让我们输入数值0。但是,这个数值不是用十进制来表示的,而是用八进制。
那现在尝试直接使用数值来打印字符串Hello,注意哦,斜杠后面跟着的是八进制数值。
文章图片
文章图片
文章图片
文章图片
从上表中找到Hello所对应的十进制数,然后转成八进制数。分别为110,145,154,154,157。
#include
int main()
{printf("\110\145\154\154\157");
return 0;
}
文章图片
接下来,来介绍一个特殊的数值,十进制10,八进制12。看看这个数值打印出来是什么效果
#include
int main()
{printf("Hello\12World");
return 0;
}
文章图片
如你所见,它和 \n 的效果一致,没错它们是同一个东西。 \12 就是我们之前一直在用的换行 \n 。而为 什么换行会有两种写法呢?
由于记数值是比较麻烦的,为了方便使用一些常用到的数值可以用字母来替代。
例如:
文章图片
转义字符表
有一些字符无法直接在键盘上输入,这一类字符被称为 不可见字符 。就像上面的表中列出的字符,这些字符可以用斜杠加数值来表示,也可以使用斜杠加助记字母来表示。
但是不是所有的不可见都有助记字母,所以尽可能还是需要查表来获得这些字符所对应的数值。
在ASCLL表中,数值为十进制0到31所对应的字符为不可见字符。
点个赞吧?
推荐阅读
- JS中的各种宽高度定义及其应用
- 祖母走了
- 唯独你最得我意
- 人生感悟记#环境仪器宋庆国成长记#072
- 危险也是机会
- “精神病患者”的角度问题
- 对抗抑郁最好的方法
- 放下心中的偶像包袱吧
- 怎样用黑谜速冻膜去黑头,|怎样用黑谜速冻膜去黑头, 最有效的去黑头的方法看这!
- 拉黑家人一整年之后,以为会快乐,最后却抑郁症!!