• 一个C语言程序的分析:运行速度和文件大小以及变量初始值


    环境

    • Ubuntu 22.04
    • gcc 11.4.0
    • Window 11
    • Microsoft Visual Studio Community 2022 (64-bit) - Current Version 17.6.2

    运行速度

    一个C程序 test1.c 如下:

    int array[30000][30000];
    
    int main() {
    	for (int i = 0; i < 30000; i++)
    		for (int j = 0; j < 30000; j++) {
    			array[i][j] = 12345;
    		}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    编译:

    gcc test1.c -o test1
    
    • 1

    运行并查看运行时间

    time ./test1
    ./test1  1.28s user 1.75s system 99% cpu 3.035 total
    
    • 1
    • 2

    用时1.28秒。

    修改一下代码,把循环里i和j的顺序交换一下:

    int array[30000][30000];
    
    int main() {
    	for (int j = 0; j < 30000; j++)
    		for (int i = 0; i < 30000; i++) {
    			array[i][j] = 12345;
    		}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行结果如下:

    time ./test2        
    ./test2  15.92s user 1.59s system 99% cpu 17.533 total
    
    • 1
    • 2

    可见,运行时间从原先的1.28秒暴涨到15.92秒。

    其实,我们能猜到,引起性能下降的原因,一定与数组在内存中的存储方式有关。

    二维数组为例:在C语言中,是按“先列后行”来存储的,也就是按 arr[0][0]arr[0][1]arr[0][2] ,……,arr[1][0]arr[1][1]arr[1][2] ,……这样的顺序连续存储的。

    写一个程序来检验如下:

    #include 
    
    int arr[3][3];
    
    int main() {
    	printf("size of int = %ld\n\n", sizeof(int));
    
    	for (int i = 0; i < 3; i++)
    		for (int j = 0; j < 3; j++) {
    			printf("arr[%d][%d] address: %p\n", i, j, &arr[i][j]);
    		}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    运行结果如下:

    size of int = 4
    
    arr[0][0] address: 0x5585c16fb040
    arr[0][1] address: 0x5585c16fb044
    arr[0][2] address: 0x5585c16fb048
    arr[1][0] address: 0x5585c16fb04c
    arr[1][1] address: 0x5585c16fb050
    arr[1][2] address: 0x5585c16fb054
    arr[2][0] address: 0x5585c16fb058
    arr[2][1] address: 0x5585c16fb05c
    arr[2][2] address: 0x5585c16fb060
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    可见,int 类型的size是4,而数组中每两个相邻元素,其地址编码相差也是4。

    计算机访问连续内存地址的速度,要远远快于非连续地址。这也是数组要比链表访问速度更快的原因。

    注:Java程序也类似。

    文件大小

    全局变量

    再来看一下 test1.c

    int array[30000][30000];
    
    int main() {
    	for (int i = 0; i < 30000; i++)
    		for (int j = 0; j < 30000; j++) {
    			array[i][j] = 12345;
    		}
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    经过编译后,生成的 test1 文件大小为16KB。

    ll test1*
    -rwxr-xr-x 1 root root 16K 11月 19 19:07 test1
    -rw-r--r-- 1 root root 150 11月 19 18:51 test1.c
    
    • 1
    • 2
    • 3

    如果把数组大小改变,编译后的文件大小不变。

    这是为什么呢?按理说,array是一个全局变量,是在编译期静态的生成的,应该会占据可执行文件的空间,但这里却似乎并没有影响。

    我猜测是因为编译器做了一些优化。虽然声明了全局数组,但是没有赋初值,所以编译器并没有为数组分配空间。

    如果有赋初值,则编译后的文件就会随着数组大小而显著变化。

    创建文件 test11.c 如下:

    int array[300][300] = {123};
    
    int main() {
    }
    
    • 1
    • 2
    • 3
    • 4

    编译后,生成的文件大小为368KB:

    ll test11*
    -rwxr-xr-x 1 root root 368K 11月 19 20:06 test11
    -rw-r--r-- 1 root root   45 11月 19 20:06 test11.c
    
    • 1
    • 2
    • 3

    创建文件 test12.c 如下:

    int array[3000][3000] = {123};
    
    int main() {
    }
    
    • 1
    • 2
    • 3
    • 4

    编译后,生成的文件大小为35MB:

    ll test12*
    -rwxr-xr-x 1 root root 35M 11月 19 20:08 test12
    -rw-r--r-- 1 root root  47 11月 19 20:07 test12.c
    
    • 1
    • 2
    • 3

    可见,数组大小扩大100倍,文件大小也扩大了100倍。

    局部变量

    对于局部变量,是运行期(调用对应函数时)在栈上分配内存的,所以数组大小并不会影响文件大小:

    int main() {
    	int array[30000][30000] = {123};
    }
    
    • 1
    • 2
    • 3

    这里不管数组大小如何变化,编译后生成的可执行文件都是16KB。

    未初始化数组的内容

    另一个好玩的问题是,如果定义了数组,但没有初始化,那么数组的内容是什么?

    全局变量

    对于全局变量,其内容会被自动初始化为0。

    #include 
    
    int array[300][300];
    
    int main() {
    	printf("%d\n", array[5][5]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    运行结果如下:

    0
    
    • 1

    这应该是在load可执行文件时,将其内存初始化。

    局部变量

    #include 
    
    int main() {
    	int array[300][300];
    	printf("%d\n", array[5][5]);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    运行结果如下:

    0
    
    • 1

    可见,局部变量的内存也被初始化为0(在调用对应函数,在栈上分配内存时)。

    但这不是100%确定的。在MicroSoft Visual Studio下,其运行结果为:

    -858993460
    
    • 1

    在这里插入图片描述

    总而言之,变量最好先初始化,再使用。

  • 相关阅读:
    RK3399平台开发系列讲解(PCI/PCI-E)5.52、PCIE RC侧设备树及配置
    MySQL事务——事务隔离界别,MVCC
    机械工程英语第二版叶邦彦-汉语翻译最多单元版
    Spring Bean生命周期,好像人的一生。。
    论坛议程|COSCon'23 大数据(D)
    《MySQL实战45讲》——学习笔记04-05 “深入浅出索引、最左前缀原则、索引下推优化“
    首发scitb包,一个为制作统计表格而生的R包
    【C++】C向C++的知识过度(上)
    3D应用开发引擎HOOPS如何促进AEC数字化架构革新?
    E035-服务漏洞利用及加固-利用CVE-2017-7269漏洞渗透IIS6.0实现远程控制
  • 原文地址:https://blog.csdn.net/duke_ding2/article/details/134493904