sizeof操作符

了解sizeof

sizeof是一元操作符

sizeof是编译时的一元操作符, 返回任意对象所属类型的大小(单位:字节). 值的类型是size_t, 表示无符号整数, 它是个宏, 可以定义为unsigned int或unsigned long.

形式如下:

sizeof(type name);
sizeof(expr);
sizeof expr;

sizeof中的圆括号不是必须的, 但是一元操作符的优先级高于二元操作符, 表达式sizeof a + b会被编译器误认为sizeof(a) + b. 所以圆括号有时候是必须的.

编译时决定的常量

它的值在编译时确定.

例外情况是: 如果操作数是可变长度的数组类型(c99)

sizeof不计算expr

sizeof(expr)并不计算expr, 而是确定expr的类型, 获取该类型的大小. 所以sizeof(a=b+1)不会改变a的值, 它的结果是a的类型的大小.

struct Stu {
    char stuno[10];
    char name[32];
};

int main() {   
    printf("%lu\n", (unsigned long)sizeof((struct Stu*)0)->stuno);  /* print 10 */

    int i = 2;
    printf("%lu\n", (unsigned long)sizeof(++i)); /* print 4 */
    printf("%d\n", i);                           /* print 2 (i isn't changed)*/

    int a = 5;
    int b = 3;
    printf("%lu\n", (unsigned long)sizeof(a=b+1)); /* print 4 */
    printf("%d\n", a);                             /* print 5 (a isn't changed)*/

    return 0;
}

sizeof只关心((struct Stu*)0)->stuno的类型:char[10]

char类型的sizeof值

C标准要求sizeof(char)的值一定是1, 其他类型根据具体实现而定.

对操作数的限制

除了位域、函数名和空表达式, 其他任何值都可以[1](待测试...fix me!).

输出sizeof值

sizeof操作符的值类型为无符号整数类型size_t, size_t可能被定义为unsigned int或unsigned long或unsigned long long(c99), 那么该如何输出呢?

C89中, 可以将size_t强转成unsigned long, 再输出[1]:

printf("%lu\n", (unsigned long)sizeof(int));

C99中, 有专门输出size_t类型的格式"%zu"[1]:

printf("%zu\n", sizeof(int));

常用方法

计算数组的长度

假设有定义int arr[100];那么sizeof(arr)/sizeof(arr[0])可以获取它的长度100, 可以将其定义为宏, 像使用常量一样使用它:

#define ARR_LEN (sizeof(arr)/sizeof(arr[0]))

这种写法要优于:

#define ARR_LEN (sizeof(arr)/sizeof(int))

因为即使需要修改数组arr的类型, 也无需修改该宏.

在strncpy中使用

一般建议使用strncpy函数代替strcpy函数, 防止数组溢出. 假设有字符数组a和b, 现在要将字串b复制到a中, 由于不知道b的长度, 又要保证a不溢出, 所以惯用的方法如下:

strncpy(a, b, sizeof(a) - 1);
a[sizeof(a) - 1] = '\0';

获取结构体对象的大小

误用

获取数组的大小

有人写了以下两个函数, 分别用来获取数组所占用的字节数和复制字符数组:

#include <stdio.h>

size_t getArrSize(char a[]) {
    return sizeof(a);
}

void myCopy(char a[], char b[]) {
    strncpy(a, b, sizeof(a) - 1);
    a[sizeof(a) - 1] = '\0';
}

int main() {
    char a[100];
    char b[50] = "Strong coffee and late nights";
    
    myCopy(a, b);
    printf("%d\n", (int)getArrSize(a));  /* print 4 (sizeof(char*)) */
    printf("%s\n", a);                   /* print Str */
    return 0;
}

写这两个函数的人, 在使用sizeof之前, 已经犯了错误:误以为数组可以作为值传递, 传递给形参. 声明size_t getArrSize(char a[])在编译器看来, 无异于size_t getArrSize(char *a), 所以sizeof(a)获取的永远都是sizeof(char*). 使用char a[]的方式, 唯一的作用是给看代码的人提示:实参可能是数组. 对于形参而言, 传递过来的是数组的地址(如果您对获取数组长度感兴趣, 请见另一篇文章:对数组的引用). 类似于myCopy函数中的误用, 我在真实系统的代码见到过, 我给myCopy新增了一个参数iALen用来表示数组a的长度:

void myCopy(char a[], char b[], size_t iALen) {
    strncpy(a, b, iALen - 1);
    a[iALen - 1] = '\0';
}

to be continue...I'm sleepy...fix me!见谅, 以下内容待修改.

demo

在32位的机器上, 有:

    printf("%d\n", sizeof(char));       /* print: 1 */
    printf("%d\n", sizeof(short));      /* print: 2 */
    printf("%d\n", sizeof(int));        /* print: 4 */
    printf("%d\n", sizeof(long));       /* print: 4 */
    printf("%d\n", sizeof(float));      /* print: 4 */
    printf("%d\n", sizeof(double));     /* print: 8 */
    printf("%d\n", sizeof(long double));/* print: 8 */
    printf("%d\n", sizeof(string));     /* print: 16 */

1. 若sizeof应用在表达式expr上时, 并未计算表达式的值, 而是计算表达式的类型大小:

    int i = 1;
    double d = 1.2;
    double d2 = 0.0;
    
    printf("%d\n", sizeof(d * i));      /* print: 8 */
    printf("%d\n", sizeof(d2 = d * i)); /* print: 8 */
    printf("%f\n", d2);                 /* print: 0.0 */

2. 指针, 则返回指针所占的字节数, 与所指的对象无关:

    char    c = 'a';
    double  d = 2.0;
    char    *cptr = &c;
    char    *str = "hello";
    double  *dptr = &d;
    double  *dptr1;      /* wild pointer */
    Test    *test = new Test();/* Test is a class */
    
    printf("%d\n", sizeof(cptr));      /* print: 4 */
    printf("%d\n", sizeof(str));       /* print: 4 */
    printf("%d\n", sizeof(dptr));      /* print: 4 */
    printf("%d\n", sizeof(dptr1));     /* print: 4 */
    printf("%d\n", sizeof(test));      /* print: 4 */

3. 指针所指对象, 返回指针(即使该指针是无效的)所指对象的大小:

    char    c = 'a';
    double  d = 2.0;
    char    *cptr = &c;
    char    *str = "hello";
    double  *dptr = &d;
    double  *dptr1;      /* wild pointer */
    string  *str1 = new string[2];
    str1[0] = "hello";
    str1[1] = "world";
    char *cparry = new char[10];
    
    printf("%d\n", sizeof(*cptr));      /* print: 1 */
    printf("%d\n", sizeof(*str));       /* print: 1 */
    printf("%d\n", sizeof(*dptr));      /* print: 8 */
    printf("%d\n", sizeof(*dptr1));     /* print: 8 */
    printf("%d\n", sizeof(*str1));      /* print: 16 */
    printf("%d\n", sizeof(str1[1]));    /* print: 16 */
    printf("%d\n", sizeof(cparry));     /* print: 4 */
    printf("%d\n", sizeof(*cparry));    /* print: 1 */

4. 引用, 返回所引用对象的类型的大小:

    int     i = 1;
    double  d = 3.0;
    int     &refi = i;
    double  &refd = d;

    printf("%d\n", sizeof(refi));       /* print: 4 */
    printf("%d\n", sizeof(refd));       /* print: 8 */
5. 数组, 返回数组所占用的内存空间(数组长度 * 数组元素类型大小, 若是字符串, 则最后的'\0'也计算在内):
    char    carry[] = "hello";
    int     iarry[20];
    string  strarry[] = { "hello", "world", "!" };

    printf("%d\n", sizeof(carry));      /* print: 6 */
    printf("%d\n", sizeof(iarry));      /* print: 80(20 * sizeof(int)) */
    printf("%d\n", sizeof(strarry));    /* print: 64(3 * sizeof(string)) */

注意,当数组作为函数参数时, 数组名将被看作指针:

size_t test(char var[])
{
    return sizeof var;
}

int main()
{
    char a[20] = "hello";
    printf("%d\n", test(a));    /* print: 4 */

    return 0;
}

6. 结构类型, 与该类的成员变量有关. 注意, 编译器为了提高性能, 会对数据进行对齐操作(大小为4的倍数):

struct test1
{
    char ch1;
    int i;
    char ch2;
};
struct test2
{
    char ch1;
    char ch2;
    int i;
};
struct test3
{
    int i;
    char ch1;
    char ch2;
};
struct test4
{
    char ch1;
    int i;
    static char ch2;
};

int main()
{
    printf("%d\n", sizeof(test1));      /* print: 12 */
    printf("%d\n", sizeof(test2));      /* print: 8 */
    printf("%d\n", sizeof(test3));      /* print: 8 */
    printf("%d\n", sizeof(test4));      /* print: 8 */
    return 0;
}

在内存中的结构示意图如下:

test1:
|char|--|--|--|
|-----int-----|
|char|--|--|--|

test2:
|char|char|-|-|
|-----int-----|

test3:
|-----int-----|
|char|char|-|-|

test4:
|char|--|--|--|
|-----int-----|

注意, 静态变量是存放在全局数据区中的, 而sizeof计算栈中分配的大小.

另一个例子:

struct test
{
    float f;
    char ch;
    int i[3];
};

int main()
{
    printf("%d\n", sizeof(test));      /* print: 20 */

    return 0;
}
内存结构:


|----float----|
|char|--|--|--|
|-----i[0]----|
|-----i[1]----|
|-----i[2]----|

7. 类类型:

class A
{};
class A1
{
public:
    A1() {}
    ~A1() {}
};
class A2
{
public:
    A2() {}
    virtual ~A2() {}
};
class B : public A
{};
class C : virtual public A
{};
class D : public A2
{};

int main()
{
    printf("%d\n", sizeof(A));      /* print: 1 */
    printf("%d\n", sizeof(A1));     /* print: 1 */
    printf("%d\n", sizeof(A2));     /* print: 4 */
    printf("%d\n", sizeof(B));      /* print: 1 */
    printf("%d\n", sizeof(C));      /* print: 4 */
    printf("%d\n", sizeof(D));      /* print: 4 */

    return 0;
}

空类, 及继承空类的类所占空间为1字节, 涉及到虚指针时, 则占4个字节.

参考:

1. 《C++ Primer 中文版》第4版 人民邮电出版社 Page167

2. 《程序员面试宝典(第二版)》电子工业出版社 6.3 sizeof

3. Microsoft Visual Studio 2010 文档

References:

[1] Rationale for International Standard—Programming Languages—C, Revision 2, 20 October 1999. ch6.5.3.4 The sizeof operator

[2] C语言程序:设计现代方法(第2版)(English name, C Programming: A Modern Approach),人民邮电出版社, 吕秀锋,黄倩译. ch7.6 sizeof运算符

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值