1·signed unsigned

2·数据类型及其取值范围

3·变量

4·操作符

5·强制类型转换

6·printf 和 scanf 详细介绍


1

在介绍数据类型之前,先介绍signed 和 unsigned。

signed和unsigned都是C语言中的关键字,它们的作用是决定字符或者整型的正负号。

signed(有符号)表示该数据可正可负,unsigned修饰一个数据的时候表示该数据只能是正数或者0。

而int,整型,是默认带有符号的,你可以写成signed int,也可以写成int,两种写法都是正确的。写成unsigned int 就是表示非负整数了,当然,也可以省略int ,直接写unsigned。

但是这两个的区别不止这个,还有取值范围。unsigned就厉害了,直接让正数取值范围扩大了一倍,因为数在计算机是二进制存储的,开头的数是表示符号,0表示正,1表示负。

inunsigned int类型比int类型的容量大是因为,unsigned int是无符号类型的,所以最高位不表示正负,而int类型的最高位是表示正负的。

根据VS.2022环境中limits.h(整型的取值范围。如果是float.h就是浮点型的取值范围)的文件我们来看一下取值范围。

浅浅了解一下进制

16位的 signed short int 的取值范围是:-32768~32767,最⼤是32767;⽽ unsigned short int 的取值范围是:0~65535,最⼤值增⼤到了65,535。

好了,注意,字符类型的signed和unsigned和整型是有区别的。

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


2

编程为生活服务,而生活中的元素有不同的种类,编程中也是。

下面介绍不同的数据类型及其取值范围。

如图,数据类型有两大类,今天就内置类型进行介绍,自定义类型后续跟进。

字符

字符在前一篇文章已经进行了较为详细的介绍,这里暂且略过。

整型

整型,整数类型的意思,在C语言中,将整型进行了一个细分,分为短整型,整型,长整型,更长的整型,注意,更长的整型是在C99中引入的。

对应的代码为 short int , int ,long int ,long long int 。当然除了整型的int不可省略,其他可以省略。

既然分为长短,那么取值范围就有区别,我们根据limits.h进行直观的理解。

• SCHAR_MIN , SCHAR_MAX :signed char 的最⼩值和最⼤值。

• SHRT_MIN , SHRT_MAX :short 的最⼩值和最⼤值。

• INT_MIN , INT_MAX :int 的最⼩值和最⼤值。

• LONG_MIN , LONG_MAX :long 的最⼩值和最⼤值。

• LLONG_MIN , LLONG_MAX :long long 的最⼩值和最⼤值。

• UCHAR_MAX :unsigned char 的最⼤值。

• USHRT_MAX :unsigned short 的最⼤值。

• UINT_MAX :unsigned int 的最⼤值。

• ULONG_MAX :unsigned long 的最⼤值。

• ULLONG_MAX :unsigned long long 的最⼤值。

那么是什么导致它们的取值范围不同呢?

答案是在内存中占的字节数。

PS:计算机最小存储单位是bit,然后是byte,kb,mb,tb...

一个比特存放0或者1,一个字节等于8比特,1 kb=1024byte...

一个整型占4个字节,短整型占2个字节,长整型占8或者4个字节,更长的整型占8个字节。

有人就问了,欸long为什么是两种情况,这是因为C语言中规定占字节数long>=int,取决于多少位系统。

浮点型

不少人看到这个名字觉得好高深,其实就是小数的意思啦,当然,浮点型分为两种,一种是单精度浮点型,一种是双精度浮点型,一听就知道双精度浮点型的精确度要高点。

C语言中规定,float是单精度浮点型,double是双精度浮点型。

浮点型也是有long double的。

布尔类型

C语言原来并没有为布尔值单独设置⼀个类型,而是使用整数 0 表示假,非零值表示真。 在 C99 中也引入了布尔类型 ,是专门表示真假的。

布尔类型的写法有两种,_Bool和bool,取值有false和true。

使用布尔类型要引用头文件<stdbool.h>的,那么进行一下代码演示。

好了,内置类型已经有了一个大概的了解,那么我们现在来了解一下每种数据类型在内存中的内存大小,在此之前,先了解一下sizeof这个关键词。

sizeof

sizeof既是一个关键词,也是一个操作符,可以用来计算数据类型的所占字节数,也可以用来计算数组所占字节数。

先来看看应用场景之数组大小;

arr1数组有5个字符,而一个字符占1个字节,所以输出结果是5。

但是!

sizeof 运算符的返回值,C 语⾔只规定是无符号整数,并没有规定具体的类型,而是留给系统自己去决定, sizeof 到底返回什么类型。不同的系统中,返回值的类型有可能是 unsigned int ,也有可能是 unsigned long ,甚至是 unsigned long long , 对应的 printf() 占位符分别是 %u 、%lu 和 %llu 。这样不利于程序的可移植性。 C 语言提供了⼀个解决方法,创造了⼀个类型别名 size_t ,⽤来统⼀表示sizeof的返回值类型。对应当前系统的 sizeof 的返回值类型,可能是 unsigned int ,也可能是 unsigned long long 。

所以C语言给了一个解决方法,那就是单独设一个类型别名,即sizeof的返回结果是size_t类型,对应的占位符应该是%zd

sizeof后面可以是类型,也可以是表达式,但是表达式的括号是可以不用加的,类型一定要加括号,如图

虽然sizeof可以计算表达式的所占字节数,但是sizeof里面的表达式是不进行计算的,如图

好了,接下来是不同数据类型的所占字节数;

相信大家也知道代码怎么写了,那么不多说直接上代码

ok数据类型就介绍完了,开启下个内容。


3

C语言中把变化的量称为变量,不变的量称为常量。

这是创建变量的过程。

数据类型决定变量类型。

在VS里面,要求变量最好进行初始化,不然在VS这种对语法要求比较高的环境中,可能会报错。

初始化即是对变量随便赋一个值。

变量也有分类,分为局部变量全局变量

局部变量就是放在大括号之内的变量,如;

#include <stdio.h>
int main()
{
    int a=0;
    return 0;
}

这里的a就是局部变量。

全局变量就是放在大括号之外的变量,如;

#include <stdio.h>
int b=0;
int main()
{
    int a=0;
    return 0;
}

这里的b就是全局变量。

局部变量的作用范围就只有所在的大括号之内,全局变量的作用范围自然就是整个工程。

那么,当局部变量和全局变量冲突的时候结果如何呢?

可以发现,当局部变量和全局变量冲突的时候,局部变量优先

那么它们的存储地方呢?

局部变量存储在内存的栈区,全局变量存储在内存中的静态区,这里稍作介绍,后面深入。


4

操作符也有一定的分类,算术操作符,赋值操作符,复合操作符,单目操作符,双目操作符,三目操作符等等,操作符数不胜数,这里就提到的进行介绍。

PS:操作符也被称为运算符,不同的翻译,意思一样

算数操作符,无非就是进行计算所用的操作符,那么包括的操作符有,

+ - / * %

对应的中文就是加 减 乘 除 取模,取模是取余数的意思,这些操作符与生活中的加减乘除有些许差别,但我认为是可以接受的。

这里+ - * 不进行过多介绍,计算机中运算与生活中无异。

重点介绍 / %

先看一串代码:

提问,结果是什么呢?相信不少刚接触C语言的认为答案是1.5,那么结果是否如此呢?

看来不是,而且细心的人会发现6的下面有三个点,当把鼠标移动过去就会有提醒说“整数除法运算应先去尾再转化为浮点型”,原因在于我们在进行除法运算时两个操作数都是整型,C语言中的整数除法是整除,也就是只会返回整数部分,丢弃小数部分。

所以希望得到浮点数的结果,两个运算数必须至少有一个浮点数。如图:

% 取模,即返回两个数相除的余数,但是这个操作符只能用于整数,不能用于浮点数。

但是负数也是可以进行取模的,但是结果的正负号只有第一个运算数的正负号决定,

如图:

赋值操作符和复合赋值

在定义一个变量的时候给一个初始值叫做初始化,创建好变量后再给一个值叫做赋值。

int a = 100;//初始化
a = 200;//赋值,使用的就是赋值操作符

所以=就是赋值操作符。

当然,也可以进行连续赋值,如:
 

int a = 1;
int b = 2;
int c = 0;
c = b = a+3;//连续赋值,从右向左依次赋值

C语言支持这种写法,但是这种代码不易理解,所以建议拆开写。

int a = 5;
int b = 9;
int c = 0;
b  = a + 3;
c = b;

这种就很容易观察细节了。

复合赋值符

写代码的时候我们经常会对一个数进行自增自减的操作,如下

int a = 0;
int b = 0;
a = a+3;
b = b-2;

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

int a = 18;
a += 4;

代码结果是a=22,同理,还有其他复合赋值符

如: +=   -= *= /= %=,这些运算思路和+=是一样的

当然,还有这些复合赋值符,如 >>=  <<=  &=  |=  ^=,后面进行介绍。

单目/双目/三目操作符

单双三的意思就是有几个运算符,如+ - / %需要两个运算符,就代表这些操作符是双目操作符。

单目操作符:++ -- + -

欸刚才不是说+ - 是双目吗,怎么变成单目了,因为这里的+ -是表示正负号的意思。

现在对++ -- 进行介绍。

++分为前置++和后置++,且++是一种自增的操作符,具体如下代码

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

它们的区别在于是先使用在自增还是先自增在使用,巧记的方法是谁在前谁就先使用,

前置++,++在前所以a先自增,在进行使用,后置++同理可得。那么运行结果就比较明显了。

如图:

--的作用方式是一样的,就不进行阐述了。

三目操作符,即是有三个操作数的,在C语言中有且仅有一个三目操作符,

实现代码是 (exp1 ? exp2:exp3)exp是表达式的意思。

先执行表达式1,如果结果为真,那么就执行表达式2,如果结果为假,那么就执行表达式3。

看一段比较简单的代码:

#include <stdio.h>
int main()
{
	int a = 520;
	int b = 1314;
	int c = (a > b ? a : b);
	printf("%d", c);
	return 0;
}


因为a<b,所以执行表达式3,即c的值是1314。

这些操作符就介绍完了,其他的操作符在之后进行介绍。


4

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

int a = 3.14;
//a是int类型,3.14是double类型,两边类型不一致,编译器会报警告

int a = (int)3.14;
//意思就是把3.14强制类型转换为int类型,计算机读取的时候只会读取整数部分

但强扭的瓜不甜,不到万不得已的时候不要使用,不然可能导致精度的丢失。


5

printf详解

printf的基本用法是把参数文本输出到屏幕,print是打印的意思,f代表格式化,表示可以定制输出文本的格式。

#include <stdio.h>
int main()
{
    printf("Hello C");
    return 0;
}

上述代码会在屏幕打印Hello C

但是printf不会自动添加换行符,为了让光标移到下一行的开头可以手动添加换号符,如果文本内部有换行,也可以在内部插入换行符,如图:

#include <stdio.h>
int main()
{
    printf("Hello\nC");
    printf("Hello\n");
    printf("C\n");
    return 0;
}

这些是最最基本的用法。

那么,现在引用占位符的概念;

所谓占位符,就是这个位置可以用其他值带入,如图

#include <stdio.h>
int main()
{
	int a = 1314;
	printf("%d\n", 520);
	printf("%d\n", a);
	return 0;
}

占位符的第一个字符统一为%,第二个字符表示的是占位符的类型,如%d表示的是带入的值必须是一个整数。

输出文本里可以有多个占位符,占位符与参数的关系是一一对应的,如果用%c输出整型,那么编译器就会报错。

如果有n个占位符,printf的参数就应该由n+1个参数,如果参数个数小于对应的占位符,printf可能会输出内存中的任意值。

那么,常见的占位符有这些。

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

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

• %c :字符。

• %d :十进制整数。

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

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

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

• %f :小数(包含 float 类型和 double 类型)。

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

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

• %hd :十进制 short int 类型。

• %ho :八进制 short int 类型。

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

• %hu :unsigned short int 类型。

• %ld :十进制 long int 类型。

• %lo :八进制 long int 类型。

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

• %lu :unsigned long int 类型。

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

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

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

• %llu :unsigned long long int 类型。

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

• %Lf :long double 类型浮点数。

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

• %o :⼋进制整数。

• %p :指针。

• %s :字符串。

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

• %x :十六进制整数。

• %zd : size_t 类型。

• %% :输出⼀个百分号。

当然,不是所有的都常用,随着代码数的增加慢慢就了解了。

占位符有了一定了解,那么介绍printf()可以定制占位符的输出格式

1

printf允许限定占位符的最小宽度

#include <stdio.h>
int main()
{
	printf("%5d", 123);
	return 0;
}

%5d表示这个占位符的宽度至少为5位,运行结果如图

可以看到,123前面由两个空格,当然宽度值至少为5位,那么改成%2d就不会出现前面由空格的情况

输出的默认值是右对齐,如果想要左对齐,只需要在%后面加一个-号就行。

而对于小数而言,这个限定符会限制所有数字的最小显示宽度。

如图

由于小数的默认精度是小数点后6位,所以前面有4个空格。

2

总是默认显示正负号

默认情况下,printf()不对整数显示+号,只对负数显示-号,如果想要在正数前面输出+号。可以在%后面加个+。

3

限定小数位数

有时候觉得小数点后6位太长了,占位符也可以对其进行限制,如图

所以限定的方式是%后面加.n(n是限制数字)。

当然了,这种写法可以结合限定宽度占位符使用,如图

当当当然了,最小宽度和小数位数这两个限定值都可以通过*代替,通过printf()的参数传入,如图

图中的两个*号就是由参数6 2传入的。

4

输出部分字符串

%s是用来输出字符串的,默认是全部输出,如果只想输出部分内容,可以通过%.[n]s指定输出其中的长度,其中[n]代表一个数字,表示要输出的长度。具体的代码实现:

以上就是printf()函数的较为详细的介绍。


6.1

scanf详解

1

基本用法

scanf同printf一样,原型定义在头文件stdio.h。

scanf用于读取用户的键盘输入,它的第一个参数是一个格式字符串,里面放置的占位符与printf基本一致,而要输入一个数,scanf就必须提前知道用户输入的数据类型,才能进行输入。

它的其他参数就是存放用户输入的变量,有多少个占位符就有多少个变量。

注意:变量前面必须加&(指针变量除外),这个符号是取地址符号,因为scanf传递的是地址,而不是值,即变量的地址指向用户输入的值。

先看两个错误例子

一个是没有先定义,一个是没有加&符号。

所以编译器都进行了报错。

scanf()处理数值占位符的时候,会自动过滤空白字符,包括空格,制表符,换行符等。

如图展示

scanf() 处理用户输入的原理是,用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存 进行解读。

解读用户输入时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为止。

怎么理解呢?看代码:

空格全部忽略,读取到-13,发现有个点,就不是整型,就跳过,由于遗留的是. ,对应是%f,所以会读取到.45e,%不属于浮点型的有效字符,所以会停在这里。

当然,scanf也可以像printf一样,拥有多个占位符。

2

scanf的返回值

scanf()的返回值是一个整数,表示成功读取变量的个数,如果没有读取到任何项,或者匹配失败,就会返回0。

如果在成功读取到任何数据前,发生了读取错误或者遇到读取文件结尾,就返回常量EOF。

那么,现在进行测试。

这是正常输入的情况

那么,我如果提前停止输入呢?

VS中按3次ctrl+z才结束了输入,可以看到r=2。

如果一个都不输入,输出r的值就是-1,也就是EOF的值。

3

占位符

scanf()常用的占位符如下:

• %c :字符。

• %d :整数。

• %f : float 类型浮点数。

• %lf : double 类型浮点数。

• %Lf : long double 类型浮点数。

• %s :字符串。

• %[ ] :在方括号中指定⼀组匹配的字符(比如 %[0-9] ),遇到不在集合之中的字符,匹配将会 停⽌。

上⾯所有占位符之中,除了 %c 以外,都会自动忽略起首的空白字符。 %c 不忽略空白字符,总是返回当前第⼀个字符,无论该字符是否为空格。

如果要强制跳过字符前的空白字符,可以写成scanf(" %c", &ch),%前面加上一个空格,表示跳过0个或者多个空白字符。

需要特别强调的是%s

它的作用规则是

从当前第⼀个非空白字符开始读起,直到遇到空白字符(即空格、换⾏符、制表符等)为止。

即%s不会包含空白字符,所以无法用来读取多个单词,另外,scanf()遇到%s会在字符串变量末尾存储字符0。

scanf() 将字符串读⼊字符数组时,不会检测字符串是否超过了数组长度。所以,储存字符串时, 很可能会超过数组的边界,导致预想不到的结果。为了防⽌这种情况,使用%s 占位符时,应该指定读⼊字符串的最长长度,即写成%[m]s ,其中的 [m] 是⼀个整数,表示读取字符串的最大长度,后面的字符将被丢弃。

这样就没有数组溢出的风险了。

4

赋值忽略符

有时候输入的可能不符合格式。

如果用户输入2020/01/01,那么scanf()就会解析失败,为了避免,scanf()提供了赋值忽略符,只要把*加在任意占位符的%后面,该占位符就不会返回值。

如图

scanf()介绍结束。


感谢阅读!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐