iOS 内存大小分析

533 阅读4分钟

sizeof

sizeof是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。

定义:是C/C++的一个操作符。          过程:字节数的计算在程序编译时进行。
作用:判断数据类型长度符的关键字。     返回值:内存所占字节数,其返回值类型为size_t

补充:typedef unsigned int size_t;

NSLog(@"%zd  %zd  %zd", sizeof(short), sizeof(int), sizeof(void(*)));
// 输出 2  4  8

基本数据类型的sizeof

COC32位64位
boolBOOL(64位)11
signed char(__signed char)int8_t、BOOL(32位)11
unsigned charBoolean11
shortint16_t22
unsigned shortunichar22
int int32_tNSInteger(32位)、boolean_t(32位)44
unsigned intboolean_t(64位)、NSUInteger(32位)44
longNSInteger(64位)48
unsigned longNSUInteger(64位)48
Long long int64_t88
floatCGFloat(32位)44
doubleCGFloat(64位)88

用法

sizeof有两种语法形式,如下:
sizeof(type_name); // sizeof(类型);
sizeof object; // sizeof对象;

对象 | 对象类型大小计算

int i; 
// sizeof int; 错误用法
NSLog(@"%zd %zd %zd ", sizeof(i), (sizeof i), sizeof(int));
// 打印 4 4 4

sizeof计算对象的大小也是转换成对对象类型的计算,及同种类型的不同对象其sizeof值都是一致的。

函数调用求值

sizeof也可以对一个函数调用求值,其结果是函数返回类型的大小,函数并不会被调用。如下:

char foo() {
    return 'a';
}

size_t sizeT = sizeof(foo());
NSLog(@"%zd", sizeT);

// 打印:1

指针变量的sizeof

64位系统中指针变量的sizeof通常为832为为4。
指针变量的sizeof值与指针所指的对象没有任何关系,所有的指针变量所占内存大小相等。

数组的sizeof

 数组的sizeof值等于数组所占用的内存字节数
char a1[3] = "abc";
char a3[] = "abc";
int a2[3];
NSLog(@"%zd %zd %zd", sizeof(a1), sizeof(a2), sizeof(a3));
// 打印:3 12 4

# ---------------------------------------------------------
void foo3(char a3[3]) {
    int c3=sizeof(a3);
    NSLog(@"c3== %d", c3);
    // 8
}

void foo4(char a4[]) {
    int c4=sizeof(a4);
    NSLog(@"c4== %d", c4);
    // 8
}
// 根据引用类型,调用函数foo3时,会将实参的地址传递过去,则a3为指针类型(char*),所以c3,c4在64位系统中占用8个字节。
补充说明:
'\0':字符串结束符

1、字符数组并不要求它的最后一个字符为'\0',甚至可以不包含'\0'2、系统对字符串常量自动添加一个'\0',如:char a3[] = "abc"
3、对于没有指定长度的字符数组,系统不会在最后自动添加结束符'\0'。如:char str[] = {'a', 'b', 'c', 'd'}
4、对于指定了长度的字符数组(初始化字符个数小于字符数组长度),系统会在最后自动添加结束符'\0',如:char  string[4]={'a','b','c'};

结构体的sizeof

struct Struct1 {
    int a;
    char b;
   short c;
    double d;
}struct1;

struct Struct2 {
    double c;
    char b;
    int a;
    short d;
}struct2;

struct Struct3 {
    int a;
    char b;
    struct Struct1 struct1;
    short c;
    struct Struct2 struct2;
    double d;
}struct3;

NSLog(@"%zd %zd %zd", sizeof(struct1), sizeof(struct2), sizeof(struct3));
// 打印 16 24 64
struct Struct1 s1; 
s1.a = 2;
s1.b = 'a';
s1.c = 'c';
s1.d = 2.33;
// 断点
 分析:
 1、通过lldb命令控制台输出s1内存地址
 p &s1 -> (Struct1 *) $0 = 0x000000016b2f9b28
 x/4gx 0x000000016b2f9b28 -> 
 0x16b2f9b28: 0x0063006100000002 0x4002a3d70a3d70a4
 0x16b2f9b38: 0x0000000000000001 0x00006000020f00c0
 
 2、显示内存:工具栏 -> Debug -> Debug WorkFlow -> View Memory
 
 3、Address出输入内存地址:0x16b2f9b28,显示如下:
 
  02 00 00 0061 00 63 00 |A4 70 3D 0A D7 A3 02 40
  
  - 02 00 00 00:int类型a对应占用4个字节,不足以00补齐
  - 61char类型b占用1个字节
  - 63 00:short类型c占用2个字节,不足以00补齐
  - 63 00之前的00,是因为要满足成员相对结构体首地址的偏移量是成员大小的整数倍,及「c」相对于Struct1,偏移6个字节,所以前面补齐一个字节00。
  - A4 70 3D 0A D7 A3 02 40:double类型d占用8个字节。

  s2的同样流程分析:
  A4 70 3D 0A D7 A3 02 4061 00 00 00 02 00 00 00 64 00 00 00 00 00 00 00

字节对齐规则

 1、结构体变量的首地址能够被其最宽基本类型成员的大小整除;
 2、结构体每个成员相对结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
 3、结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后填充上字节。