- C语言指针就该这样学(指针练习)
- 0x00 - 关于指针练习的重要总结
- 0x01 - 指针练习第一部分
- 0x011 - 第一个练习
- 0x012 - 第二个练习
- 0x013 - 第三个练习
- 0x014 - 第四个练习
- 0x015 - 第五个练习
数组名通常是指数组首元素的地址,但是有如下两个例外情况:
1、当数组名放在运算符sizeof之中的时候,数组名表示整个数组,sizeof(arr)求取整个数组的大小。(在下文中简称情况一)
2、当对数组名取地址&arr时,表示取了整个数组的地址,类型是一种数组指针。(在下文中简称情况二)
其余情况中直接使用数组名表示数组首元素的地址
0x01 - 指针练习第一部分指针进行加减时,需要将整数乘以指针类型对于的字节长度,这样指针才能正确找打其应该所在的位置。
源码写在前面:
# include0x011 - 第一个练习# include void test1() { int a[] = { 1, 2, 3, 4 }; printf("%d ", sizeof(a)); printf("%d ", sizeof(a + 0)); printf("%d ", sizeof(*a)); printf("%d ", sizeof(a + 1)); printf("%d ", sizeof(a[1])); printf("%d ", sizeof(&a)); printf("%d ", sizeof(*&a)); printf("%d ", sizeof(&a + 1)); printf("%d ", sizeof(&a[0])); printf("%d ", sizeof(&a[0] + 1)); } void test2() { char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; printf("%dn", sizeof(arr)); printf("%dn", sizeof(arr + 0)); printf("%dn", sizeof(*arr)); printf("%dn", sizeof(arr[1])); printf("%dn", sizeof(&arr)); printf("%dn", sizeof(&arr + 1)); printf("%dn", sizeof(&arr[0] + 1)); printf("%dn", strlen(arr)); printf("%dn", strlen(arr + 0)); printf("%dn", strlen(*arr)); printf("%dn", strlen(arr[1])); printf("%dn", strlen(&arr)); printf("%dn", strlen(&arr + 1)); printf("%dn", strlen(&arr[0] + 1)); } void test3() { char arr[] = "abcdef"; printf("%dn", sizeof(arr)); printf("%dn", sizeof(arr + 0)); printf("%dn", sizeof(*arr)); printf("%dn", sizeof(arr[1])); printf("%dn", sizeof(&arr)); printf("%dn", sizeof(&arr + 1)); printf("%dn", sizeof(&arr[0] + 1)); printf("%dn", strlen(arr)); printf("%dn", strlen(arr + 0)); printf("%dn", strlen(*arr)); printf("%dn", strlen(arr[1])); printf("%dn", strlen(&arr)); printf("%dn", strlen(&arr + 1)); printf("%dn", strlen(&arr[0] + 1)); } void test4() { char* p = "abcdef"; printf("%dn", sizeof(p)); printf("%dn", sizeof(p + 1)); printf("%dn", sizeof(*p)); printf("%dn", sizeof(p[0])); printf("%dn", sizeof(&p)); printf("%dn", sizeof(&p + 1)); printf("%dn", sizeof(&p[0] + 1)); printf("%dn", strlen(p)); printf("%dn", strlen(p + 1)); printf("%dn", strlen(*p)); printf("%dn", strlen(p[0])); printf("%dn", strlen(&p)); printf("%dn", strlen(&p + 1)); printf("%dn", strlen(&p[0] + 1)); } void test5() { int a[3][4] = { 0 }; printf("%dn", sizeof(a)); printf("%dn", sizeof(a[0][0])); printf("%dn", sizeof(a[0])); printf("%dn", sizeof(a[0] + 1)); printf("%dn", sizeof(*(a[0] + 1))); printf("%dn", sizeof(a + 1)); printf("%dn", sizeof(*(a + 1))); printf("%dn", sizeof(&a[0] + 1)); printf("%dn", sizeof(*(&a[0] + 1))); printf("%dn", sizeof(*a)); printf("%dn", sizeof(a[3])); } int main(void) { test1(); //test2(); //test3(); //test4(); //test5(); return 0; }
# include# include void test1() { int a[] = { 1, 2, 3, 4 }; printf("%dn", sizeof(a)); // 16 printf("%dn", sizeof(a + 0)); // 4/8 printf("%dn", sizeof(*a)); // 4 printf("%dn", sizeof(a + 1)); // 4/8 printf("%dn", sizeof(a[1])); // 4 printf("%dn", sizeof(&a)); // 4/8 printf("%dn", sizeof(*&a)); // 16 printf("%dn", sizeof(&a + 1)); // 4/8 printf("%dn", sizeof(&a[0])); // 4/8 printf("%dn", sizeof(&a[0] + 1)); // 4/8 } int main(void) { test1(); //test2(); //test3(); //test4(); //test5(); return 0; }
对每个练习都逐个进行解析:
- 1、直接对a进行sizeof适用于情况一,计算的是整个数组的字节大小,每个int类型是4个字节,所以结果是16个字节。
- 2、a代表首元素的地址,a+0任然是首元素地址,这里再取sizeof,对于32位的机器来说,地址线共有32位,需要4个字节来存储,对于64位的机器来说,地址线共有64位,需要8个字节来进行存储,所以这里的结果是4或者8。
- 3、sizeof(*a)表示对首元素的地址处的指针进行解引用,即获得了数组的首元素,值为1是一种int类型,占4个字节所以结果为4。
- 4、sizeof(a+1)和第三条类似,a代表首元素的地址,a+1代表指针指向下一位元素,a+1任然是一种地址指针,所以占4或者8个字节。
- 5、sizeof(a[1]),a[1]可以等价为*(a+1),这其实是表示取出a数组中索引为1,实际是第二个元素2,元素的类型为int,所以最终的结果是4个字节。
- 6、sizeof(&a),如情况二所示,&a表示将数组的地址取出来,类型是数组指针,这道题中应该是int (*) [4]类型,仍然是一种地址,那么就应该是32位或者64位,那么就应该占4个字节或者8个字节。
- 7、sizeof(*&a),这里一种简单的看法就是*和&相互抵消了,实际上等价于sizeof(a)表示取出数组的字节长度,结果是16,另一种看法是&a先取出了a数组的地址,然后再对其解引用,实际上是获得了数组a,计算数组的字节长度,那么就是16。
- 8、sizeof(&a + 1),&a这里获得数组a的地址,类型是int (*) [4],数组指针类型进行+1那么会跳过一个数组的长度,即跳过16个字节,那么&a+1实际指向的下一个数组的位置,但是&a+1同样是地址,那么就为32或者64位,占4或者8个字节。
- 9、sizeof(&a[0]),实际上是取出a数组首元素的地址,32位或者64位,占4或者8个字节。
- 10、sizeof(&a[0]+1),&a[0]的类型是int*,那么&a[0]+1实际是进行4个字节的加减,那么就指向了数组索引为1,第二个元素,仍然保存的是地址,32位或者64位,占4个字节或者8个字节。
# include# include void test2() { char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' }; printf("%dn", sizeof(arr)); // 6 printf("%dn", sizeof(arr + 0));// 4/8 printf("%dn", sizeof(*arr)); // 1 printf("%dn", sizeof(arr[1])); // 1 printf("%dn", sizeof(&arr)); // 4/8 printf("%dn", sizeof(&arr + 1)); // 4/8 printf("%dn", sizeof(&arr[0] + 1)); // 4/8 printf("%dn", strlen(arr)); //随机值 printf("%dn", strlen(arr + 0)); //随机值 printf("%dn", strlen(*arr)); //error printf("%dn", strlen(arr[1])); //error printf("%dn", strlen(&arr)); //随机值 printf("%dn", strlen(&arr + 1)); //随机值-6 printf("%dn", strlen(&arr[0] + 1)); //随机值-1 } int main(void) { //test1(); test2(); //test3(); //test4(); //test5(); return 0; }
- 1、sizeof(arr),适用于情况一,表示求数组的字节长度,那么数组arr共有6个元素,每个元素的类型都是char,占一个字节,所以数组的字节长度为6。
- 2、sizeof(arr +0),arr这里表示数组首元素的地址,那么类型就是char*,arr+0仍然指向的是首元素,这里是一个地址,那么占4个字节或者8个字节。
- 3、sizeof(*arr),arr表示数组首元素的地址,*arr表示对其进行解引用获得了数组的首元素,值为a,是char类型,占1个字节。
- 4、sizeof(arr[1]),实际上等价于sizeof(*(arr+1)),是取出了数组的第二个元素,值为b,是char类型,占1个字节。
- 5、sizeof(&arr),适用于情况二,&arr取出的是整个数组的地址,是数组指针,类型是char (*)[6],仍然是地址,那么占4个字节或者8个字节。
- 6、sizeof(&arr + 1),&arr取出的是整个数组的地址,是数组指针,类型是char (*)[6],&arr + 1指向的是下一个数组的地址。但是其本身仍然是一个地址,所以占4个字节或者8个字节。
- 7、sizeof(&arr[0] + 1),&arr[0]表示取出arr首元素的地址,类型是char*,&arr[0] + 1表示取出arr中第二个元素的地址,仍然是一种地址,占4个字节或者8个字节。
- 8、strlen(arr),arr表示首元素的地址,那么表示从a这个值开始向下计数,直到 停止计算了字符的长度,但是这里不知道 在内存中什么位置,所以这里出现随机值。
- 9、strlen(arr+0),arr+0仍然是首元素的地址,结果仍然是随机值。
- 10、strlen(*arr),*arr表示对首元素的地址进行解引用,获得首元素的值,即a但是strlen需要传入一个指针类型的变量,这里传入了一个a,会引发报错error。
- 11、strlen(arr[1]),arr[1]表示获得数组第二个元素的值即’b’,同样的道理,也会引发报错error。
- 12、strlen(&arr),适用于情况二,&arr表示取出数组的地址,类型是char (*)[6],&arr指向的数组的起始位置,从起始位置往下直到遇见 停止,所以这个结果也是随机值。
- 13、strlen(&arr + 1),&arr取出的是整个数组的地址,是数组指针,类型是char (*)[6],&arr + 1指向的是下一个数组的地址。这里从下一个数组的起始位置开始计算,也是遇见 停止,那么结果也是随机值,这个随机值可以写作随机值-6。
- 14、strlen(&arr[0] + 1),&arr[0]表示取出arr首元素的地址,类型是char*,&arr[0] + 1表示取出arr中第二个元素的地址,也是遇见 停止,那么结果也是随机值,这个随机值可以写作随机值-1。
# include# include void test3() { char arr[] = "abcdef"; printf("%dn", sizeof(arr)); //7 printf("%dn", sizeof(arr + 0)); //4/8 printf("%dn", sizeof(*arr)); //1 printf("%dn", sizeof(arr[1])); //1 printf("%dn", sizeof(&arr)); //4/8 printf("%dn", sizeof(&arr + 1)); //4/8 printf("%dn", sizeof(&arr[0] + 1)); //4/8 printf("%dn", strlen(arr)); //6 printf("%dn", strlen(arr + 0)); //6 printf("%dn", strlen(*arr)); //error printf("%dn", strlen(arr[1]));//error printf("%dn", strlen(&arr));//6 printf("%dn", strlen(&arr + 1));//随机值 printf("%dn", strlen(&arr[0] + 1));//5 } int main(void) { //test1(); //test2(); test3(); //test4(); //test5(); return 0; }
- 1、sizeof(arr),适用于情况一,表示直接计算数组字节长度,这里数组其实有7个元素,所以实际长度为7 * 1,为7个字节。
- 2、sizeof(arr + 0),arr这里表示数组首元素的地址,那么类型就是char*,arr+0仍然指向的是首元素,这里是一个地址,那么占4个字节或者8个字节。
- 3、sizeof(*arr),*arr这里表示对数组首元素进行解引用,获得了a,字节长度为1。
- 4、sizeof(arr[1]),arr[1]表示取出数组的第二个元素b,字节长度为1。
- 5、sizeof(&arr),适用于情况二,&arr取出的是整个数组的地址,是数组指针,类型是char (*)[6],仍然是地址,那么占4个字节或者8个字节。
- 6、sizeof(&arr+1),&arr取出的是整个数组的地址,是数组指针,类型是char (*)[6],&arr + 1指向的是下一个数组的地址。但是其本身仍然是一个地址,所以占4个字节或者8个字节。
- 7、sizeof(&arr[0] + 1),&arr取出的是整个数组的地址,是数组指针,类型是char (*)[6],&arr + 1指向的是下一个数组的地址。但是其本身仍然是一个地址,所以占4个字节或者8个字节。
- 8、strlen(arr),arr表示首元素的地址,那么表示从a这个值开始向下计数,直到 停止计算了字符的长度,计算长度为6。
- 9、strlen(arr + 0), arr表示首元素的地址,arr+0同样也是指向a的指针,那么表示从a这个值开始向下计数,直到 停止计算了字符的长度,计算长度为6。
- 10、strlen(*arr),*arr表示对首元素的地址进行解引用,获得首元素的值,即a但是strlen需要传入一个指针类型的变量,这里传入了一个a,会引发报错error。
- 11、strlen(arr[1]),arr[1]表示获得数组第二个元素的值即’b’,同样的道理,也会引发报错error。
- 12、strlen(&arr),适用于情况二,&arr表示取出数组的地址,类型是char (*)[6],&arr指向的数组的起始位置,从起始位置往下直到遇见 停止,结果为6。
- 13、strlen(&arr + 1),&arr取出的是整个数组的地址,是数组指针,类型是char (*)[6],&arr + 1指向的是下一个数组的地址。这里从下一个数组的起始位置开始计算,也是遇见 停止,那么结果是随机值。
- 14、strlen(&arr[0] + 1),&arr[0]表示取出arr首元素的地址,类型是char*,&arr[0] + 1表示取出arr中第二个元素的地址,从第二个元素开始计算,到 停止,结果为5。
# include# include void test4() { char* p = "abcdef"; printf("%dn", sizeof(p)); //4/8 printf("%dn", sizeof(p + 1)); //4/8 printf("%dn", sizeof(*p)); //1 printf("%dn", sizeof(p[0])); //1 printf("%dn", sizeof(&p)); //4/8 printf("%dn", sizeof(&p + 1)); //4/8 printf("%dn", sizeof(&p[0] + 1)); //4/8 printf("%dn", strlen(p)); //6 printf("%dn", strlen(p + 1)); //5 printf("%dn", strlen(*p)); //error printf("%dn", strlen(p[0])); //error printf("%dn", strlen(&p)); //随机值 printf("%dn", strlen(&p + 1)); //随机值 printf("%dn", strlen(&p[0] + 1)); //5 } int main(void) { //test1(); //test2(); //test3(); test4(); //test5(); return 0; }
- 1、sizeof(p),p是指向字符串的指针,p保存的是地址,所以值为4个字节或者8个字节。
- 2、sizeof(p+1),p是指向字符串的指针,类型是char*,所以+1就会加上1个字节,指向第二个元素,p+1同样也是保存的地址,所以为4个字节或者8个字节。
- 3、sizeof(*p),*p表示对p进行解引用获得了p指向的值a,a是一个char类型的值,所以占1个字节大小。
- 4、sizeof(p[0]),可以等价于sizeof(*(p+0)),取出数组的第一个元素a,占一个字节大小。
- 5、sizeof(&p),p中存放的是a的地址,&p取地址,取出的是存放p指针的地址,仍然是一个地址,大小为4或者8个字节。
- 6、sizeof(&p + 1),p中存放的是a的地址,&p取地址,取出的是存放p指针的地址,p是char*类型的指针,那么&p应该是char**类型的指针,&p+1指向p后面的一个地址,仍然是一种地址,那么应该为4或者8个字节。
- 7、sizeof(&p[0] + 1),p[0]等价于*(p+0),实际上的取出了数组的第一个元素,&p[0]实际获得了数组首元素的地址,那么&p[0]+1获得了数组第二个元素的地址。为4个字节或者8 个字节。
- 8、strlen(p),p是指向字符串的指针,从首元素地址开始计算,长度为6。
- 9、strlen(p+1),p是指向字符串的指针,类型是char*,所以+1就会加上1个字节,指向第二个元素,从第二个元素地址开始计算,长度为5。
- 10、strlen(*p),*p表示对p进行解引用获得了p指向的值a,a是一个char类型的值,a不是一个地址,所以这里会出现错误error。
- 11、strlen(p[0]),可以等价于sizeof(*(p+0)),取出数组的第一个元素a,a不是一个地址,所以这里会出现错误error。
- 12、strlen(&p),&p取地址,取出的是存放p指针的地址,这里就不知道后续的内存中存储了什么值,直到遇见 才停止,所以这里出现随机值。
- 13、strlen(&p+1),p中存放的是a的地址,&p取地址,取出的是存放p指针的地址,p是char*类型的指针,那么&p应该是char**类型的指针,&p+1指向p后面的一个地址,这里就不知道后续的内存中存储了什么值,直到遇见 才停止,所以这里出现随机值。
- 14、strlen(&p[0] + 1),p[0]等价于*(p+0),实际上的取出了数组的第一个元素,&p[0]实际获得了数组首元素的地址,那么&p[0]+1获得了数组第二个元素的地址,从第二个元素开始计算,长度为5。
# include# include void test5() { int a[3][4] = { 0 }; printf("%dn", sizeof(a)); //48 printf("%dn", sizeof(a[0][0])); //4 第一行第一列的元素 printf("%dn", sizeof(a[0])); //16 第一行的元素 printf("%dn", sizeof(a[0] + 1)); //4/8 第一行第二个元素的地址 printf("%dn", sizeof(*(a[0] + 1))); //4 第一行第二个元素的值 printf("%dn", sizeof(a + 1)); //4/8 第二行的元素的地址 printf("%dn", sizeof(*(a + 1))); //16 第二行的元素 printf("%dn", sizeof(&a[0] + 1)); //8 第二行元素的地址,为什么和a2[0] + 1的结果不一样呢? //这是因为&a2[0]获得的指针类型是数组指针,+1会跳过整个一行, //而a2[0]是普通的int类型指针,+1跳过4个字节,获得下一个元素的地址。 printf("%dn", sizeof(*(&a[0] + 1))); //16 第二行的元素 printf("%dn", sizeof(*a)); //16 第一行的元素 printf("%dn", sizeof(a[3])); //16 第四行的元素 } int main(void) { //test1(); //test2(); //test3(); //test4(); test5(); return 0; }
- 1、sizeof(a),适用于情况一,计算整个数组的字节大小,数组共有12个元素,每个都是int类型占4个字节,所以总共48个字节。
- 2、sizeof(a[0][0]),计算第一行第一列元素的大小,为int类型,所以结果为4。
- 3、sizeof(a[0]),a[0]是一个一维数组名,类型是int*,同样也是数组名适用于情况一,计算整个数组的大小,有4个元素,都是int类型占4个字节,所以结果为16。
- 4、sizeof(a[0]+1),a[0]是一个一维数组名,类型是int*,a[0]+1应该跳过一个int类型即4个字节,a[0]+1获得了第一行第二个元素的指针,是一个地址,占4个或者8个字节。
- 5、sizeof(*(a[0]+1)),a[0]是一个一维数组名,类型是int*,a[0]+1应该跳过一个int类型即4个字节,a[0]+1获得了第一行第二个元素的指针,*(a[0]+1)获得了第一行第二列这个元素0,是一个int类型,占4个字节。
- 6、sizeof(a+1),a是数组名,是指向整个数组首元素的指针,对于一个二维数组来说,他的首元素的一个一维数组,所以a+1指向第二行的元素,是一个地址,结果为4或则和8个字节。
- 7、sizeof(*(a + 1)),a是数组名,是指向整个数组首元素的指针,对于一个二维数组来说,他的首元素的一个一维数组,所以a+1指向第二行的元素,*(a+1)获得了第二行的元素,所以结果为16个字节。
- 8、sizeof(&a[0] + 1),a[0]是第一行元素,&a[0]是取出第一行元素的地址,是数组指针,类型为int (*)[4],+1会跳过16个字节的数据,所以&a[0] + 1会指向下一行数据,即指向第二行,本身是个地址,占4或者8个字节。
- 9、sizeof(*(&a[0] + 1)),a[0]是第一行元素,&a[0]是取出第一行元素的地址,是数组指针,类型为int (*)[4],+1会跳过16个字节的数据,所以&a[0] + 1会指向下一行数据,即指向第二行,*(&a[0]+1)获得第二行的元素,计算第二行的字节大小,结果为16。
- 10、sizeof(*a),a保存的是首元素的地址,首元素是一维数组即第一行,*a获得第一行的元素,计算其字节大小,结果为16。
- 11、sizeof(a[3]),这里需要了解一个sizeof的特性,sizeof并不会执行其中的代码,而是只计算其类型,这里a[3]明显已经越界了,但是sizeof只计算其大小并不运行代码,所以这里的结果为16个字节。



