关于sizeof的一些思考

前言

今天,我所在的技术群(qq185017593),讨论了sizeof的问题,聊得不亦乐乎,然而一个群友贴的代码,我感觉有一些漏洞,于是梳理了一下思路和知识点,特此记录一下。

sizeof 实现

关于sizeof的实现,如下所示[1]:

#define SIZEOF(v) (char*)(&v + 1) - (char*)(&v)

我们可以看到,计算某个变量的字节数,本质是取到该变量的地址后,经过一次+1运算,获得下一个变量的起始地址,最后通过相减获得字节数。

测试

现在我们拿自己实现的SIZEOF和官方实现的sizeof作对比,来测试这段代码。

#include <stdio.h>
#include <stdlib.h>

#define SIZEOF(v) (char*)(&v + 1) - (char*)(&v)

char array[10] = { 0 };
char* a = NULL;

int main() {
    printf("array SIZEOF size:%ld \n", SIZEOF(array));
    printf("array sizeof size:%ld \n", sizeof(array));
    
    printf("a SIZEOF size:%ld \n", SIZEOF(a));
    printf("a sizeof size:%ld \n", sizeof(a));
    
	return 0;
}

-- 输出
array SIZEOF size:10 
array sizeof size:10 
a SIZEOF size:8 
a sizeof size:8 

由此我们可以看到,即便是值为NULL的指针变量,其实质在程序启动时,也已经被分配了内存,sizeof其实质是在计算a,这个指针变量的字节数。

关于计算malloc和realloc字节大小的问题

对上面的例子,进行修改,得到如下代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZEOF(v) (char*)(&v + 1) - (char*)(&v)
#define CONST_STR "Hello World"

char array[10] = { 0 };
char* a = NULL;

int main() {
    a = (char*)malloc(strlen(CONST_STR) + 1);
    printf("after malloc sizeof(a) = %ld", sizeof(a));
    free(a);
    
	return 0;
}

-- 输出
after malloc sizeof(a) = 8

由此可见,在对a变量malloc分配空间前,和对a分配空间后的sizeof值大小是一致的,这是因为a在程序启动的时候,已经被分配了内存,sizeof只是计算a这个变量的内存字节大小,而malloc出来的内存块的地址,只是作为值保存在a中,然而我们并不能通过sizeof(*a)得到malloc出来的完整的内存块字节,只能获得1:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZEOF(v) (char*)(&v + 1) - (char*)(&v)
#define CONST_STR "Hello World"

char array[10] = { 0 };
char* a = NULL;

int main() {
    a = (char*)malloc(strlen(CONST_STR) + 1);
    printf("after malloc sizeof(a) = %ld", sizeof(*a));
    free(a);
    
	return 0;
}

-- 输出
after malloc sizeof(*a) = 1

因此,malloc出来的大小,只能我们自己去记录。同时realloc的情况也类似

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZEOF(v) (char*)(&v + 1) - (char*)(&v)
#define CONST_STR "Hello World"

char array[10] = { 0 };
char* a = NULL;

int main() {
    a = (char*)realloc(a, strlen(CONST_STR) + 1);
    printf("after malloc sizeof(a) = %ld", sizeof(*a));
    free(a);
    
	return 0;
}

-- 输出
after malloc sizeof(a) = 1

关于字符串赋值

对于字符串赋值,最好的方式就是使用strcpy,不过今天和别人讨论的时候,发现一个有意思的问题,有个朋友非得用memcpy来进行拷贝,于是有了这样的场景:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZEOF(v) (char*)(&v + 1) - (char*)(&v)
#define CONST_STR "Hello World"

char array[10] = { 'H', 'e', 'l', 'l', 'o' };
char array2[20] = { 0 };

int main() {
    printf("1 %s \n", array2);
    memcpy(array2, array, sizeof(array));  
    printf("2 %s \n", array2);
    
	return 0;
}

-- 输出
1  
2 Hello 

emmmm,一切看上去是正常的。如果我们换一种方式,假设两个字符串变量的内存,都是通过malloc开辟的堆内存,那么我们来看一下如下的场景:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZEOF(v) (char*)(&v + 1) - (char*)(&v)
#define CONST_STR "Hello World"

char* a = NULL;
char* b = NULL;

int main() {
    printf("before malloc sizeof(a) = %ld \n", sizeof(a));
    
    a = (char*)malloc(strlen(CONST_STR) + 1);
    printf("after malloc sizeof(a) = %ld \n", sizeof(a));
    strcpy(a, CONST_STR);
    
    b = (char*)malloc(strlen(CONST_STR) + 1);
    memcpy(b, a, sizeof(a));
    printf("b str = %s \n", b);
    
    free(b);
    free(a);
	return 0;
}

-- 输出
before malloc sizeof(a) = 8 
after malloc sizeof(a) = 8 
b str = Hello Wo 

emmmmm…b字符串被截取了,因为正如我们前面看到,sizeof(a)拿到的是,a这个指针变量的字节,而不是a所指向的内存块的字节大小,这台测试机是64位的,因此它是8个字节,如果非得用memcpy的话,那么需要将sizeof(a)修改成strlen(a)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZEOF(v) (char*)(&v + 1) - (char*)(&v)
#define CONST_STR "Hello World"

char* a = NULL;
char* b = NULL;

int main() {
    printf("before malloc sizeof(a) = %ld \n", sizeof(a));
    
    a = (char*)malloc(strlen(CONST_STR) + 1);
    strcpy(a, CONST_STR);
    printf("after malloc sizeof(a) = %ld  strlen(a) = %ld \n", sizeof(a), strlen(a));
    
    b = (char*)malloc(strlen(CONST_STR) + 1);
    memcpy(b, a, strlen(a));
    printf("b str = %s \n", b);
    
    free(b);
    free(a);
	return 0;
}

-- 输出
before malloc sizeof(a) = 8 
after malloc sizeof(a) = 8  strlen(a) = 11 
b str = Hello World 

结论

sizeof返回一个变量,连续的字节大小,如果是个指针变量,那么它只作用在指针变量本身,而不是其所指向的内存地址块。

Reference

[1] implement sizeof operator in c

C