zoukankan      html  css  js  c++  java
  • c++ | size_t

     

    1.size_t的定义

    size_t是一种数据相关的无符号类型,它被设计得足够大以便能够内存中任意对象的大小。

    在cstddef头文件中定义了size_t类型,这个文件是C标准库stddef.h头文件的C++版本。

    关于百度百科:

    size_t概述: size_t 类型定义在cstddef头文件中,该文件是C标准库的头文件stddef.h的C++版。它是一个与机器相关的unsigned整型类型,其大小足以保证存储内存中对象的大小。

    size_t由来: 在C++中,设计 size_t 就是为了适应多个平台的 。size_t的引入增强了程序在不同平台上的可移植性。

    在需要通过数组下标来访问数组时,通常建议将下标定义size_t类型,因为一般来说在进行下标访问时,下标都是正的。当然,也不是所有的下标访问操作下标都是正的,比如以下代码:

    int a[10]={1,2,3,4,5,6,7,8,9,10};
    
    int * ptr =&(a[4]);
    
    cout<<ptr[-2]<<endl;
    

      

      这段代码中对ptr进行了下标访问,并且下标是负的,但是这个访问并不是错误的,执行的结果是输出3。


    2.size_t占用的真实空间
    size_t大小: 关于size_t占用的空间百度百科的描述是:经测试发现,在32位系统中size_t是4字节的,而在64位系统中,size_t是8字节的,这样利用该类型可以增强程序的可移植性。

    疑问百度百科和网上猿友的描述看似很有道理,但是很多人在测试的时候发现,为什么测试环境明明是64位的系统,sizeof(size_t)的值却等于4呢?而不是原本预期的8。

    本机环境是Win7 64bits,使用VS2012来验证。

    本机系统类型: 


    测试代码:

    cout<<"sizeof(size_t)="<<sizeof(size_t)<<endl;
    1
    输出结果: 


    疑问解答: 为什么会这样,我之前一直也弄不明白。原来网上说的size_t的大小由系统的位数决定是不准确的。那size_t的大小究竟是由什么决定的呢?

    先看一下我刚刚测试代码的VS2012的编译配置。配置如下:

     
    红色框中的Win32表示的是什么意思呢?原来Win32表示的是生成的程序是32bits。32bits的程序既可以在Windows 32bits的系统下运行,也可以在Windows 64bits的系统下运行。所以,我们配置生成的程序是32bits的,因此size_t就是unsigned int 类型,即大小为4个字节。

    VC++中关于size_t类型的定义如下:

    #ifdef _WIN64
    typedef unsigned __int64 size_t;
    #else
    typedef _W64 unsigned int size_t;
    #endif
    1
    2
    3
    4
    5
    其大概的意思就是size_t要么是unsigned int,要么是unsigned long int,那么按照上面的推理,修改编译选项为x64,生成64bits的程序,size_t的类型是不是就变成了unsigned long int了呢?验证如下:

    VS2012的编译配置更改如下: 


    同样的测试代码:

    cout<<"sizeof(size_t)="<<sizeof(size_t)<<endl;
    1
    输出结果为: 
     
    正如预期的一样,size_t变成了unsigned long int ,占用8字节的内存空间。

    总结:size_t的大小并非像很多网上描述的那样,其大小是由系统的位数决定的。size_t的大小是由你生成的程序类型决定的,只是生成的程序类型与系统的类型有一定关系。32bits的程序既可以在64bits的系统上运行,也可以在32bits的系统上运行。但是64bits的程序只能在64bits的系统上运行。然而我们编译的程序一般是32bits的,因此size_t的大小也就变成了4个字节。

    由此引发指针的大小的问题?

    关于指针的大小,网上描述基本上是千篇一律,认为指针是存放地址的,如果是32位机器就是4字节的,如果是64位机器就是8字节的,根据机器字而决定的。

    这里的32位机器和64位机器指的是什么呢?我觉的CPU的架构决定了机器的类型,如果CPU是x86架构,那么就是32位的CPU,当然并非所有的x86架构的CPU都是32位的,比如intel的8086和8088就是16位的CPU。

    如果CPU是x86-64的架构,那么就是64位的CPU。CPU的位数是由其字长决定,字长表示CPU在同一时间中能够处理二进制数的位数叫字长。字长是由CPU中寄存器的位数决定的,并非由数据总线的宽度决定的,只是数据总线的宽度一般与CPU的位数相一致。

    系统的位数是依赖于CPU的位数,即32位的CPU不能装64位的系统,但是现在(2015年)的CPU基本上都是x86-64的CPU,都支持64位的系统。但是正如上面的讨论,如果编译生成的程序不是64位的,那么指针的大小依然是4个字节。

    验证如下:

    VS2012的编译配置如下: 
     
    测试代码:

    cout<<"sizeof(char*)="<<sizeof(char*)<<endl;
    1
    输出结果: 


    更改编译配置,生成64位的程序,我们将得到预想的结果: 


    3.size_t的作用
    某些情况下正确的使用size_t可以提高程序的可移植性和有效性。
    4.为什么size_t重要?
    为什么size_t重要?(Why size_t matters)
    10 September 2014


         
      之前在《内存拷贝的注意事项》一文中提到过size_t,可能许多人对这个类型不太熟悉没有用过或者根本不敢去用,最近看到一篇文章对这个类型讲的比较详细,便翻译过来让不熟悉的同学可以知道它产生的原因以及如何使用。
      原文地址: Why size_t matters

      前言:使用size_t可能会提高代码的可移植性、有效性或者可读性,或许同时提高这三者。

      在标准C库中的许多函数使用的参数或者返回值都是表示的用字节表示的对象大小,比如说malloc(n) 函数的参数n指明了需要申请的空间大小,还有memcpy(s1, s2, n)的最后一个参数,表明需要复制的内存大小,strlen(s)函数的返回值表明了以''结尾的字符串的长度(不包括''),其返回值并不是该字符串的实际长度,因为要去掉''。
      或许你会认为这些参数或者返回值应该被申明为int类型(或者long或者unsigned),但是事实上并不是。C标准中将他们定义为size_t。标准中记载malloc的申明应该出现在,定义为:

    void *malloc(size_t n);
      memcpy和strlen的申明应该出现在中:

    void *memcpy(void *s1, void const *s2, size_t n);
    size_t strlen(char const *s);
      size_t还经常出现在C++标准库中,此外,C++库中经常会使用一个相似的类型size_type,用的可能比size_t还要多。
      据我所知,大部分的C和C++程序员害怕这些库使用size_t,因为他们不知道size_t代表什么或者为什么这些库需要使用它,归根结底,原因在于他们什么时候什么地方需要用到它。

    可移植性问题
      早期的C语言(由Brian Kernighan 和 Dennis Ritchie 在The C Programming Language书中所写,Prentice-Hall, 1978)并没有提供size_t类型,C标准委员会为了解决移植性问题将size_t引入,举例如下:
      让我们来写一个可移植的标准memcpy函数,我们将会看到一些不同的申明和它们在不同平台不同大小的地址空间上编译下的情况。
      回忆memcpy(s1, s2, n)函数,它将s2指向地址开始的n个字节拷贝到s2指向的地址,返回s1,这个函数可以拷贝任何数据类型,所以参数和返回值的类型应该为可以指向任何类型的void*,同时,源地址不应该被改变,所以第二个参数s2类型应该为const void*,这些都不是问题。
      真正的问题在于我们如何申明第三个参数,它代表了源对象的大小,我相信大部分程序员都会选择int:

    void *memcpy(void *s1, void const *s2, int n);
      使用int类型在大部分情况下都是可以的,但是并不是所有情况下都可以。int是有符号的,它可以表示负数,但是,大小不可能是复数。所以我们可以使用unsigned int代替它让第三个参数表示的范围更大。
      在大部分机器上,unsigned int的最大值要比int的最大值大两倍,比如说再也给16位的机器上,unsigned int的最大值为65535,int的最大值为32767。
      尽管int类型的大小依赖于C编译器的实现,但是在给定的平台上int对象的大小和unsigned int对象的大小是一样的。因此,使用unsigned int修饰第三个参数的代价与int是相同的:

    void *memcpy(void *s1, void const *s2, unsigned int n);
      这样似乎没有问题了,unsigned int可以表示最大类型的对象大小了,这种情况只有在整形和指针类型具有相同大小的情况下,比如说在IP16中,整形和指针都占2个字节(16位),而在IP32上面,整形和指针都占4个字节(32位)。(参见下面C数据模型表示法)

    C数据模型表示法
      最近,我偶然发现几篇文章,他们使用简明的标记来表述不同目标平台下c语言数据的实现。我还没有找到这个标记的来源,正式的语法,甚至连名字都没有,但他似乎很简单,即使没有正规的定义也可以很容易使用起来。这些标记的一边形式形如:
      I nI L nL LL nLL P nP。
      其中每个大写字母(或成对出现)代表一个C的数据类型,每一个对应的n是这个类型包含的位数。I代表int,L代表long,LL代表long long,以及P代表指针(指向数据,而不是函数)。每个字母和数字都是可选的。
      例如,I16P32架构支持16位int和32位指针类型,没有指明是否支持long或者long long。如果两个连续的类型具有相同的大小,通常省略第一个数字。例如,你可以将I16L32P32写为I16LP32,这是一个支持16位int,32位long,和32位指针的架构。
      标记通常把字母分类在一起,所以可以按照其对应的数字升序排列。例如,IL32LL64P32表示支持32位int,32位long,64位long long和32位指针的架构;然而,通常写作ILP32LL64。
      不幸的是,这种memcpy的申明在I16LP32架构上(整形是16-bit 长整形和指针类型时32-bits)显得不够用了,比如说摩托罗拉第一代处理器68000,在这种情况下,处理器可能拷贝的数据大于65535个字节,但是这个函数第三个参数n不能处理这么大的数据。
      什么?你说很容易就可以改正?只需要把memcpy的第三个参数的类型修改一下:

    void *memcpy(void *s1, void const *s2, unsigned long n);
      你可以在I16LP32目标架构上使用这个函数了,它可以处理更大的数据。而且在IP16和IP32平台上效果也还行,说明它确实给出了memcpy的一种移植性较好的申明。但是,在IP16平台上相比于使用unsigned int,你使用unsigned long可能会使你的代码运行效率大打折扣(代码量变大而且运行变慢)。
      在标准C中规定,长整形(无论无符号或者有符号)至少占用32位,因此在IP16平台上支持标准C的话,那么它一定是IP16L32 平台。这些平台通常使用一对16位的字来实现32位的长整形。在这种情况下,移动一个长整形需要两条机器指令,每条移动一个16位的块。事实上,这个平台上的大部分的32位操作都需要至上两条指令。
      因此,以可移植性为名将memcpy的第三个参数申明为unsigned long而降低某些平台的性能是我们所不希望看到的。使用size_t可以有效避免这种情况。
      size_t类型是一个类型定义,通常将一些无符号的整形定义为size_t,比如说unsigned int或者unsigned long,甚至unsigned long long。每一个标准C实现应该选择足够大的无符号整形来代表该平台上最大可能出现的对象大小。

    使用size_t   转自http://jeremybai.github.io/blog/2014/09/10/size-t/
      size_t的定义在<stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h>和<wchar.h>这些标准C头文件中,也出现在相应的C++头文件, 等等中,你应该在你的头文件中至少包含一个这样的头文件在使用size_t之前。
      包含以上任何C头文件(由C或C++编译的程序)表明将size_t作为全局关键字。包含以上任何C++头文件(当你只能在C++中做某种操作时)表明将size_t作为std命名空间的成员。   根据定义,size_t是sizeof关键字(注:sizeof是关键字,并非运算符)运算结果的类型。所以,应当通过适当的方式声明n来完成赋值:

    n = sizeof(thing);
      考虑到可移植性和程序效率,n应该被申明为size_t类型。类似的,下面的foo函数的参数也应当被申明为sizeof:

    foo(sizeof(thing));
      参数中带有size_t的函数通常会含有局部变量用来对数组的大小或者索引进行计算,在这种情况下,size_t是个不错的选择。
      适当地使用size_t还会使你的代码变得如同自带文档。当你看到一个对象声明为size_t类型,你马上就知道它代表字节大小或数组索引,而不是错误代码或者是一个普通的算术值。
    ---------------------
    作者:PzLu
    来源:CSDN
    原文:https://blog.csdn.net/Richard__Ting/article/details/79433814
    版权声明:本文为博主原创文章,转载请附上博文链接!

  • 相关阅读:
    使用npm安装包失败的解决办法(使用npm国内镜像介绍)
    JavaScript的变量、作用域和内存问题
    JavaScript的基本概念
    在Html中使用JavaScript
    JavaScript简介
    C++为了兼容,所以并不是纯面向对象编程语言
    C++四种不同的对象生存方式
    Java BigDecimal使用
    ext 对齐
    ext grid 子表格
  • 原文地址:https://www.cnblogs.com/shona/p/11289643.html
Copyright © 2011-2022 走看看