sizeof操作符

来源:互联网 发布:淘宝酒类授权怎么写 编辑:程序博客网 时间:2024/05/28 15:08

了解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运算符

原创粉丝点击