C语言:指针和数组(看完拿捏指针和数组)
目录
以上就是个人学习见解和学习的解析,欢迎各位大佬在评论区探讨!
感谢大佬们的一键三连! 感谢大佬们的一键三连! 感谢大佬们的一键三连!
数组名的理解:
数组名是数组首元素的地址;
但是有2个例外:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节;
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
代码中的4/8指的是在32位或者X86环境下位4个字节,在X64环境下是8个字节。
一维数组:
#include <stdio.h>
int main()
{
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
return 0;
}
解析:
1、sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节。
2、a不是单独放在sizeof()内部,也没有取地址,所以a就是首元素的地址,a+0还是首元素的地址,是地址大小就是4/8个字节。
3、*a中的a是数组首元素的地址,*a就是对首元素的地址解引用,找到的就是首元素,首元素的大小就是4个字节。
4、这里的a是数组首元素的地址,a+1是第二个元素的地址,sizeof(a+1)就是地址的大小
5、计算的是第二个元素的大小。
6、&a取出的数组的地址,数组的地址也就是个地址。
7、&a----> int( * )[ 4 ], *&a----->int [ 4 ]。
8、&a取出的是数组的地址,&a--> int( * )[ 4 ],&a+1是从数组a的地址向后跳过一个(4个整型元素)数组的大小,&a+1还是地址,是地址就是4/8个字节。
9、&a[ 0 ]就是第一个元素的地址,计算的是地址的大小,是地址就是4/8个字节。
10、&a[ 0 ]+1就是第二个元素的地址,大小是4/8个字节,&a[ 0 ]+1---> &a[ 1 ] 。
字符数组:
#include <stdio.h>
int main()
{
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
return 0;
}
解析:
1、//数组名单独放在sizeof内部,这里的arr表示整个数组,计算的是整个数组的大小,单位是字节,总共6个字节类型是char [6]。
2、//arr表示数组首元素的地址,arr+0还是数组首元素的地址,是地址就是4/8个字节,类型是char*。
3、//arr表示数组首元素的地址,*arr就是首元素,大小1个字节,类型是char。
4、//arr[1]就是第二个元素,大小是1个字节。
5、//&arr是数组的地址,但是数组的地址也是地址,是地址就是4/8。
6、//&arr + 1是跳过整个数组后的地址,是地址就是4/8个字节。
7、//第二个元素的地址,是4/8个字节。
#include <stdio.h>
int main()
{
char arr[] = {'a','b','c','d','e','f'};
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));//随机值
return 0;
}
解析:
1、因为字符数组arr中没有‘ ’,所以在求字符串长度的时候,会一直往后找,产生的结构就是随机值。
2、arr + 0是首元素的地址,和第一个一样,也是随机值。
3、//错误, arr是数组首元素的地址,*arr就是数组首元素,就是'a'-97;
//strlen函数参数的部分需要传一个地址,当我们传递的是'a'时,'a'的ASCII码值是97,那就是将97作为地址传参。
//strlen就会从97这个地址开始统计字符串长度,这就非法访问内存了。4、//错误
5、//&arr是数组的地址,数组的地址和数组首元素的地址,值是一样的,那么传递给strlen函数后,依然是从数组的第一个元素的位置开始往后统计。
6、//随机值
7、//&arr[0] + 1是第二个元素的地址。结果也是随机值。
字符串数组:
#include <stdio.h>
int main()
{
char arr[] = "abcdef";// a b c d e f
printf("%dn", sizeof(arr));//7
printf("%dn", sizeof(arr+0));//4
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
return 0;
}
解析:
1、//7,类型是char [7]。
2、//arr + 0是首元素的地址。
3、//*arr其实就是首元素,1个字节,理解为*arr--> *(arr+0) -- arr[0]。
4、//arr[1]是第二个元素,1个字节。
5、//&arr是数组的地址,是地址就是4/8个字节。
6、//&arr + 1是跳过一个数组的地址,4/8。
7、//&arr[0] + 1是第二个元素的地址 4/8。
#include <stdio.h>
int main()
{
char arr[] = "abcdef";
printf("%dn", strlen(arr));//6
printf("%dn", strlen(arr+0));//6
printf("%dn", strlen(*arr));//错误
printf("%dn", strlen(arr[1]));//错误
printf("%dn", strlen(&arr));//6
printf("%dn", strlen(&arr+1));//随机值
printf("%dn", strlen(&arr[0]+1));//5
return 0;
}
解析:
由于与上面内容相同,就不做过多解释
1、//6
2、//6
3、//err
4、//err
5、//6
6、//随机值
7、//5
一级指针:
#include <stdio.h>
int main()
{
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
return 0;
}
解析:
1、//p是一个指针变量,大小就是4/8。
2、//p+1是'b'的地址,是地址大小就是4/8个字节。
3、//*p 就是'a'元素,就是1个字节。
4、//p[0]--> *(p+0) --> *p 1个字节。
5、//4/8,类型是&p -- char**。
6、//4/8
7、//4/8 , &p[0] + 1得到是'b'的地址 。
#include <stido.h>
int main()
{
char *p = "abcdef";
printf("%dn", strlen(p));//6
printf("%dn", strlen(p+1));//5
printf("%dn", strlen(*p));//错误
printf("%dn", strlen(p[0]));//错误
printf("%dn", strlen(&p));//随机值
printf("%dn", strlen(&p+1));//随机值
printf("%dn", strlen(&p[0]+1));//5
return 0;
}
解析:
由于与上面内容相同,就不做过多解释
1、//6
2、//5
3、//err
4、//err
5、//随机值
6、//随机值
7、//5
二维数组:
#include <stdio.h>
int main()
{
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));//4/8
printf("%dn",sizeof(*(&a[0]+1)));//16
printf("%dn",sizeof(*a));//16
printf("%dn",sizeof(a[3]));//16
return 0;
}
解析:
1、//3*4*4 = 48
2、//4
3、//a[0]是第一行这个一维数组的数组名,数组名算是单独放在sizeof内部了,计算的是整个数组的大小,大小是16个字节。
4、//a[0]作为第一行的数组名,没有单独放在sizeo内部,没有&,a[0]表示数组首元素的地址,也就是a[0][0]的地址,所以a[0]+1是第一行第二个元素的地址,是地址就是4/8个字节。
5、//4,计算的是就是第一行第2个元素的大小。
6、//4 / 8,a是数组首元素的地址,是第一行的地址 int(*)[4],a+1 就是第二行的地址。
7、//16,*(a+1) --> a[1] -> sizeof(*(a+1))->sizeof(a[1]) 计算的是第二行的大小,a+1 --> 是第二行的地址,int(*)[4],*(a+1) 访问的第二行的数组。
8、//4/8,&a[0]是第一行的地址 int(*)[4],&a[0]+1 是第二行的地址 int(*)[4]。
9、//16 计算的是第二行的大小。
10、//计算的是第一行的大小-16,a是数组首元素的地址,就是第一行的地址,*a 就是第一行,*a --> *(a+0) --> a[0]。
11、//16,类型是a[3]--> int [4]。
指针笔试题:
题一:一维数组
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
注意:&a+1的类型为int( * )[ 5 ],强制转换为int( * ).此时每次移动的就是4个字节,而不是20个字节了!
题二: 结构体指针
#include <stdio.h>
//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%pn", p + 0x1);
printf("%pn", (unsigned long)p + 0x1);
printf("%pn", (unsigned int*)p + 0x1);
return 0;
}
注意:整型就是正常的加减运算,指针类型就需要加减n*类型的大小。
题三: 一维数组
#include <stdio.h>
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf( "%x,%x", ptr1[-1], *ptr2);
return 0;
}
注意: a强制转换成int类型+1就是正常加减,最后转换为指针int计算机会向后查找四个字节。
题四: 二维数组
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
注意: 注意二维数组中没有用{ }把数值包含,这里用的是( )是逗号表达式,取最后一个值为元素;a[ 0 ]可以看做&a[ 0 ][ 0 ].
题五: 二维数组
#include <stdio.h>
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf( "%p,%dn", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
注意:地址相减为相差的元素个数 ,此题为-4,因为求的是地址,所以需要将-4转换成补码输出。
题六: 二维数组
#include <stdio.h>
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
注意:*(aa+1) = a[1] = &a[1][0]。
题七:指针数组
#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%sn", *pa);
return 0;
}
注意:这里的a是指针数组,数组中每个位置的类型为char*。
题八: 指针数组(多级)
#include <stdio.h>
int main()
{
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
printf("%sn", **++cpp);
printf("%sn", *--*++cpp+3);
printf("%sn", *cpp[-2]+3);
printf("%sn", cpp[-1][-1]+1);
return 0;
}
注意:有了上面的基础,这道题一步一步来,你们也可以解释清楚!!!
以上就是个人学习见解和学习的解析,欢迎各位大佬在评论区探讨!
感谢大佬们的一键三连! 感谢大佬们的一键三连! 感谢大佬们的一键三连!