超详细理解数组和指针
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
- (a),表示a[ ]数组,int占4个字节,一共有4个元素,即4个int,故为:4x4=16
- (a+0),数组不能进行+运算,隐式转成了指针,为int * 类型,故为:4
- (*a),数组不能解引用,隐式转成指针,为int * 类型,再解引用,得到int类型,故为:4
- (a+1),和第2个一样,为int * 类型,故为:4
- (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
- (&a),表示数组指针,为int(*)[4] 类型,也是指针,故为:4
- (*&a),先&a,得到int *[4] ,是数组指针,再解引用,便还原到int[4],就是一个数组,故为:4x4=16
- (&*a),先 *a,得到一个int,再&,得到int *,故为:4
- (&a),得到一个数组指针,再+1,还是数组指针,故为:4
- (&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
- (arr),表示arr[ ],整个char数组里有6个元素,故为:6
- (arr+0),数组不能进行+运算,隐式转成了指针,为char* 类型,故为:4
- (*arr),数组不能解引用,隐式转成指针,为char * 类型,再解引用,得到char类型,故为:4
- arr[1],得到字符b,故为1
- &arr,得到一个数组指针,类型是char(*)[6],还是指针,故为:4
- (&arr+1),和5一样,为数组指针类型,故为:4
- (&arr[0]+1),先[0],得到char类型,再&,得到一个char*,再+1,仍为指针,故为:4
- (*&arr),先&arr,得到char( *)[6],再解引用,得到char[6],故为:6x1=6
- (&*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));
- 上述为未定义行为,因为arr数组中没有 ‘\\0’
- arr+0,到a,从a开始找,仍无’\\0’,故仍为未定义行为
- strlen的类型是const char*,而*arr的类型是char,类型不匹配,编译不通过(弱类型C语言可以编译运行,发生隐式类型转换)
- arr[1],得到字符b,为char类型,与strlen类型不匹配
- &arr,得到数组指针,与strlen类型不匹配,在C语言中,会发生隐式类型转换,把数组指针传换成char*,仍找不到’\\0’,故仍为未定义行为
- 未定义行为,&arr,得到数组指针,再+1,跳过整个数组,下标越界
- 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
- (a),表示a[ ],为char类型,包含’\\0’,故为:6+1=7
- 数组不能+运算,隐士转换成指针,为char*类型,故为:4
- *a,得到字符a,为char类型,故为:1
- a[1],得到一个char,故为:1
- a[1],得到一个字符b,为char,再+1,即char和int进行运算,触发整型提升,结果为int,故为:4
- &a,得到数组指针,为char(*)[7]类型,也为指针,故为:4
- (&a+1),&a,得到一个数组指针,再+1,仍为数组指针,故为:4
- a[0],得到字符a,为char,再&,得到char*,再+1,仍为char*,故为:4
- *&a,先&a,得到一个数组指针,为char( *)[7],再 *,得到char[7],故为:7
- &*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
- a是字符数组表示字符串,故为:6
- 从字符a开始往后找’\\0’,找6步,故为:6
- *a,得到字符a,为char类型,类型不匹配,编译不通过
- a[1],得到字符a,为char类型,类型不匹配,编译不通过
- &a,得到数组指针,与strlen的类型不匹配(理论来说编译应该不通过),但在C语言中会触发隐士类型转换,转成char*,从a往后找,6步找到’\\0’,故为:6
- &a,得到数组指针,再+1,要跳过这个数组,下标越界,故为未定义行为
- 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
- p的类型是char*,故为:4
- p是char*,再+1,仍为char*,故为:4
- *p,是char类型,故为:1
- p[0],得到一个char,故为:1
- &p,得到char**类型,故为:4
- &p,得到char** 类型,+1仍为char**,故为:4
- 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
- p,指向字符a,往后找’\\0’,需6步,故为:6
- (p+1),指向字符b,往后找’\\0’,需5步,故为:5
- *p,得到char,strlen的类型是const char *,类型不匹配,编译出错
- p[0],得到char,strlen的类型是const char *,类型不匹配,编译出错
- &p,得到char**,与strlen类型不匹配,编译出错
- (&p + 1)),同5
- 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
- (a),二维数组一共12个元素,12x4=48字节,故为:48
- a[0][0],取到第0行第0列的元素,故为:4
- a[0],即得到int[4],故为:4x4=16
- a[0],得到int[4],数组不能+1,隐士转成指针,为int*,故为:4
- a[0],得到int[4],+1,隐士转成int*,再*,得到int,故为:4
- a为int[3][4]类型,+1,得到int(*)[4],故为:4
- a+1得到int(* )[4],再*,得到int[4],故为:4x4=16
- a[0],得到int[4],再&,得到int(* )[4],+1,仍为int(*)[4],故为:4
- &a[0] + 1,同上,得到nt(*)[4],再 *,得到int[4],故为:4x4=16
- *a,相当于隐士转成指针后再解引用,*a等价于a[0],类型是int[4],故为:4x4=16
- 看起来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 |
以上是关于超详细理解数组和指针的主要内容,如果未能解决你的问题,请参考以下文章