精华内容
下载资源
问答
  • C语言学习实践摘要本文将从C语言变量的本质,不同类型变量在内存中的存储方式,类型强制转换,格式输出4个方面阐述C语言初学阶段的一些问题。关键词:内存存储,类型强制转换,反汇编1. 变量变量来源于数学,是...

    C语言学习实践

    摘要

    本文将从C语言变量的本质,不同类型变量在内存中的存储方式,类型强制转换,格式输出4个方面阐述C语言初学阶段的一些问题。

    关键词:内存存储,类型强制转换,反汇编

    1. 变量

    变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。在诸如C语言等高级语言中,变量的使用屏蔽了数据的底层细节,使得高级语言程序员不必像汇编程序员那样关心数据与硬件之间的关系。为了探究C语言中变量在内存中的存储形式,可以借助反汇编查看汇编语言以及内存数据。

    2. 变量在内存中的存储形式

    在内存中,无论哪种数据类型的数据,都是以相应长度的二进制码存取。从内存取数据是,如果不按照定义数据类型的方式取数据,所取数据就会错误。

    2.1   用反汇编查看变量内存数据

    (1)   实验代码如下。在赋值部分打点后调试,转入反汇编。

    0818b9ca8b590ca3270a3433284dd417.png

    0818b9ca8b590ca3270a3433284dd417.png

    (2)   在监视窗口查看变量的内存地址,并在内存窗口中查看数据。

    0818b9ca8b590ca3270a3433284dd417.png

    整形变量_4ByteData的数据在以内存地址0x0023FA58起始的四个Byte中存放:【注意】 Intel处理器是小端机,数据高位在高地址,地位在地址。所存数据:4Bytes的十六进制数 0x12345678

    0818b9ca8b590ca3270a3433284dd417.png

    单精度浮点型变量fl的数据在内存中的存储:

    0818b9ca8b590ca3270a3433284dd417.png

    双精度浮点型变量df的数据在内存中的存储:

    0818b9ca8b590ca3270a3433284dd417.png

    字符型变量ch的数据在内存中存储;

    0818b9ca8b590ca3270a3433284dd417.png

    结论:

    (1)   局部变量存储在函数栈中,且该栈向低地址生长,所以先定义的局部变

    量在较高内存地址(比如_4ByteData在0x0023FA58,ch在0x0023FA33),并且局部变量之间并非紧密排布,而是由8个Byte 的cc数据隔开。变量周围塞些CCCCCCCC,这可能是编译器提供的一种保护机制,越界了好出断言。通过网上查找资料,这是VC在Debug时给变量留出空间,用来检查stack overflow。

    用release调试,就不会有多余的cc。(但是注意这里char型变量与之前的局部变量之间仍有数据,我猜测这里是应为有变量对齐的缘故,char型变量也占了4byte,只不过多余的3byte由其他数据填充。)

    0818b9ca8b590ca3270a3433284dd417.png

    (2)   通过观察不同类型的变量在内存中的存储情况,可以发现:

    在32位机器,VS2010 IDE中,一个int型数据占4B,float型变量占4B,double型变量占8B,char型占1B。

    2.2    IEEE754 单精度数的格式

    单精度浮点数占据4个字节,4个字节的分配如下:

    (a)第一位为符号位,0表示正,1表示负;

    (b)第2~9位为阶码,采用移码表示;

    (c)第10~32位为尾数,采用原码表示。

    给定32位串,如何转换成十进制数:

    假设内存中存在32位串:00 00 00 3f,因为INTELCPU采用little endian存储方式,所以其真实的值为:

    0x 3f 00 00 00。将其写成二进制形式:

    (1)第一步,化为二进制

    0 01111110 0000000 00000000 00000000

    (2)第二步

    该浮点数为正数,阶码 01111110,移码表示(126-127) = -1

    尾数 0000000 00000000 00000000

    因为在IEEE754中,单精度浮点数有规格化处理,所以其真正尾数部分为

    1.0000000 00000000 00000000,其中‘.’为小数点

    (3)   第三步

    根据公式写出实际数值大小

    0.10000000 00000000 0000000 化为二进制:0.5

    2.3    IEEE754 双精度数的格式

    长实数也称双精度数符号位1位,阶码11位,尾数52位

    给定32位串,如何转换成十进制数:

    假设内存中存在64位串:00 00 00 0000 00 e0 3f,因为INTEL CPU采用littleendian存储方式,所以其真实的值为:       0x 3f e0 00 00 00 00 00 00。将其写成二进制形式:

    (1)第一步

    0 01111111110 0000 00000000 00000000 00000000 00000000 0000000000000000

    (2)第二步

    该浮点数为正数,阶码 01111111110,移码表示(1022-1023) = -1尾数 0

    因为在IEEE754中,单精度浮点数有规格化处理,所以其真正尾数部分为

    1.0,其中‘.’为小数点

    (3) 第三步

    根据公式写出实际数值大小

    0.10 化为二进制:0.5

    3. 格式化输出

    3.1 printf函数调用的一般形式

    printf函数是一个标准库函数,它的函数原型在头文件“stdio.h”中。但作为一个特例,不要求在使用 printf 函数之前必须包含stdio.h文件。printf函数调用的一般形式为:printf(“格式控制字符串”, 输出表列)。

    其中格式控制字符串用于指定输出格式。格式控制串可由格式字符串和非格式字符串两种组成。格式字符串是以%开头的字符串,在%后面跟有各种格式字符,以说明输出数据的类型、形式、长度、小数位数等。如:

    “%d”表示按十进制整型输出;

    “%ld”表示按十进制长整型输出;

    “%c”表示按字符型输出等

    非格式字符串原样输出,在显示中起提示作用。输出表列中给出了各个输出项,要求格式字符串和各输出项在数量和类型上应该一一对应。

    3.2 类型不对应下的格式输出

    _4ByteData = 0x12345678;  //Hexadecimal:0x12345678对应Decimal:305419896

    fl = 0.5;

    df = 0.5;

    ch = 65;

    printf("%d\n",_4ByteData);

    printf("%c\n",_4ByteData);

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

    printf("%f\n",&_4ByteData);

    printf("%d\n",fl);

    printf("%d\n",df);

    0818b9ca8b590ca3270a3433284dd417.png

    分析:

    可以看到同样是int型变量,printf("%d\n",_4ByteData);与

    printf("%c\n",_4ByteData);其结果分别为305419896(0x12345678对应的十进制数),而后者只取了4字节数据的最低一个字节0x78,所以打印出了AISII码0x78对应的字符’x’。

    联系C语言中指针的用法,我做出假设:格式输出函数printf()根据类型字符%以及变量名,就可以根据数据首地址+读取长度的方式输出数据。

    进一步发现:

    但是对字符型变量ch使用类型字符%d输出,得到的是其ASCII码的十进制数,如果按照上述假设,会输出以ch地址起始的4B的数据(这将是一个错误数据)。

    但实验结果是正确输出了ch字符的ACSII码的十进制数。

    再对浮点数做实验:

    用类型字符%d输出单精度数,双精度数,结果均为0。

    由上文2.1可知单精度浮点变量fl(十进制0.5)在内存中占4B,机器码是

    0x00 00 00 3f(小端机)。现对整数_4ByteData赋值0x3f000000,并使用类型字符%f对该整数输出,查看结果:

    0818b9ca8b590ca3270a3433284dd417.png

    0818b9ca8b590ca3270a3433284dd417.png

    结果仍是0。这说明即使内存中数据存储的内容一样,但是使用类型字符%f对整型变量输出,其结果仍然不是浮点数!

    综上,原假设值得怀疑!

    4. printf()函数类型不对应下的格式输出进一步研究

    4.1用%d输出float类型数据

    float fl=0.5;如果用printf("%d",fl);输出的是0。 但float型用%d输出是否一定是0呢,答案肯定不都是0(如下图)。

    0818b9ca8b590ca3270a3433284dd417.png

    为什么 0.5 用%d输出的是0?

    分析如下:

    首先来了解下printf的输出格式,int 和 long int 都是32位的,用%d输出;float 、double都是%f输出,但 float 是32位的,double 是64位的,所以在参数传递的时候C语言统一将 float 类型数值传换为double 类型再传入 printf 函数。如果是32位整型则输出格式为%lld。

    下面来讲一下  float fl=0.5f ;printf("%d",fl)输出为0的情况:

    %d只输出低32位的数据,并将这些32位二进制以十进制数输出,编译器首先将 0.5从float类型转换为double类型,0.5在内存中的存放方式是0x3f000000,转换成double类型在内存中的数据就是这个0x3fe0000000000000,这个内存数据可以很明显看出低32位全是0,而%d则只能截取到低32位,所以这个以%d输出0.5的数值当然是 0了。如大家不相信可以用%lld 输出看看,这个%lld就很读到低64位数据,读出的结果就是0x3fe0000000000000,在屏幕上看到一个很大的十进制数。(这里用%llx显示十六进制数更直观)

    0818b9ca8b590ca3270a3433284dd417.png

    如果我一定要输出0.5在内存中的存放方法怎么办呢?

    可以用printf("%d",*(int *)&fl);这里做了一下处理,不是直接把fl传进来,把fl所在地址里的内容处理了一下,不管fl是什么类型,只对地址进行操作,利用(int *)&lf,将fl所在地址中的内容0x3f000000直接当成 int 类型传给printf,int 的类型数据不会再转成double类型了,所以输出正常,这个只是针对浮点型数据只占低32位,如果输出64位还得用%lld格式控制输出。

    如果用printf("%d",(int)fl),输出行不行?

    这个强制类型转换只针对fl的数据类型进行转换,0.5转换 int 类型是0,而上面的*(int *)&a,是对内存中的实际存储数据进行操作,蔽开数据类型这一层面,只将这个数据0x3f000000直接转成int类型输出。而(int)fl,要先看fl的类型,C语言会根据所要数据类型,对内存存储的数据进行改变,以便可以用int类型正确解析内存数据。

    如果用printf("%d",(float)fl),输出什么,输出的是0,这个只是将fl的float类型还转成float类型,还是要自动转成doube类型,传给printf函数。

    为什么float非要转成double类型呢?

    因为printf格式控制浮点型输出只有%f,所以统一按doube类型输出,不像整型有32位的%d或%ld,64位的有%lld,这就将32位整型和64位整型用不同的格式控制分开了,而%f则没有,所以printf输出的浮点数其实是统一遍历了64位内存,如果float传入printf没有进行转换,那么printf输出高32位数据将不可预知,printf输出结果也就不正确了,因此传入printf的浮点数都会被编译器隐含转成double类型。

    4.2 int类型%f格式输出

    如果定义了inta=0x3f000000;用printf("%f",a)输出的结果是多少呢?

    答案是0,至少我们看的屏幕上显示的是0.000000,实际值可不是0啊,只是我们显示的精度只能有15位小数,而实际的数据可能很小很小,0.0000....000几百个0后会有几个有效数据。

    我们分析一下:

    首先C语言把a传进printf,因为a是整型,所以不会自动转成double型数据,直接将0x3f0000000传进printf,而%f寻的是64位内存,也就是把0x000000003f000000这个内存中的数据当成浮点型输出来,那浮点型的数据是多少呢,又是怎么存储的呢?

    64位浮点数的存放方式:

    63位                 62~52位                 51~0位

    1个符号位       11个阶数                 52个尾数

    从0x000000003f000000来看:

    00000000

    1)符号位是0,表示正

    2)阶数是0,用移码表示:0-1023 = -1023,

    用指数表示:1.#*2^-1023,‘#’是代表尾数。

    3)尾数就是,0x000003f000000

    4)浮点二进制表示

    1.000000000000000000000011 1111 000000000000000000000000*2^(-1023),2^-1023次方可想而知有多小!

    这就是为什么我们的int型数据用%f输出是0.000000的原因!

    如果把0.5的双精度数对应的十六进制数赋给long long类型变量,则可以输出正确的小数:

    0818b9ca8b590ca3270a3433284dd417.png

    5. 总结:

    通过以上实验,我验证了原假设基本正确:

    格式输出函数printf()根据类型字符%以及变量名,就可以根据数据首地址+读取长度的方式输出数据。

    但是,还要注意其中的一些细节:

    (1)用%d输出float类型数据时,在参数传递的时候C语言统一将 float 类型数值传换为 double 类型再传入 printf 函数。而%d只截取低32位数据,所以得到的数字不是相应浮点数的二进制码。

    (2)int类型%f格式输出,%f寻的是64位内存,所以输出的数据可能很小(比如2^-1023),那么结果是0.

    综上,无论什么类型的数据,都只是01二进制数据。只要清楚其内存存储机制,拿到数据首址+偏移量,就能正确操作该数据!

    最后,我想说C语言非常灵活,高级程序员也要熟悉汇编语言,会使用反汇编这把“手术刀”在底层剖析程序,会有更深刻的认识!

    展开全文
  • 读入三个整数,按每个整数占 8 个字符的宽度,右对齐输出它们。 输入格式 只有一行,包含三个int范围内的整数,整数之间以一个空格分开。 输出格式 只有一行,按照格式要求依次输出三个整数,之间以一个空格...

    为了能够更清晰整齐地看到输出结果,我们可以去控制输出的格式

    • 读入三个整数,按每个整数占 8 个字符的宽度,右对齐输出它们。

    输入格式
    只有一行,包含三个int范围内整数,整数之间以一个空格分开。

    输出格式
    只有一行,按照格式要求依次输出三个整数,之间以一个空格分开。

    代码如下:

    #include <stdio.h>
    
    int main (void){
        
        int a , b ,c ;
        
        scanf("%d%d%d",&a,&b,&c);
        
        printf("%8d %8d %8d",a,b,c);
        
        return 0;
    }
    

    别忘了按题目要求还需有 一个 空格 隔开字符

    展开全文
  • C语言printf对齐输出方式

    万次阅读 多人点赞 2017-02-07 10:36:10
    C语言中,常见的有两种方式可以实现其输出字符串或数字对齐输出 制表符\t 制表符\t输出的时候,会移动输出光标,实现对齐效果。因此可以在输出的对应位置,增加\t来实现对齐。但有个缺点:要求每行相同列输出占用...

    在编程调试时,通常将log信息输出到文件中,此时需要注意输出对齐方式
    C语言中,常见的有两种方式可以实现其输出字符串或数字对齐输出

    1. 制表符\t
      制表符\t输出的时候,会移动输出光标,实现对齐效果。因此可以在输出的对应位置,增加\t来实现对齐。但有个缺点:要求每行相同列输出占用空间差别不可以太大。(若输出的是数字信息,可以直接将log信息copy到excel表格中,能很好地统计数字数值信息)
    2. 加入占用宽度控制数字
      使用printf格式化输出时,每个控制字符可以写成%nC的形式,如%10d, %12f, %5c, %20s等等,其对应的是不足部分左侧补空格,实现右对齐效果;若要不足部分右侧补空格,只需要在宽度字符前加-符号即可,如%-12f,此时实现的是左对齐效果。
    展开全文
  • c语言编写对齐输出

    千次阅读 2019-02-21 16:55:18
    描述:读入三个整数,按每个整数占8个字符的宽度,右对齐输出它们。 输入:只有一行,包含三个整数,整数之间以一个空格分开。 输出:只有一行,按照格式要求依次... 分析:这里需要用到的是C语言printf对齐输出...
    1. 描述:读入三个整数,按每个整数占8个字符的宽度,右对齐输出它们。
    2. 输入:只有一行,包含三个整数,整数之间以一个空格分开。
    3. 输出:只有一行,按照格式要求依次输出三个整数,之间以一个空格分开。

         样例输入:

     123456789 0 -1

         样例输出:

      123456789        0       -1

         分析:这里需要用到的是C语言printf对齐输出方式;

         在编程调试时,通常将log信息输出到文件中,此时需要注意输出对齐方式,C语言中,常见的有两种方式可以实现其输出字符串或数字对齐输出

        (1)制表符\t
          制表符\t输出的时候,会移动输出光标,实现对齐效果。因此可以在输出的对应位置,增加\t来实现对齐。

          缺点:要求每行相同列输出占用空间差别不可以太大。(若输出的是数字信息,可以直接将log信息copy到excel表格中,能很好地统计数字数值信息)
        (2)加入占用宽度控制数字
          使用printf格式化输出时,每个控制字符可以写成%nC的形式,如%10d, %12f, %5c, %20s等等,其对应的是不足部分左侧补空格,实现右对齐效果;若要不足部分右侧补空格,只需要在宽度字符前加-符号即可,如%-12f,此时实现的是左对齐效果。
          原文:https://blog.csdn.net/chengzhilong94/article/details/54907141

    实现算法:
          

    #include <stdio.h>
    #include <stdlib.h>
    
    int main()
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);   //读入三个整数
        printf("%8d %8d %8d\n",a,b,c);  //将读入的三个整数,按每个整数占8个字符的宽度,右对齐输出它们。
    
        return 0;
    }
    

     

    展开全文
  • 假如这个字节数为 N,那么对齐的原则是:理论上所有成员在分配内存时都是紧接在前一个变量后面依次填充的,但是如果是“以 N 对齐”为原则,那么,如果一行中剩下的空间不足以填充某成员变量,即剩下的空间小于某...
  • C语言输出对齐

    2020-08-04 20:12:48
    对齐 printf("%d",1000); //默认 printf("%-10d",10001); //打印宽度为10,不够则输出空格 多余则继续输出对齐 printf("%3d",123); //打印宽度为3
  • <C语言>printf的对齐输出

    千次阅读 2017-08-26 15:31:42
    C语言中,常见的有两种方式可以实现其输出字符串或数字对齐输出 制表符\t  制表符\t输出的时候,会移动输出光标,实现对齐效果。因此可以在输出的对应位置,增加\t来实现对齐。但有个缺点:要求每行相同列输出...
  • 在编程调试时,通常将log信息输出到文件中,此时需要注意输出对齐方式C语言中,常见的有两种方式可以实现其输出字符串或数字对齐输出制表符\t制表符\t输出的时候,会移动输出光标,实现对齐效果。因此可以在输出的...
  • 读入三个整数,按每个整数占8个字符的宽度,右对齐输出它们,按照格式要求依次输出三个整数,之间以一个空格分开。 【输入】 只有一行,包含三个整数,整数之间以一个空格分开。 【输出】 只有一行,按照...
  • //对齐输出九九乘法口诀 #include #include //using namespace std; int main() { int i,j; for(i=1;i;i++) { for(j=1;j;j++) { cout*"(2)*j; } cout; } return 0
  • "*"的左对齐输出,i控制行的输出,j控制列的输出 #include <stdio.h> int main() { int i,j; for(i=1;i<12;i++) { for(j=0;j<i;j++) { printf("*"); } printf("\n"); } return 0; } "*"的右...
  • c语言:制表符/t对齐输出

    千次阅读 2020-04-10 13:53:53
    看代码和结果就懂了: int main() { int a=1; int b=12; printf("%d\t%d\n",a,b); printf("%d\t%d\n",b,a); } 结果:
  • C语言输出对齐问题

    2015-12-06 19:00:23
    比如我要输出 12 123 12345 324 11234 2345 如何使每排都对齐
  • cout << setw(15) << std::right << "tbc" << " " << setw(15) << std::right << "ef" << " " << setw(15) << std::right...
  • 1102: 整数幂时间限制:1 Sec内存限制:128 MB提交...输入输入3整数,用空格隔开输出输出3行,每行3个整数,分别是它们的1次幂、2次幂和3次幂,每个整数占9列,不足9列左对齐样例输入1 5 100样例输出1 1 15 25 125100 ...
  • C语言输出对齐对齐,补位

    万次阅读 多人点赞 2018-10-07 16:13:31
    %d 十进制有符号整数  %u 十进制无符号整数  %f 浮点数  %s 字符串  %c 单个字符  %p 指针的值  %e 指数形式的浮点数  ...1.左对齐 方式1 printf("%d\n",101010); 默认打...
  • c语言printf格式输出小细节回顾 1,printf函数的格式字符 格式字符 说明 d 以带符号的十进制形式输出 o 以无符号的八进制形式输出 X,x 以无符号的十六进制形式输出 u 以无符号的十进制形式输出 c ...
  • C语言 printf 格式化 输出对齐补零

    千次阅读 2018-07-16 09:58:01
    C语言IO--printf右对齐补零关于printf的一点总结,内容来自http://blog.sina.com.cn/s/blog_5ac88b350100auna.html1.1 标准输入输出函数1.1.1 格式化输入输出函数 Turbo C2.0 标准库提供了两个控制台格式化输入、 ...
  • 由于你并没有指定每个int值输出的长度,因此int值有多长就会输出多长,此时没有讨论左对齐或者右对齐的意义。   你只有加上%10d的时候(10只是我举的例子),这时候限定了int值输出长度为10,而12345这个int值的...
  • C语言对齐

    2013-08-25 18:59:00
    "%-md":左对齐,若m比实际少时,按实际输出。(m为整数) "%md":右对齐,若m比实际少时,按实际输出。 我自己编了一个,给你看看实际效果: #include<stdio.h> #defineN5 intmain() { inti,j,a[N][N]; ...
  • C语言中主要通过两个库函数,scanf,printf实现文字的输入输出。一:格式输出函数printf()1、调用形式一般为:printf("格式化控制字符串",输出表列);2、格式化控制字符串用于指定输出格式,它有三种形式:1、格式...
  • C语言字节对齐

    万次阅读 多人点赞 2011-08-29 16:06:57
    文章最后本人做了一幅图,一看就... 一、概念 对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好位于它长度的整数倍,他就被称做自然对齐。比如在32位cpu下,假设一个整型变量的地址为0x00000004,那它就是
  • C语言怎么用printf输出字符串printf函数的一般格式printf(格式控制字符串,输出表列);以图1所示为例。图1.函数参数包括两部分:【1】格式控制字符串,是用双引号括起来的字符串,也称为转换控制字符串,它指定输出...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,216
精华内容 12,886
关键字:

对齐输出c语言

c语言 订阅