欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

程序员文章站 2022-03-23 16:54:50
...

所有代码测试都是在vs2019环境下测试,不同平台可能会有所差异,取决于平台,不影响理解。

格式输入与输出

格式输出

printf 函数称为格式输出函数,其关键字最后一个字母 f 即为“格式”(format)之意。其功能是按用户指定的格式,把指定的数据显示到显示器屏幕上。
我们给出标准格式;
printf(“格式控制字符串”,[输出列表]);
其中格式控制字符串用于指定输出格式。格式控制串可由格式字符串和非格式字符串两种组成。格式字符串是以%开头的字符串,在%后面跟有各种格式字符,以说明输出数据的类型、形式、长度、小数位数等。
在这里格式转换符控制要求跟输出是一一对应的,其他的非格式字符原样输出。
我们用代码进行解释:

 printf("this  number is  %d", 5);

%d是格式转换符 this number is 是非格式字符串。
所以在输出的时候结果为 :
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

printf("number1 is % d + number2 is % d == number3 % d", 4, 3, 4 + 3);

%d是格式转换符 其他都是 是非格式字符串原样输出。前面格式控制符一共有三个%d格式转换符,所以输出列表用4,3,4+3分别一一对应。
所以在输出的时候结果为 :
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
接下来我们介绍格式串:
形式: “% [标志][输出最小宽度][.精度][长度]类型”
强调一点这里所有的[ ]里面的内容都不会显示,我们只是实现[ ]里面的具体功能对于格式字符的影响,在使用中[ ] 中的限定可以根据需要选择是否存在
首先我们先介绍[ ] 之外的就是类型我们可以看出,类型是必须要有的,因为它不在[ ] 里面 并且按照实际情况[ ] 里面的内容只是修饰类型,所以类型的存在是必要的。我们给出C语言用到的所有类型:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
上面我们已经给出了例子,大家可以进行自行操作检验所有类型,字符串和地址现在不理解的话,可以不检测,之后在学习和用到的时候会详细讲解。
在这里,我们对于浮点类型数据再加入一点说明:

float data1 = 1234.56789;  
	printf("%f\n", data1);	

结果为:格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
float类型精度为7 所以输出的12345,67是精度部分,正确输出,
后面数字是因为要求输出小数点后6位,所以只是凑够小数点后6位 不在浮点数精度里面包括,只是按照要求输出小数点后6位。
首先我们讲解宽度:说明一点 精度只是对于效数有意义

printf("123456789123456789\n");//这里出现的数字只是方便我们看出宽度
	printf("%d\n", 10);    //我们在这里介绍所占宽度  打印10占2个宽度
	printf("%5d\n", 10);    //这里我们给出宽度为5  
printf("%5.4\n", 10.123);  //这里我们给出宽度为5精度为6,打印小数点后四位。
//以上内容希望读者一定要根据理解清楚!!!

//接下来我们引入精度的概念 首先说明一点 精度只是对于效数有意义

printf("%10.4f\n", 12.3456789); //这里精度为4 宽度为10
printf("%2d\n", 12345);   //当实际宽度大于所给的宽度的时候按照实际输出

输出结果为:(解释在代码注释)
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

标志

我们首先给出常用的标志和对应的意义:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

int data = 0x55;       //我们定义一个整型变量 data  赋值为十六进制 55
	printf("%#x\n", data); //分别以十六进制,八进制,十进制打印输出
	printf("%#o\n", data); //加#会直接显示会以那种进制打印输出
	printf("%#d\n", data); //%d   可以不用加#  因为默认是十进制打印
	printf(" %#x\t%#o\t%#d\n", data, data, data);

输出结果为:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
以上结果详细说明在代码和注释中都已经说明

printf("%5d\n", 10);   //宽度为5
	printf("%5f\n", 10.02);   //宽度为5 小数点后显示6位
	printf("%5.2f\n", 10.02);   //宽度为5
	printf("%-5d****\n", 10);  //-标志意义为左对齐,但是宽度依然存在

所以输出结果为:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
宽度仍然是5 只不过是左对齐 所以以上才会出现第四行结果.

长度

长度这里我们只是做一个做出说明:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
详细的内使用在【C】 [ b ] 里面已经使用和体现过。

格式输入

格式输入:格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
其中,格式控制字符串的作用与 printf 函数相同,地址表列中给出各变量的地址。 地址是由地址运算符“&”后跟变量名组成的。 例如:&a、&b 分别表示变量 a 和变量 b 的地址。 这个地址就是编译系统在内存中给 a、b 变量分配的地址。在 C 语言中,使用了 地址这个概念,这是与其它语言不同的。 应该把变量的值和变量的地址这两个不同 的概念区别开来。变量的地址是 C 编译系统分配的,我们不必关心具体的地址是多少。
//scanf 里面有一个天然的间隔 空格 tab Enter Enter结束
//格式串和被输入的变量一一对应,非格式串需要原样输入
格式字符串的一般形式为: %[*][输入数据宽度][长度]类型 其中有方括号[]的项为限定类型项。和printf中的作用一样。但是我们在输入的时候不使用数据宽度和长度的限制,使用时也会出现混乱现象,并且在实际的使用过程中也很少用到。

所以我们在这里只介绍字符格式的用法及意义:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

char a = 'a';
	char b = 'b';
	//scanf("%c%c",&a,&b); 
	//以上这种写法在vs2019上面程序会崩溃,但是其他平台例如Qt可以运行
	scanf_s("%c",&a);
	scanf_s("%c",&b);
	printf("%c%c\n",a,b);

输出结果为:格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
我们在这里给出这样的程序:

int a, b;
	scanf("!!%d!!%d", &a, &b);
	printf("%d", a + b);

格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
以上第一行是输入,第二行是输出结果。
我们在特别强调:
所有的非格式字符必须原样输入才能保证最终输入的正确性从而保证输出的重要性!!!。

这个
我们给出一些注意事项:
1,待输入变量,必是以其地址的方式呈现。
2,除格式字符,空格,回车,Tab 外,其它需要原样输入。
3,空格 ,Tab, 回车,均可作为输入间隔,以回车作为结束。
4,在输入字符数据时,若格式控制串中无非格式字符,则认为所有输入的字符均 为有效字符。(%d %c %c %c)
5,如输入的数据与输出的类型不一致时,虽然编译能够通过,但结果将不正确。

//例如



	float fData;
	scanf_s("%f", &fData);
	printf("%f\n", fData);
	printf("%d\n", fData);
	
	//强调输入输出格式的匹配******************

输出结果为:格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

int fData;
	scanf_s("%d", &fData);
	printf("%d\n", fData); 
	printf("%f\n", fData);

输出结果为:格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

类型转换

隐式转化:

隐式转换规则:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)不需要人为参与而产生的默认转称为隐式转化。隐式转化,是任何语言层面最复杂 的东西。当下,我们需要了解的只是一些规则,不考虑溢出,截断和有无符号。后期的会有更为详细的介绍。

算数转换:

char short int 等类型在一起运算时,首先提升到 int,这种现象叫作整型提升。整 型提升的原更换是符号扩充。
例如: char + char / char + char / char + int /short + int
我们用代码加以解释:

char ch = 1;
	short sh = 1;
	int in = 1;
	printf("sizeof(ch) = %d\n", sizeof(ch));             //不涉及计算  ch是一个字节
	printf("sizeof(ch + ch) = %d\n", sizeof(ch + ch));   //涉及计算 ch为4个字节
	//所以体现出来   char类型在参与计算的时候提升到int类型
	printf("sizeof(sh) = %d\n", sizeof(sh));             //不涉及计算  ch是一个字节
	printf("sizeof(sh + ch) = %d\n", sizeof(sh + ch));   //涉及计算 ch为4个字节
	printf("sizeof(in) = %d\n", sizeof(in));             //不涉及计算  ch是一个字节
	printf("sizeof(in + ch) = %d\n", sizeof(in + ch));   //涉及计算 ch为4个字节

输出结果为:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
我们给出两两不同类型在计算的时候的数据类型所占字节的大小,就可以得到在计算的时候转化之后的类型。(及其重要的理解,思考方法)

整型提升:


	//涉及符号扩充     小空间到大空间由于符号扩充的行为
	//保持符号不变   小数据赋值给大变量  符号不丢失
	//大数据赋值给小变量   有可能会溢出导致数据丢失
	char ch ;
	int in;
	ch = -1;
	in = ch;
	  

	printf("ch = %d\n", ch);
	printf("in = %d\n", in);
	//涉及符号扩充

涉及符号扩充 小空间到大空间由于符号扩充的行为
保持符号不变 小数据赋值给大变量 符号不丢失
大数据赋值给小变量 有可能会溢出导致数据丢失

输出结果为:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

char ch;
	int in;
	ch = 127;
		in = ch;


	printf("ch = %d\n", ch);
	printf("in = %d\n", in);

输出结果为:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
以上结果我们给出图解:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
我们在这里主要注意有符号和无符号的整型数据在类型转换时候的区别,上图红色部分表示数字-1从char类型进入到int类型的过程,黑色部分表示数字127从char类型进入到int类型的过程,我们在char类型里面用物理存储方式保存之后,只需要在int类型里面保证符号为不改变。以上给出的分析图需要读者深刻理解,在这里再强调,存储数据的时候是数据的补码存储,而符号位是通过原来数字的补码的第一位来判断,例如char类型的127物理存储第一位是0 那么进入int类型存储的过程中首位符号位也必须是0来保证char类型进入到int类型之后的符号为不变,-1同理也要保证符号位不变。

混合提升

前面我们介绍的都是整型的类型提升,那么如果int float double longlong
我们首先给出不同类型所占的大小,然后用一种巧妙的方式得出以上类型在计算的时候类型的提升转换。
不同的大小字节是否一定是小字节转化为大字节呢
// float 和 int 计算 是转化为int类型 还是 float类型
//int 和 float 类型 计算 向 float类型转(我们已经验证)
//int 向 double转 float类型向double类型转 (常规)
//我们经过验证 float 类型和 long long类型进行计算的时候 long long 类型会转换为float类型
//long long类型和 double类型进行计算的时候 long long 类型会转换为double类型
以下给出代码并且进行解释:

printf("%d\n", sizeof(long long));
	printf("%d\n", sizeof(double));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof(float));
	float f = 3.4;
	long long i = 5;
	//类型不匹配的时候输出是错误的  *********    方法
	printf("f + i = %d\n", f + i);
	printf("f + i = %f\n", f + i);

输出结果为:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
由于float +long long 的3.4 和 5 最终结果以%f格式打印出来正确结果,所以float类型和long long类型进行计算的时候转换为float类型。
我们每一个都使用这种方法,只是修改类型,我们用参与运算的两种类型分别输出,如果类型匹配输出正确,那么就是转化之后提升的类型,输入不正确,所输出的格式类型就是两种格式在计算的时候转换提升的类型。
所有的类型都可以使用这种方法进行检验。我们在前面已经给出了结论,其他的类型读者可以自行检验。

赋值转换

int in = 10;     //整数向小数赋值   加小数点
	float fl = 3.14;//小数向整数赋值去除小数  整数部分赋值
	fl = in;
	printf("%f", fl);

输出结果为:
格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
整数向小数赋值 加小数点

int in = 10;     //整数向小数赋值   加小数点
	float fl = 3.14;//小数向整数赋值去除小数  整数部分赋值
	in = fl;
	printf("%d", in);

输出结果为:格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)
小数向整数赋值去除小数 整数部分赋值

强制转换

	int a = 10;
	int b = 3;
	printf("%d\n", a / b);
	printf("%f\n",(float)a / b);
	printf("%f",10 / 3.0);
	return 0;
}

输出结果为:格式输入输出及各种类型转化(隐式转换、整型提升,混合提升,赋值转换,强制转换)【C】(c)

大多数情况下能够去使用强制类型转化的原因是程序设计的不够好,有缺陷的一种补救方法

最后我们说明一点小问题:本篇文章在使用scanf函数时使用了scanf_s 其实两个是一样的,只不过vs2019改进了scanf的一些安全问题,这种写法来自与编译器,读者在编写代码的时候只需要使用scanf即可,效果完全相同,如果读者也使用的是vs2019并且也想要使用scanf而不是用scanf_s需要宏定义:

#define _CRT_SRCURE_NO_WARNINGS就可以解决。

在前面我们用大量时间和内容说明一些底层知识是很有必要的,这对于我们之后的整个学习过程和理解都有很大的作用,对于我们理解数据类型和操作,在真正认识类型的基础上去使用和操作,才能把类型用的更加准确和减少我们的一些错误的发生,所以我认为是很有必要的。