超详细理解数组和指针

Posted 一朵花花

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了超详细理解数组和指针相关的知识,希望对你有一定的参考价值。

本篇代码均在32位系统下
只要是指针类型,不管是什么类型,在同一个系统下,占用的内存空间都相同

通过sizeof和strlen来加深我们对指针和数组的理解:

sizeof,
sizeof是一个运算符,它的作用是取一个对象(数据类型或者数据对象)的长度(即占用内存的大小,是以字节为单位的) 使用sizeof,一定要关注类型~

strlen,size_t strlen ( const char * str )
计算字符串的长度是一种函数。C字符串的长度等于字符串开头和终止空字符之间的字符数(不包括终止空字符本身),返回值是字符串长度,使用strlen函数,需要包含头文件string.h

一维数组

int a[ ] = { 1,2,3,4 };

1.printf("%d\\n", sizeof(a));        //16
2.printf("%d\\n", sizeof(a + 0));    //4
3.printf("%d\\n", sizeof(*a));       //4
4.printf("%d\\n", sizeof(a + 1));    //4
5.printf("%d\\n", sizeof(a[1]));     //4
  1. (a),表示a[ ]数组,int占4个字节,一共有4个元素,即4个int,故为:4x4=16
  2. (a+0),数组不能进行+运算,隐式转成了指针,为int * 类型,故为:4
  3. (*a),数组不能解引用,隐式转成指针,为int * 类型,再解引用,得到int类型,故为:4
  4. (a+1),和第2个一样,为int * 类型,故为:4
  5. (a[1]),a[1]得到数组中的’2’这个元素,为int类型,故为:4
6.printf("%d\\n", sizeof(&a));       //4
7.printf("%d\\n", sizeof(*&a));      //16
8.printf("%d\\n", sizeof(&*a));      //4
9.printf("%d\\n", sizeof(&a + 1));   //4
10.printf("%d\\n", sizeof(&a[0]+1)); //4
  1. (&a),表示数组指针,为int(*)[4] 类型,也是指针,故为:4
  2. (*&a),先&a,得到int *[4] ,是数组指针,再解引用,便还原到int[4],就是一个数组,故为:4x4=16
  3. (&*a),先 *a,得到一个int,再&,得到int *,故为:4
  4. (&a),得到一个数组指针,再+1,还是数组指针,故为:4
  5. (&a[0]+1),a[0],得到int型,&a[0],得到int*,再+1,仍为int*,故为:4

字符数组

char arr[ ]={ ‘a’,‘b’,‘c’,‘d’,‘e’,‘f’ };

1.printf("%d\\n", sizeof(arr));        //6
2.printf("%d\\n", sizeof(arr + 0));    //4
3.printf("%d\\n", sizeof(*arr));       //1
4.printf("%d\\n", sizeof(arr[1]));     //1 
5.printf("%d\\n", sizeof(&arr));       //4
6.printf("%d\\n", sizeof(&arr + 1));   //4
7.printf("%d\\n", sizeof(&arr[0] + 1));//4
8.printf("%d\\n", sizeof(*&arr));      //6
9.printf("%d\\n", sizeof(&*arr));      //4
  1. (arr),表示arr[ ],整个char数组里有6个元素,故为:6
  2. (arr+0),数组不能进行+运算,隐式转成了指针,为char* 类型,故为:4
  3. (*arr),数组不能解引用,隐式转成指针,为char * 类型,再解引用,得到char类型,故为:4
  4. arr[1],得到字符b,故为1
  5. &arr,得到一个数组指针,类型是char(*)[6],还是指针,故为:4
  6. (&arr+1),和5一样,为数组指针类型,故为:4
  7. (&arr[0]+1),先[0],得到char类型,再&,得到一个char*,再+1,仍为指针,故为:4
  8. (*&arr),先&arr,得到char( *)[6],再解引用,得到char[6],故为:6x1=6
  9. (&*arr),先 *arr,得到char,再&,得到char *,故为:4
1.printf("%d\\n", strlen(arr));
2.printf("%d\\n", strlen(arr + 0));
3.printf("%d\\n", strlen(*arr));
4.printf("%d\\n", strlen(arr[1]));
5.printf("%d\\n", strlen(&arr));
6.printf("%d\\n", strlen(&arr + 1));
7.printf("%d\\n", strlen(&arr[0] + 1));
  1. 上述为未定义行为,因为arr数组中没有 ‘\\0’
  2. arr+0,到a,从a开始找,仍无’\\0’,故仍为未定义行为
  3. strlen的类型是const char*,而*arr的类型是char,类型不匹配,编译不通过(弱类型C语言可以编译运行,发生隐式类型转换)
  4. arr[1],得到字符b,为char类型,与strlen类型不匹配
  5. &arr,得到数组指针,与strlen类型不匹配,在C语言中,会发生隐式类型转换,把数组指针传换成char*,仍找不到’\\0’,故仍为未定义行为
  6. 未定义行为,&arr,得到数组指针,再+1,跳过整个数组,下标越界
  7. arr[0],得到char,再&,得到char*,再+1,得到b,从b往后找,仍无’\\0’,故为未定义行为

只要当前字符数组不是字符串,就不能使用str系列的函数~

char a[ ] = “abcdef”;

1.printf("%d\\n", sizeof(a));        //7
2.printf("%d\\n", sizeof(a + 0));    //4
3.printf("%d\\n", sizeof(*a));       //4
4.printf("%d\\n", sizeof(a[1]));     //1
5.printf("%d\\n", sizeof(a[1] + 1)); //4
6.printf("%d\\n", sizeof(&a));       //4
7.printf("%d\\n", sizeof(&a + 1));   //4
8.printf("%d\\n", sizeof(&a[0] + 1));//4
9.printf("%d\\n", sizeof(*&a));      //7
10.printf("%d\\n", sizeof(&*a));     //4
  1. (a),表示a[ ],为char类型,包含’\\0’,故为:6+1=7
  2. 数组不能+运算,隐士转换成指针,为char*类型,故为:4
  3. *a,得到字符a,为char类型,故为:1
  4. a[1],得到一个char,故为:1
  5. a[1],得到一个字符b,为char,再+1,即char和int进行运算,触发整型提升,结果为int,故为:4
  6. &a,得到数组指针,为char(*)[7]类型,也为指针,故为:4
  7. (&a+1),&a,得到一个数组指针,再+1,仍为数组指针,故为:4
  8. a[0],得到字符a,为char,再&,得到char*,再+1,仍为char*,故为:4
  9. *&a,先&a,得到一个数组指针,为char( *)[7],再 *,得到char[7],故为:7
  10. &*a,先 *a,得到一个char,再&,得到一个char *,故为:4
1.printf("%d\\n", strlen(a));        //6
2.printf("%d\\n", strlen(a + 0));    //6
3.printf("%d\\n", strlen(*a));       //编译不通过
4.printf("%d\\n", strlen(a[1]));     //编译不通过
5.printf("%d\\n", strlen(&a));       //6
6.printf("%d\\n", strlen(&a + 1));   //未定义行为
7.printf("%d\\n", strlen(&a[0] + 1));//5
  1. a是字符数组表示字符串,故为:6
  2. 从字符a开始往后找’\\0’,找6步,故为:6
  3. *a,得到字符a,为char类型,类型不匹配,编译不通过
  4. a[1],得到字符a,为char类型,类型不匹配,编译不通过
  5. &a,得到数组指针,与strlen的类型不匹配(理论来说编译应该不通过),但在C语言中会触发隐士类型转换,转成char*,从a往后找,6步找到’\\0’,故为:6
  6. &a,得到数组指针,再+1,要跳过这个数组,下标越界,故为未定义行为
  7. a[0],得到字符a,再&,得到char*,+1,指向字符b,从b开始找’\\0’,需要5步,故为:5

char* p = “abcdef”;

1.printf("%d\\n", sizeof(p));         //4 
2.printf("%d\\n", sizeof(p + 1));     //4 
3.printf("%d\\n", sizeof(*p));        //1
4.printf("%d\\n", sizeof(p[0]));      //1
5.printf("%d\\n", sizeof(&p));        //4
6.printf("%d\\n", sizeof(&p + 1));    //4
7.printf("%d\\n", sizeof(&p[0] + 1)); //4
  1. p的类型是char*,故为:4
  2. p是char*,再+1,仍为char*,故为:4
  3. *p,是char类型,故为:1
  4. p[0],得到一个char,故为:1
  5. &p,得到char**类型,故为:4
  6. &p,得到char** 类型,+1仍为char**,故为:4
  7. p[0],得到char,再&,得到char*,+1仍为char*,故为:4
1.printf("%d\\n", strlen(p));        //6
2.printf("%d\\n", strlen(p + 1));    //5
3.printf("%d\\n", strlen(*p));       //编译出错
4.printf("%d\\n", strlen(p[0]));     //编译出错
5.printf("%d\\n", strlen(&p));       //编译出错
6.printf("%d\\n", strlen(&p + 1));   //编译出错
7.printf("%d\\n", strlen(&p[0] + 1));//5
  1. p,指向字符a,往后找’\\0’,需6步,故为:6
  2. (p+1),指向字符b,往后找’\\0’,需5步,故为:5
  3. *p,得到char,strlen的类型是const char *,类型不匹配,编译出错
  4. p[0],得到char,strlen的类型是const char *,类型不匹配,编译出错
  5. &p,得到char**,与strlen类型不匹配,编译出错
  6. (&p + 1)),同5
  7. p[0],得到char,再&,得到字符a,+1,得到字符b,往后找’\\0’,5步,故为:5

二维数组

int a[3][4] = { 0 };
长度为3,里面的每个元素的长度又为4

1.printf("%d\\n", sizeof(a));            //48
2.printf("%d\\n", sizeof(a[0][0]));      //4
3.printf("%d\\n", sizeof(a[0]));         //16
4.printf("%d\\n", sizeof(a[0] + 1));     //4
5.printf("%d\\n", sizeof(*a[0] + 1));    //4
6.printf("%d\\n", sizeof(a + 1));        //4
7.printf("%d\\n", sizeof(*(a + 1)));     //16
8.printf("%d\\n", sizeof(&a[0] + 1));    //4
9.printf("%d\\n", sizeof(*(&a[0] + 1))); //16
10.printf("%d\\n", sizeof(*a));          //16 
11..printf("%d\\n", sizeof(a[3]));       //16
  1. (a),二维数组一共12个元素,12x4=48字节,故为:48
  2. a[0][0],取到第0行第0列的元素,故为:4
  3. a[0],即得到int[4],故为:4x4=16
  4. a[0],得到int[4],数组不能+1,隐士转成指针,为int*,故为:4
  5. a[0],得到int[4],+1,隐士转成int*,再*,得到int,故为:4
  6. a为int[3][4]类型,+1,得到int(*)[4],故为:4
  7. a+1得到int(* )[4],再*,得到int[4],故为:4x4=16
  8. a[0],得到int[4],再&,得到int(* )[4],+1,仍为int(*)[4],故为:4
  9. &a[0] + 1,同上,得到nt(*)[4],再 *,得到int[4],故为:4x4=16
  10. *a,相当于隐士转成指针后再解引用,*a等价于a[0],类型是int[4],故为:4x4=16
  11. 看起来a[3]越界,但由于sizeof是编译期求值,而越界是发生在运行期,当这个代码编译完毕,等价于转换成:printf("%d\\n",16),故为:16

*(a+1) 等价于 a[1]

sizeof和strlen的区别

sizeof是一个关键字,而strlen是函数
sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以’’\\0’'结尾的。
strlen的结果要在运行的时候才能计算出来,是用来计算字符串的长度,不是类型占内存的大小,而sizeof是编译期求值
sizeof只关心这块内存的大小,不关心这块内存存放了什么数据,而strlen关心这块内存存放的数据,不关心这块内存的大小,直到遇到第一个\\0为止。

不同的参数,sizeof的返回值

数组编译时分配的数组空间大小
指针存储该指针所用的空间大小(在32位系统是4,在64系统是8)
对象对象的实际占用空间大小
类型该类型所占的空间大小
函数函数的返回类型所占的空间大小。函数的返回类型不能是void

以上是关于超详细理解数组和指针的主要内容,如果未能解决你的问题,请参考以下文章

C语言指针(指针数组数组指针函数指针传参回调函数等)超详细

搞定C语言指针,指针超详细讲解,及指针面试题

一篇文章搞定C语言指针,指针超详细讲解,及指针面试题

Python Numpy库教程(超详细)

学习NumPy全套代码超详细基本操作数据类型数组运算复制和试图索引切片和迭代形状操作通用函数线性代数

超详细一文学会链表解题