前言
今天,我所在的技术群(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返回一个变量,连续的字节大小,如果是个指针变量,那么它只作用在指针变量本身,而不是其所指向的内存地址块。