继续走,莫停留——C语言数据类型和变量

一、数据类型介绍以及signed和unsigned

现实生活中,你会见到各种各种数据,比如这是某饮料的营养成分表

通过该表你能看到有文本类型的数据,如:能量、蛋白质......

有整数数据:100、217

有小数数据:1.8、1.4......

同样,C语言也内置了四种数据类型,分别是: 字符型、整型、浮点型和布尔类型

同时C语言使用 signed 和 unsigned 关键字修饰字符型和整型类型

signed 关键字,表示一个类型带有正负号,包含负值;

unsigned 关键字,表示该类型不带有正负号,只能表示零和正整数。

下面一一介绍这几种类型以及取值范围。

1.1字符型

 字符的英文单词是character,所以C语言中用char表示字符类型。

char                   //character

[signed] char     //有符号的

unsigned char   //无符号的

字符是什么见故事的开始总是极具温柔——C语言常见概念-优快云博客第九节

取值范围:

signed char c; // 范围为 -128 到 127

unsigned char c; // 范围为 0 到 255

注意,C语言规定 char 类型默认是否带有正负号,由当前系统决定。这就是说, char 不等同于 signed char ,它有可能是 signed char ,也有可能是unsigned char 。

1.2整型

整数的英文单词是integer,所以C语言中用int表示整型类型。

    int                        //定义整型变量

    signed int

    unsigned int

    long int b;           //定义长整型变量

    signed long int

    unsigned long int

    long long int c;   //定义更长的整型变量

    signed long long int

    unsigned long long int

    short int d;         //定义短整型

    signed short int

    unsigned short int

对于 int 类型,默认是带有正负号的,也就是说 int 等同于 signed int 。 由于这是默认情况,关键字 signed ⼀般都省略不写,但是写了也不算错。

取值范围:

 16位系统,int(signed int)取值范围是(-2^{15}\sim 2^{15}-1),

                   unsigned int取值范围是(0\sim 2^{16}-1

32位系统,int(signed int)取值范围是(-2^{31}\sim 2^{31}-1),

                   unsigned int取值范围是(0\sim 2^{32}-1

里如果你学过数字电路,应该很快就能理解,这里以16位为例:

16位的无符号位的int,可以表示0000 0000 0000 0000~1111 1111 1111 1111,共2^{16}-1种,所以取值范围是0\sim 2^{16}-1

有符号位的int,最高位表示符号位,其中1表示负号,0表示正号,所以16位的有符号位的int,可以表示1000 0000 0000 0000~0111 1111 1111 1111,也是共2^{16}-1种,但是取值范围是-2^{31}\sim 2^{31}-1

授人以鱼不如授人以渔,这里介绍一个函数sizeof()

每⼀种数据类型都有自己的长度,使用不同的数据类型,能够创建出长度不同的变量,变量长度的不同,存储的数据范围就有所差异。

#include <stdio.h>

int main()
{
 printf("%zd\n", sizeof(short));
 printf("%zd\n", sizeof(int));
 printf("%zd\n", sizeof(long));
 printf("%zd\n", sizeof(long long));
 return 0;
}

X64配置下运行结果:

这里的单位是字节,一字节=8bit,每个bit可以存放一位2进制数

以第一个short为例,运行出来2字节,换算出来就是2*8=16bit,所以有符号位的取值范围就是-2^{16-1}\sim 2^{16-1}-1也就是-2^{15}\sim 2^{15}-1

最后再强调一下,取值范围和所配置的环境有关,所以取值范围并不一定都一样!!!

1.3浮点型

float

double

long double

1.4布尔类型

布尔类型的使用得包含头文件

布尔类型变量的取值是: true 或者 false 。

#define bool _Bool

#define false 0

#define true 1

如:

	bool flag = true;

	if (flag)
		printf("i like C\n");

已经定义了flag为布尔类型的变量,且为真,所以if里面的条件成立,打印printf

故运行结果:

2. 变量

什么是变量?顾名思义就是变化的量。

现实生活中有很多变量,比如身高、体重、温度、年龄......

与之相对的还有常量,比如:光在真空中的传播速度......(本来想举例性别,怕有人说去趟泰国可以变,还想举例水的密度,怕又有人说水在不同温度下的密度是变化的,所以举这个例子,这个总不会变吧)

2.1变量的创建

数据类型说了那么多,有啥用呢?

欸,就是用来创建变量的。

变量创建的语法形式如下:

data_type name;

data_type是变量类型,name是你定义这个变量的名字。

举例1:

int age;

这里的data_type是整型,name是age,所以定义的是名为age的整型变量。

举例2:

char ch;

这里的data_type是字符型,name是ch,所以定义的是名为ch的字符型变量。

举例3:

double weight; 

这里的data_type是浮点型,name是weight,所以定义的是名为weight的浮点型变量。

变量名命名规则:

• 只能由字母(包括大写和小写)、数字和下划线(`_`)组成。

• 不能以数字开头。

• 长度不能超过63个字符。

• 变量名中区分大小写的。

• 变量名不能使用关键字。

在创建一个变量的时候就给定一个初始值就叫变量的初始化。

int age = 18;

char ch = 'w';

double weight = 48.0;

unsigned int height = 100;

2.2全局变量和局部变量

这个直接上代码吧

#include<stdio.h>
int i = 2024;
int main()
{
	int i=2025;
	printf("%d", i);
	return 0;
}

这个其实很好理解

大括号外面定义的变量是全局变量,大括号内部定义的变量是局部变量。

这里的i=2024是全局变量,i=2025是局部变量

全局变量括号内也能使用,使用的范围更广

局部变量只能括号内使用,括号外不能使用

还有就是局部变量的使用优先级最高,也就是括号里面定义了优先用里面变量,没定义就用括号外的。

所以这个代码运行结果是

如果你是个像我一样的小白,你或许根本不会关注变量的存储地址。

需要注意的是一般我们在学习C/C++语言的时候,我们会关注内存中的三个区域:栈区、堆区、静态区。

1. 局部变量是放在内存的栈区

2. 全局变量是放在内存的静态区

3. 堆区是用来动态内存管理的

想了解关于存储更详细的内容,请移步《计算机组成原理》和《数据结构》


3. 算术操作符:+、-、*、/、%

这个更没啥好说的,不就是简单的加减乘除和取余嘛,小学二年级都知道的东西。

稍微要补充的知识点就是:

3.1 /

#include<stdio.h>
int main()
{
	int a = 10 / 4 ;
	float b = 10 / 4 ;
	printf("%d\n", a );
	printf("%f\n", b );
	return 0;
}

定义了a为整型变量,计算结果直接取整数部分,小数部分直接忽略,而不是四舍五入。

定义了b为浮点类型,但由于表达式的分子分母都是整型,所以计算类型直接变成了整型2,前面加了float设计类型转换,又把int类型的2转变成float类型的2.000000,所以结果是:

这个时候你想说了,如果我就想计算出对的值(2.5)咋办?

直接把分母分子的任意一个写成小数形式,这样运算后的类型也是浮点型,这样就保留了2.5这个结果。

#include<stdio.h>
int main()
{
	float a = 10.0 / 4;
	float b = 10 / 4.0;
	printf("%f\n", a);
	printf("%f\n", b);
	return 0;
}

结果:

试试身手:

#include<stdio.h>
int main()
{
	int score = 5;
	score = (score / 20) * 100;
	printf("%d", score);
	return 0;
}

如果你还说这个score结果为25,那就大错特错了。

运行的优先级和小学学的一样,有括号先算括号里面的

首先score定义成整型的5,分母是整型的20,两个整型相除,结果是整型的,前面说过,整型的取值是直接略去小数部分的,所以结果为0

0再乘以100,结果还是为0

所以这道题目的答案为:

那要怎么把这个结果变成我们小学二年级所学过的正确答案呢?

前面也说了,这个运算的数学错误究其本质是因为括号内把小数忽略了,所以我们把分母改成小数,使得括号内运行结果变成浮点类型,不就对了吗?

#include<stdio.h>
int main()
{
	int score = 5;
	score = (score / 20.0) * 100;
	printf("%d", score);
	return 0;
}

答案:

3.2 %

首先这个运算符只能用于整数,不能用于浮点数。

比如:

#include<stdio.h>
int main()
{
	int x = 6 % 4.0; // 2
	printf("%d", x);
	return 0;
}

运行结果:

其次负数求模的规则是,结果的正负号由第一个运算数的正负号决定。

#include <stdio.h>

int main()
{
 printf("%d\n", 11 % -5); 

 printf("%d\n",-11 % -5); 

 printf("%d\n",-11 % 5); 

 return 0;
}

结果:

从答案可以看出,结果的正负号只和%前面值的正负号有关。


4. 赋值操作符:=和复合赋值

这个要区别于后面的==,=是赋值,后面赋值给前面。

比如:

int a=10;//初始化
a=20;//赋值,把20赋给a

4.1连续赋值

赋值操作符也可以连续赋值,如:

int a=1;
int b=2;
int c=3;
c=b=a-1;

连续操作也是从右向左依次赋值,这里是先计算a-1,然后赋值给b,再把b赋值给c。

4.2复合赋值

在写代码的时候,我们可能对一个数进行自增或者自减的操作,如:

int x=0;
x=x+2;
x=x-7;

C语言提供了更加方便的写法:

int x=0;
x+=2;
x-=7;

其他复合赋值符号还有:

+=    -=    /=     *=     %=

还有(以下操作符后期再说):

>>=    <<=     &=    |=     ^=


5. 单目操作符:++、--、+、-

以加法为例,运行如下代码:

#include <stdio.h>
int main()
{
	int a = 0;
	int b = 0;
	int c = 0;
	int d = 0;
	a = a + 1;//语句一
	b += 1;//语句二
	c++;//语句三
	++d;//语句四
	printf("%d %d %d %d", a,b,c,d);

	return 0;
}

你会发现a,b,c,d的结果都为1,学过前面的内容你也知道了,语句二是语句一的简短表达,运行的过程没有任何区别,那c++和++d有区别吗?这就涉及了是先赋值还是先++了。

5.1++和--

这里主要要介绍前置++与--和后置++与--的区别

5.1.1后置++与--

前置的特征就是符号在前,这里计算的区别是先++,再赋值

#include <stdio.h>
int main()
{
	int i = 1;
	int j = 0;
	j = i++;
	printf("%d",j );
	return 0;
}

比如这段代码,咋一看 j 等于多少?

有人说,诶,我知道,i++是i+1,初始i=0,加一后就是1,再赋值给 j ,所以打印出来j=1。

但是运行一看

不对啊,这和分析不服啊,是不是编译器出问题了?

nonono,注意看原代码,是i++,先赋值,后++

也就是说先将 i 赋值给 j ,然后 i 再加一,所以段代码等效于

#include <stdio.h>
int main()
{
	int i = 1;
	int j = 0;
	j = i;
    i=i+1;
	printf("%d",j );
	return 0;
}

后置--同理,不再赘述了。

5.1.2前置++和--

举完上面的例子,这个也就很容易理解了,前置++就是先++后赋值

也就是说代码

int i = 1;
int j = 0;
j = ++i;

等效于

int i = 1;
int j = 0;
i = i + 1;
j = i;

前置--同理,也不再赘述了。

巩固一下

#include <stdio.h>
int main()
{
	int a = 5;
	int b = 5;
	int c = 5;
	int d = 5;
	printf("%d %d %d %d\n", a++, ++b, c--, --d);
	printf("%d %d %d %d\n", a, b, c, d);
	return 0;
}

以上代码两个printf函数打印的分别会是什么值?

分析:

a++:先赋值后++,所以先把 a 赋值给第一个 printf 函数,第一个 printf 打印 5 ;再 ++,所以第              二个 printf 打印 6 。

++b:先++后赋值,所以先运行 b=b+1,再赋值给第一个printf,所以两个 printf 均打印6。

c--:先赋值后 --,所以先把c赋值给第一个 printf 函数,第一个 printf 打印 5;再 -- ,所以第二个            printf 打印 4 。

--d:先 -- 后赋值,所以先运行 d=d-1,再赋值给第一个printf ,所以两个 printf 均打印4。

综上分析:第一个 printf 打印:5 6 5 4;第二个 printf 打印:6 6 4 4

结果:

5.2 +和-

这里的 + 是正号,- 是负号,和我们小学六年级所学的符号一致。

运算符 + 对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。

int a = +10; 等价于 int a = 10;

运算符 - 用来改变一个值的正负号,负数的前面加上 - 就会得到正数,正数的前面加上 - 会得到负 数。

int a = 10;

int b = -a;

int c = -10;

printf("b=%d c=%d\n", b, c);//这⾥的b和c都是-10

int a = -10;

int b = -a;

printf("b=%d\n", b); //这⾥的b是10

这个理解都不难,所以也不赘述了。

6. 强制类型转换

在操作符中还有一种特殊的操作符是强制类型转换,语法形式很简单,形式如下:

(类型)

比如这个代码:

	int e = 2.71828;
	printf("%d", e);

e定义的是int类型,但是赋值确是小数,所以用的时候编译器会报警告

为了消除这个警告,我们可以使用强制类型转换:

	int e =(int)2.71828;
	printf("%d", e);

意思是将2.71828强制类型转换为int类型,这种强制类型转换只取整数部分

了解有这个功能就好,能不用就不用。

7. printf和scanf介绍

7.1 printf

7.1.1基本用法

关于这个已经在故事的开始总是极具温柔——C语言常见概念-优快云博客第七节介绍过了,所以直接进入占位符的介绍。

7.1.2 占位符

快上课了,你一边马不停蹄的往教室赶,一边联系你的好兄弟:“帮我占个座位。”

你好兄弟听完,啪!把一本书甩在一个座位上,其他人看到这本书也不会选择这个位置入座。

等你赶到教室,坐到你好兄弟占座的位置,你好兄弟把书拿走,进行了你和书发生“替换”的过程。

这本书就好比C语言中的占位符,在使用printf打印的时候,你可以在其中增添占位符,并在最后加入你要添加的部分,printf打印过程中,就会默认用后面的值替换占位符。

举个例子:

#include <stdio.h>
int main()
{
	printf("我得到%d元",100);
	return 0;
}

这里的%d就是占位符,这个值用后面的100替代,所以打印出来的内容是:

占位符的第一个字符一律为百分号 %,第二个字符表示占位符的类型。

%d 表示这里代入的值必须是一个整数。

7.1.2.1 %a

%a:十六进制浮点数,字母输出为小写。

根据C语言标准,%a的标准输出格式为:\left ( - \right )0xh.hhhhhhhhhhhhhp\pm d,其中0x表示十六进制数,h是十六进制数字,小数点后包含尽可能多的数字,以准确表示该浮点数的精度。\pm d为指数,p\pm d表示以2的\pm d次方。

举例:

	printf("%a\n", 0.5);//0x1.0000000000000p-1
	printf("%a\n", -0.5);//-0x1.0000000000000p-1
	printf("%a\n", 27.0);//0x1.b000000000000p+4

第一个,因为0.5=1\times 16^{^{0}}\times 2^{^{-1}},所以输出为0x1.0000000000000p-1

第二个,因为-0.5=-1\times 16^{^{0}}\times 2^{^{-1}},所以输出为-0x1.0000000000000p-1

第三个,因为27.0=1\times 2^{^{4}}+11\times 16^{-1}\times 2^{4},所以输出为0x1.b000000000000p+4

运行结果:

7.1.2.2 %A

%A:十六进制浮点数,字母输出为大写。

这个和%a差不多,唯一的区别就是大小写的区别。

比如:

	printf("%A\n", 0.5);//0X1.0000000000000P-1
	printf("%A\n", -0.5);//-0X1.0000000000000P-1
	printf("%A\n", 27.0);//0X1.b000000000000P+4

对比%a你能发现,相比于%a,表示十六进制的0x变成了大写的0X,十六进制数b变成了大写的B,表示底数2的p变成了大写的P。

7.1.2.3 %c

%c:字符。//char

	printf("%c\n", 'A');  // 输出: A
	printf("%c\n", 65);  // 输出: A(65是'A'的ASCII码)

%c占位字符型,碰到数字自动转换成ASCII码中的对应字符。

感觉没啥好说的,唯一注意的就是打印字符记得加  '  '   (单引号)

7.1.2.4 %d

%d:十进制整数(有符号的10进制整数)。//int

引入占位符就是这个例子,这里就不说了吧。

7.1.2.5 %e

%e:使用科学计数法的浮点数,指数部分的 e 为小写。

这个理解就简单了,就是小学所学的科学计数法,e表示10的几次方。

	printf("%e\n", 16.0);//1.600000e+01
	printf("%e\n", -16.0);//-1.600000e+01
	printf("%e\n", 0.5);//5.000000e-01

第一个,16.0=1.6\times 10^{^{1}};第二个,-16.0=-1.6\times 10^{^{1}};第三个,0.5=5\times 10^{-1}

7.1.2.6 %E

%E :使用科学计数法的浮点数,指数部分的 E 为大写。

这个和上面%e一样,就是用E表示10的几次方而已。

	printf("%E\n", 16.0);//1.600000E+01
	printf("%E\n", -16.0);//-1.600000E+01
	printf("%E\n", 0.5);//5.000000E-01
7.1.2.7 %i

%i :整数,基本等同于%d 。

既然基本等于%d了,这里就不说了。

7.1.2.8 %f

%f:小数(包含 float 类型和 double 类型)。//float-%f double-%lf

对于printf函数来说,float类型和double类型都能用%f占位,这里与scanf函数进行区分,scanf中%f用于读取float类型变量(需指针传递),%lf用于读取double类型变量。默认保留6位小数。

	float a = 1.0;
	double b = 1.0;
	printf("%f %f", a, b);//1.000000 1.000000

 

涉及小数,当然无法避免尾数保留问题,%.nf就能保留小数,其中n表示保留n位有效数字。

	printf("%.2f\n", 2.7161);//2.72
	printf("%.2f\n", 2.7151);//2.72
	printf("%.2f\n", 2.7150);//2.71
	printf("%.2f\n", 2.7149);//2.71

聪明的你可以发现,尾数取舍方式为4舍6入,如果为5,则再往下看下一位,下一位不为0则入,否则舍去(这句话理解起来困难的话就看上面代码,相信看完之后就好理解了)。

%m.nf表示指定总宽度为m字符,保留n位小数(右对齐,不足补空格)

7.1.2.9 %g

%g:6个有效数字的浮点数。整数部分一旦超过6位,就会自动转为科学计数法,指数部分的 e

为小写。

7.1.2.10 %G

%G :等同于 %g ,唯一的区别是指数部分的 E 为大写。

7.1.2.11 %hd

%hd :十进制 short int 类型。

7.1.2.12 %ho

%ho :八进制 short int 类型。

7.1.2.13 %hx

%hx :十六进制 short int 类型。

7.1.2.14 %hu

%hu :unsigned short int 类型。

7.1.2.15 %ld

%ld :十进制 long int 类型。

7.1.2.16 %lo

%lo :八进制 long int 类型。

7.1.2.17 %lx

%lx :十六进制 long int 类型。

7.1.2.18 %lu

%lu :unsigned long int 类型。

7.1.2.19 %lld

%lld :十进制 long long int 类型。

7.1.2.20 %llo

%llo :八进制 long long int 类型。

7.1.2.21 %llx

%llx :十六进制 long long int 类型。

7.1.2.22 %llu

%llu :unsigned long long int 类型。

7.1.2.23 %Le

%Le :科学计数法表示的 long double 类型浮点数。

7.1.2.24 %Lf

%Lf :long double 类型浮点数。

7.1.2.25 %n

%n :已输出的字符串数量。该占位符本身不输出,只将值存储在指定变量之中。

7.1.2.26 %o

%o :八进制整数。

7.1.2.27 %p

%p :指针(用来打印地址)。

7.1.2.28 %s

%s :字符串。

7.1.2.29 %u

%u :无符号整数(unsigned int)。

7.1.2.30 %x

%x :十六进制整数。

7.1.2.31 %zd

%zd : size_t 类型。

7.1.2.32 %%

%% :输出⼀个百分号。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fresh_man2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值