Determining the Size of a Class Object

C++类大小
本文探讨了C++中类对象大小的决定因素,包括非静态数据成员的大小、成员变量的排列顺序、字节对齐、基类大小及虚函数的存在等。

Determining the Size of a Class Object

转自:http://cprogramming.com/tutorial/size_of_class_object.html

By Girish Shetty

There are many factors that decide the size of an object of a class in C++. These factors are:

  1. Size of all non-static data members
  2. Order of data members
  3. Byte alignment or byte padding
  4. Size of its immediate base class
  5. The existence of virtual function(s) (Dynamic polymorphism using virtual functions).
  6. Compiler being used
  7. Mode of inheritance (virtual inheritance)

Size of all non-static data members

Only non-static data members will be counted for calculating sizeof class/object.

class A { 
private: 
        float iMem1; 
        const int iMem2; 
        static int iMem3; 
        char iMem4; 
}; 

For an object of class A, the size will be the size of float iMem1 + size of int iMem2 + size of char iMem3. Static members are really not part of the class object. They won't be included in object's layout. <2>Order of data members

 

The order in which one specifies data members also alters the size of the class.

class C { 
        char c; 
        int int1; 
        int int2; 
        int i; 
        long l; 
        short s; 
}; 

The size of this class is 24 bytes. Even though char c will consume only 1 byte, 4 bytes will be allocated for it, and the remaining 3 bytes will be wasted (holes). This is because the next member is an int, which takes 4 bytes. If we don't go to the next (4th) byte for storing this integer member, the memory access/modify cycle for this integer will be 2 read cycles. So the compiler will do this for us, unless we specify some byte padding/packing.

If I re-write the above class in different order, keeping all my data members like below:

class C { 
        int int1; 
        int int2; 
        int i; 
        long l; 
        short s; 
        char c; 
}; 

Now the size of this class is 20 bytes.

In this case, it is storing c, the char, in one of the slots in the hole in the extra four bytes.

Byte alignment or byte padding

As mentioned above, if we specify 1 byte alignment, the size of the class above (class C) will be 19 in both cases.

Size of its immediate base class

The size of a class also includes size of its immediate base class.

Lets take an example:

Class B { 
... 
        int iMem1; 
        int iMem2; 
} 

Class D: public B { 
... 
        int iMem; 
} 

In this case, sizeof(D) is will also include the size of B. So it will be 12 bytes.

The existence of virtual function(s)

Existence of virtual function(s) will add 4 bytes of virtual table pointer in the class, which will be added to size of class. Again, in this case, if the base class of the class already has virtual function(s) either directly or through its base class, then this additional virtual function won't add anything to the size of the class. Virtual table pointer will be common across the class hierarchy. That is

class Base { 
public: 
... 
        virtual void SomeFunction(...); 
private: 
        int iAMem 
}; 

class Derived : public Base { 
... 
        virtual void SomeOtherFunction(...); 
private: 
        int iBMem 
}; 

In the example above, sizeof(Base) will be 8 bytes--that is sizeof(int iAMem) + sizeof(vptr). sizeof(Derived) will be 12 bytes, that is sizeof(int iBMem) + sizeof(Derived). Notice that the existence of virtual functions in class Derived won't add anything more. Now Derived will set the vptr to its own virtual function table.

Compiler being used

In some scenarios, the size of a class object can be compiler specific. Lets take one example:

class BaseClass { 
        int a; 
        char c; 
}; 

class DerivedClass : public BaseClass { 
        char d; 
        int i; 
}; 

If compiled with the Microsoft C++ compiler, the size of DerivedClass is 16 bytes. If compiled with gcc (either c++ or g++), size of DerivedClass is 12 bytes.

The reason for sizeof(DerivedClass) being 16 bytes in MC++ is that it starts each class with a 4 byte aligned address so that accessing the member of that class will be easy (again, the memory read/write cycle).

Mode of inheritance (virtual inheritance)

In C++, sometimes we have to use virtual inheritance for some reasons. (One classic example is the implementation of final class in C++.) When we use virtual inheritance, there will be the overhead of 4 bytes for a virtual base class pointer in that class.

class ABase{ 
        int iMem; 
}; 

class BBase : public virtual ABase { 
        int iMem; 
}; 

class CBase : public virtual ABase { 
        int iMem; 
}; 

class ABCDerived : public BBase, public CBase { 
        int iMem; 
}; 

And if you check the size of these classes, it will be:

  • Size of ABase : 4
  • Size of BBase : 12
  • Size of CBase : 12
  • Size of ABCDerived : 24

Because BBase and CBase are dervied from ABase virtually, they will also have an virtual base pointer. So, 4 bytes will be added to the size of the class (BBase and CBase). That is sizeof ABase + size of int + sizeof Virtual Base pointer.

Size of ABCDerived will be 24 (not 28 = sizeof (BBase + CBase + int member)) because it will maintain only one Virtual Base pointer (Same way of maintaining virtual table pointer).

OPTIONS = common.OPTIONS BLOCK_SIZE = common.BLOCK_SIZE FIXED_SALT = "aee087a5be3b982978c923f566a94613496b417f2af592639bc80d141e34dfe7" # From external/avb/avbtool.py MAX_VBMETA_SIZE = 64 * 1024 MAX_FOOTER_SIZE = 4096 class BuildVerityImageError(Exception): """An Exception raised during verity image building.""" def __init__(self, message): Exception.__init__(self, message) def CreateVerityImageBuilder(prop_dict): """Returns a verity image builder based on the given build properties. Args: prop_dict: A dict that contains the build properties. In particular, it will look for verity-related property values. Returns: A VerityImageBuilder instance for Verified Boot 1.0 or Verified Boot 2.0; or None if the given build doesn't support Verified Boot. """ partition_size = prop_dict.get("partition_size") # partition_size could be None at this point, if using dynamic partitions. if partition_size: partition_size = int(partition_size) # Verified Boot 2.0 if (prop_dict.get("avb_hash_enable") == "true" or prop_dict.get("avb_hashtree_enable") == "true"): # key_path and algorithm are only available when chain partition is used. key_path = prop_dict.get("avb_key_path") algorithm = prop_dict.get("avb_algorithm") # Image uses hash footer. if prop_dict.get("avb_hash_enable") == "true": return VerifiedBootVersion2VerityImageBuilder( prop_dict["partition_name"], partition_size, VerifiedBootVersion2VerityImageBuilder.AVB_HASH_FOOTER, prop_dict["avb_avbtool"], key_path, algorithm, prop_dict.get("avb_salt"), prop_dict["avb_add_hash_footer_args"]) # Image uses hashtree footer. return VerifiedBootVersion2VerityImageBuilder( prop_dict["partition_name"], partition_size, VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER, prop_dict["avb_avbtool"], key_path, algorithm, prop_dict.get("avb_salt"), prop_dict["avb_add_hashtree_footer_args"]) return None class VerityImageBuilder(object): """A builder that generates an image with verity metadata for Verified Boot. A VerityImageBuilder instance handles the works for building an image with verity metadata for supporting Android Verified Boot. This class defines the common interface between Verified Boot 1.0 and Verified Boot 2.0. A matching builder will be returned based on the given build properties. More info on the verity image generation can be found at the following link. https://source.android.com/security/verifiedboot/dm-verity#implementation """ def CalculateMaxImageSize(self, partition_size): """Calculates the filesystem image size for the given partition size.""" raise NotImplementedError def CalculateDynamicPartitionSize(self, image_size): """Calculates and sets the partition size for a dynamic partition.""" raise NotImplementedError def PadSparseImage(self, out_file): """Adds padding to the generated sparse image.""" raise NotImplementedError def Build(self, out_file): """Builds the verity image and writes it to the given file.""" raise NotImplementedError class VerifiedBootVersion2VerityImageBuilder(VerityImageBuilder): """A VerityImageBuilder for Verified Boot 2.0.""" AVB_HASH_FOOTER = 1 AVB_HASHTREE_FOOTER = 2 def __init__(self, partition_name, partition_size, footer_type, avbtool, key_path, algorithm, salt, signing_args): self.version = 2 self.partition_name = partition_name self.partition_size = partition_size self.footer_type = footer_type self.avbtool = avbtool self.algorithm = algorithm self.key_path = common.ResolveAVBSigningPathArgs(key_path) self.salt = salt self.signing_args = signing_args self.image_size = None def CalculateMinPartitionSize(self, image_size, size_calculator=None): """Calculates min partition size for a given image size. This is used when determining the partition size for a dynamic partition, which should be cover the given image size (for filesystem files) as well as the verity metadata size. Args: image_size: The size of the image in question. size_calculator: The function to calculate max image size for a given partition size. Returns: The minimum partition size required to accommodate the image size. """ if size_calculator is None: size_calculator = self.CalculateMaxImageSize # Use image size as partition size to approximate final partition size. image_ratio = size_calculator(image_size) / float(image_size) # Prepare a binary search for the optimal partition size. lo = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE - BLOCK_SIZE # Ensure lo is small enough: max_image_size should <= image_size. delta = BLOCK_SIZE max_image_size = size_calculator(lo) while max_image_size > image_size: image_ratio = max_image_size / float(lo) lo = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE - delta delta *= 2 max_image_size = size_calculator(lo) hi = lo + BLOCK_SIZE # Ensure hi is large enough: max_image_size should >= image_size. delta = BLOCK_SIZE max_image_size = size_calculator(hi) while max_image_size < image_size: image_ratio = max_image_size / float(hi) hi = int(image_size / image_ratio) // BLOCK_SIZE * BLOCK_SIZE + delta delta *= 2 max_image_size = size_calculator(hi) partition_size = hi # Start to binary search. while lo < hi: mid = ((lo + hi) // (2 * BLOCK_SIZE)) * BLOCK_SIZE max_image_size = size_calculator(mid) if max_image_size >= image_size: # if mid can accommodate image_size if mid < partition_size: # if a smaller partition size is found partition_size = mid hi = mid else: lo = mid + BLOCK_SIZE logger.info( "CalculateMinPartitionSize(%d): partition_size %d.", image_size, partition_size) return partition_size def CalculateDynamicPartitionSize(self, image_size): self.partition_size = self.CalculateMinPartitionSize(image_size) return self.partition_size def CalculateMaxImageSize(self, partition_size=None): """Calculates max image size for a given partition size. Args: partition_size: The partition size, which defaults to self.partition_size if unspecified. Returns: The maximum image size. Raises: BuildVerityImageError: On error or getting invalid image size. """ if partition_size is None: partition_size = self.partition_size assert partition_size > 0, \ "Invalid partition size: {}".format(partition_size) add_footer = ("add_hash_footer" if self.footer_type == self.AVB_HASH_FOOTER else "add_hashtree_footer") cmd = [self.avbtool, add_footer, "--partition_size", str(partition_size), "--calc_max_image_size"] cmd.extend(shlex.split(self.signing_args)) proc = common.Run(cmd) output, _ = proc.communicate() if proc.returncode != 0: raise BuildVerityImageError( "Failed to calculate max image size:\n{}".format(output)) image_size = int(output) if image_size <= 0: raise BuildVerityImageError( "Invalid max image size: {}".format(output)) self.image_size = image_size return image_size def PadSparseImage(self, out_file): # No-op as the padding is taken care of by avbtool. pass def Build(self, out_file): """Adds dm-verity hashtree and AVB metadata to an image. Args: out_file: Path to image to modify. """ add_footer = ("add_hash_footer" if self.footer_type == self.AVB_HASH_FOOTER else "add_hashtree_footer") cmd = [self.avbtool, add_footer, "--partition_size", str(self.partition_size), "--partition_name", self.partition_name, "--image", out_file] if self.key_path and self.algorithm: cmd.extend(["--key", self.key_path, "--algorithm", self.algorithm]) if self.salt: cmd.extend(["--salt", self.salt]) cmd.extend(shlex.split(self.signing_args)) proc = common.Run(cmd) output, _ = proc.communicate() if proc.returncode != 0: raise BuildVerityImageError("Failed to add AVB footer: {}".format(output)) def CreateCustomImageBuilder(info_dict, partition_name, partition_size, key_path, algorithm, signing_args): builder = None if info_dict.get("avb_enable") == "true": builder = VerifiedBootVersion2VerityImageBuilder( partition_name, partition_size, VerifiedBootVersion2VerityImageBuilder.AVB_HASHTREE_FOOTER, info_dict.get("avb_avbtool"), key_path, algorithm, # Salt is None because custom images have no fingerprint property to be # used as the salt. None, signing_args) return builder def GetDiskUsage(path): """Returns the number of bytes that "path" occupies on host. Args: path: The directory or file to calculate size on. Returns: The number of bytes based on a 1K block_size. """ cmd = ["du", "-b", "-k", "-s", path] output = common.RunAndCheckOutput(cmd, verbose=False) return int(output.split()[0]) * 1024 def CalculateVbmetaDigest(extracted_dir, avbtool): """Calculates the vbmeta digest of the images in the extracted target_file""" images_dir = common.MakeTempDir() for name in ("PREBUILT_IMAGES", "RADIO", "IMAGES"): path = os.path.join(extracted_dir, name) if not os.path.exists(path): continue # Create symlink for image files under PREBUILT_IMAGES, RADIO and IMAGES, # and put them into one directory. for filename in os.listdir(path): if not filename.endswith(".img"): continue symlink_path = os.path.join(images_dir, filename) # The files in latter directory overwrite the existing links common.RunAndCheckOutput( ['ln', '-sf', os.path.join(path, filename), symlink_path]) cmd = [avbtool, "calculate_vbmeta_digest", "--image", os.path.join(images_dir, 'vbmeta.img')] return common.RunAndCheckOutput(cmd) def main(argv): if len(argv) != 2: print(__doc__) sys.exit(1) common.InitLogging() dict_file = argv[0] out_file = argv[1] prop_dict = {} with open(dict_file, 'r') as f: for line in f: line = line.strip() if not line or line.startswith("#"): continue k, v = line.split("=", 1) prop_dict[k] = v builder = CreateVerityImageBuilder(prop_dict) if "partition_size" not in prop_dict: image_size = GetDiskUsage(out_file) # make sure that the image is big enough to hold vbmeta and footer image_size = image_size + (MAX_VBMETA_SIZE + MAX_FOOTER_SIZE) size = builder.CalculateDynamicPartitionSize(image_size) prop_dict["partition_size"] = size builder.Build(out_file) if __name__ == '__main__': try: main(sys.argv[1:]) finally: common.Cleanup()结合verity_utils.py分析一下哈希树是在怎么构建的
10-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值