c语言容易出现的错误数组,%99的C语言初学者都会犯的错误——奇妙的数组(玄学)...

这篇文章将介绍关于数组的两个有趣的问题

一.c语言初学者困惑不解的问题之数组越界导致的无限循环。

我先来简单的介绍一下数组的一些简单概念,这些概念将有助于你更好的理解这篇文章:

数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具有相同类型的数据。

数组(Array)有两个特点:

1.连续的内存空间

2.存储相同类型的的数据

数组(Array)与链表(Link list)的区别:

数组适合随机访问,时间复杂度为O(1);

链表适合插入和删除数据,时间复杂度为O(1);

注意随机访问不是查找,即使用排列好的数组进行二分查找,时间复杂度也要O(logn)。

话不多说,上代码:

int main(int argc, char* argv[]){

int arr[3] = {0};

int i;

for(i; i<=3; i++){

arr[i] = 0;

printf("hello world\n");

}

return 0;

}

首先,我请你来分析一下这段 C 语言代码的运行结果:

你可能会马上得出打印了三次“hello world”的结果

实际上这段代码的运行结果是无限打印 “hello world”,为什么会是这样的结果呢?

实际上这与编译器分配内存地址和字节对齐有关,数组arr中有3个元素 加上变量a之后,4个整数刚好能满足8字节对齐,所以i的地址恰好跟在a[2]的后面,导致死循环。

下面借助图片帮助你理解这个问题。

上图之前先介绍一下数组的寻址公式:

一维数组:a[k]的地址 = base_address+ k * type_size

二维数组:a[i][j]的地址= base_address + ( i * n + j) * type_size(假设为m * n大小的数组)

base_address表示数组首地址,type_size表示数组中数据类型大小。

此时数组在内存中的存储形式为:

c语言容易出现的错误数组,%99的C语言初学者都会犯的错误——奇妙的数组(玄学)..._第1张图片

图片中的a相当于i

如果你明白了以上内容相信下面的内容对你来说更是小菜一碟啦!

我们来看下面的代码

#include

int main(int argc, char* argv[]){

int i;

int arr[4] = {0};

for(i = 0; i<=4; i++){

arr[i] = 0;

printf("hello world\n");

}

return 0;

}

这时我们将数组的大小改为4,运行结果就截然不同了,程序正常运行,运行结果打印了五次"hello world",根据字节对齐原则,这时i的地址没有跟在数组地址的后面所以程序当然不会陷入死循环啦!

题外话(并不是所有的语言都将检查数组是否越界的操作交给程序员,例如在使用Java时,随机访问数组元素下标越界时,编译器会直接抛出异常,感兴趣的小伙伴可以去试一下!)

下面是第二个内容:

二.数组的下标为何要从0开始

这部分内容就非常简单啦:

只需要两个公式你就能明白其中的奥妙了:

数组下标从0开始时:a[k]的地址 = base_address + k * type_size;

数组下标从1开始时: a[k]的地址 = base_address + (k-1)*type_size;

第二个式子可以这样理解,当访问数组中的第一个元素时

a[1]的地址为数组的首地址,所以第一个式子中的k变为(k-1)

也就是说下标从1开始时每次随机访问数组元素都多进行了一次减1操作。而数组作为非常基础的数据结构,通过下标随机访问数组元素又是其非常基础的编程操作,效率的优化就要尽可能做到极致。所以为了减少一次减法操作,数组选择了从 0 开始编号,而不是从 1 开始。

总结:这篇文章的内容可能会有一些不足之处,希望大家在评论区多多提出意见,我也会积极改正的,以后我会经常记录自己在实验室打比赛的经历,空闲的时候还会更新一些有趣的算法题(由于不是专业打ACM的,太难的我也不会哈哈哈),如果这篇文章对你有所帮助,请点个赞吧!谢谢鼓励!

c语言容易出现的错误数组,%99的C语言初学者都会犯的错误——奇妙的数组(玄学)..._第2张图片

你可能感兴趣的:(c语言容易出现的错误数组)