C++ sizeof 杂谈

发布时间 2023-09-15 16:16:03作者: NBest

原来 sizeof 是一个特殊的,运算优先级很高的一种运算符?之前一直都不知道。

参考博客:

c++中sizeof()的用法介绍

C++ 学习杂谈:sizeof(string)到底是多少?

优先级

作为一个运算符,sizeof 自然也是有优先级的,它在 C++ 中优先级为 \(3\),也就是除了作用域解析运算符和诸如括号的操作符,它优先级基本上是最高的,和一般的运算符不同,它的运算是从右向左进行结合的。

运算逻辑

看一下 sizeof 在 msdn 上的定义:

The sizeof keyword gives the amount of storage, in bytes, associated with a variable or a type (including aggregate types). This keyword returns a value of type size_t.

其作用就是返回一个对象或者类型所占的内存字节数,注意,sizeof 返回值是 unsigned long long int

然而一定记住 sizeof 不是一个函数,可以不加括号,比起操作运算符更像是一个特殊的宏,在编译阶段进行求值,为什么这么说?看下文吧。

运算

函数

我们都知道,sizeof 其实是可以对一个函数调用求值的。其结果是函数返回值类型的大小,函数并不会被调用

对函数求值的形式:`sizeof(函数名(实参表))``

注意:
1)不可以对返回值类型为空的函数求值。

2)不可以对函数名求值。

3)对有参数的函数,在用 sizeof 时,须写上实参表。

exp.

#include<bits/stdc++.h>
using namespace std;
unsigned long long int zyx(){
    return 0;
}
int cnt;
void sbzyx(){
    return;
}
int nbest(){
    cnt++;
    return 1;
}
int main(){
    printf("%lld\n",sizeof zyx());
    //输出 8,等价于 sizeof unsigned long long int
    //printf("%lld\n",sizeof sbzyx()); 我这里好像不会报错,输出了个 1,但是会 warning
    //printf("%lld\n",sizeof zyx);报错
    printf("%lld\n",sizeof nbest());
    //输出 4,等价于 sizeof int
    printf("%d\n",cnt);
    //输出 0,证明并没有调用函数
    return 0;
}

基本类型

直接返回类型所占字节,输出对应类型的变量的 sizeof 也一样。

结构体

我也不知道结构体会有字节对齐的问题。

可供参考博客:C/C++ struct字节对齐那些事儿

具体原因上网查吧,我这里展示个代码你们就懂了。

#include<bits/stdc++.h>
using namespace std;
struct node{
    int p;
    long long k;
    bool w;
};
struct node1{
    int p[53];
    long long k;
    bool w;
};
struct node2{
    int p[54];
    long long k;
    bool w;
};
struct zyx{
    int p;
};
struct nb{

};
int main(){
    printf("%lld %lld %lld\n",sizeof(zyx),sizeof(node),sizeof(nb));
    //第一个不出意料的输出 4
    //第二个输出24
    //第三个输出 1
    printf("%lld %lld",sizeof(node1),sizeof(node2));
    //没想到吧,两个都是输出 232,因为两个 int 直接对齐了,但是最后单独的一个 int 为了和 long long 对齐,也会被改成 8。
    return 0;
}

有趣的小知识:
C/C++不允许对struct成员进行重排序,即成员的内存排列顺序一定是定义顺序。

所以这会导致一个问题:

#include<bits/stdc++.h>
using namespace std;
struct node1{
    int a;
    bool b;
    long long c;
};
struct node2{
    int a;
    long long c;
    bool b;
};
int main(){
    cout<<sizeof(node1)<<endl;
    //输出16,因为int 和 bool 合并之后再对齐
    cout<<sizeof(node2);
    //输出24,int 和bool 分别对齐
    return 0;
}

看似相同的代码内存占用却不一样(当然数据大的话其实看不出来)。

联合体union

结构体在内存组织上是顺序式的,联合体则是重叠式,各成员共享一段内存。所以整个联合体的 sizeof 也就是每个成员 sizeof 的最大值。

#include<bits/stdc++.h>
using namespace std;
union poper{
    int k;
    long long p;
    bool x;
    int w[6];
    char ppp[8];
};
union po{
    int k;
    long long p;
    bool x;
    //int w[6];
    //char ppp[8];
};
int main(){
    printf("%lld %lld",sizeof(poper),sizeof(po));
    //第一个最长的是 w[6] 为 4*6=24,输出24
    //第二个最长的是 long long ,输出8
    return 0;
}

数组

数组的 sizeof 值等于数组所占用的内存字节数。

  • 当字符数组表示字符串时,其 sizeof 值将 /0 计算进去。
  • 当数组为形参时,其sizeof值相当于指针的sizeof值。
#include<bits/stdc++.h>
using namespace std;
int poper[82];
char po[18];
char zyx[]="sbzyx";
void yqt(char a[]){
    cout<<sizeof(a)<<endl;
}
void yqtsb(char a[18]){
    cout<<sizeof(a)<<endl;
}
int main(){
    printf("%lld %lld %lld\n",sizeof(poper),sizeof(po),sizeof(zyx));
    //输出 328 18 6
    yqt(po);
    //输出 8,即指针大小
    yqtsb(po);
    //输出 8,同上
    return 0;
}

指针

指针是用来记录另一个对象的地址,所以指针的内存大小当然就等于计算机内部地址总线的宽度。

指针变量的 sizeof 值与指针所指的对象没有任何关系,同一台计算机输出的结果一定一样。

动态空间

比如 string,vector 等动态空间,对它们用 sizeof 的返回值通常是固定的,与长度无关。但是这并不意味着它们的空间就是这么大。

#include<bits/stdc++.h>
using namespace std;
string a="abcadfasfsddxgsdfgf";
basic_string<int>poww;
basic_string<pair<int,int> >fc; 
basic_string<char>fwc="1"; 
vector<pair<int,int> >f(1965);
pair<int,long long> w;
int main(){
    printf("%lld %lld %lld\n",sizeof a,sizeof f,sizeof w);
    //32 24 16
    printf("%lld %lld %lld %lld\n",sizeof(fc),sizeof(fwc),sizeof(poww),sizeof(string));
    //32 32 32 32
    return 0;
}

奇怪的运算符

所以我们可以回答上面的问题了,为什么说 sizeof 很奇怪呢?正常的运算符都是会运行内部的运算的,但是 sizeof 不会(不会运算函数,不会运行赋值操作等)。

#include<bits/stdc++.h>
using namespace std;
int a;
int main(){
    cout<<sizeof(a=3)<<endl;
    //4
    cout<<a<<endl;
    //0
    cout<<sizeof(++a)<<endl;
    //4
    cout<<a<<endl;
    //0
    return 0;
}

小结

为了初赛,算是搞懂了这个奇怪的东西,去研究 union 了~