本文不涉及非常基础知识,但会提及经常出错的地方,知识点;
数组名:在一般情况下代表数组首元素地址,有两个例外一是sizeof(数组名)和 &数组名,此时这两种数组名的使用代表整个数组;
一、整型数组
注释:以下如有4/8这是运行平台不同,32位为4字节,64位为8字节;
大家思考以下代码打印的值(单位:字节);
#include<stdio.h>
int main()
{
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
//16---计算整个数组所占空间的大小
printf("%d\n",sizeof(a+0));
// 4/8---a代表首元素地址加零还是首元素地址,地址为4/8个字节
printf("%d\n",sizeof(*a));
//4---a代表首元素地址(*a)就是首元素,计算首元素所占空间
printf("%d\n",sizeof(a+1));
//4/8---a+1指向第二个元素地址
printf("%d\n",sizeof(a[1]));
//4---计算第二个元素所占空间
printf("%d\n",sizeof(&a));
//4/8---&a取出整个数组的地址,地址为4/8个字节
printf("%d\n",sizeof(*&a));
//16---(*&a)=(a)这两个效果相同
printf("%d\n",sizeof(&a+1));
//4/8---&a取出数组地址加1跳过整个数组
//指向数组最后一个元素的下一个元素的地址
//此处存在越界这里只是讲&a+1指向哪里
printf("%d\n",sizeof(&a[0]));
//4/8---第一个元素地址,地址为4/8个字节
printf("%d\n",sizeof(&a[0]+1));
//4/8---指向第二个元素的地址,地址为4/8个字节
return 0;
}
二、字符数组
思考以下代码的值(单位:字节);
#include<stdio.h>
int main()
{
//strlen寻找'\0'为结尾
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(&arr));
//以上三个为随机值且相同,都是将arr地址交给strlen();
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
//以上两句都为错误语句;strlen参数必须为地址;
//*arr取到arr数组第一个元素'a',将'a'传进strlen函数,
//strlen函数认为这个'a'为查找起始地址,
//此时a将转换为97则会从地址为97开始访问,但此时已经非法访问
return 0;
}
三、二维数组
二维数组的数组名是首元素地址,此时首元素地址为第一行地址;
思考以下代码的值(单位:字节);
#include<stdio.h>
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof( a ));
//48---计算整个数组的大小
printf("%d\n", sizeof( a[0][0] ));
//4---计算数组第一行第一个元素所占空间
printf("%d\n", sizeof( a[0]) );
//16---a[0]表示第一行,所以计算的是第一行所占空间的大小
printf("%d\n", sizeof( a[0]+1) );
//4/8---a[0]表示第一行,相当于数组名,数组名就是首元素地址
//所以加1就是第一行第二个元素的地址
printf("%d\n", sizeof( *(a[0]+1) ));
//4---a[0]+1表示第一行第二个元素的地址
printf("%d\n", sizeof( a+1 ));
//4/8-a是首元素地址加1指向第二行的地址,此时把二维数组抽象成一位数组
printf("%d\n", sizeof( *(a+1) ));
//16---a+1表示第二行地址,(*a+1)计算第二行元素所占空间
printf("%d\n", sizeof( &a[0]+1 ));
//4/8---&a[0]表示第一行地址,加1指向第二行地址
printf("%d\n", sizeof( *(&a[0]+1) ));
//16---&a[0]+1表示第二行地址,
//*(&a[0]+1)取出第二行,即计算第二行元素所占空间
printf("%d\n", sizeof( *a ));
//16---a表示首元素地址,即第一行地址*a计算第一行元素所占空间
printf("%d\n", sizeof( a[3] ));
//16---计算第四行所占空间,但此时已非法访问;
//注意:sizeof函数不会真实去访问内存
return 0;
}
以下是以下题目(大家认真思考哦):
第一题:
#include<stdio.h>
struct Test
{
int Num;
int *pcName;
short sDate;
}
//假设p的值为0x100000,struct Test类型变量大小20字节;
int main()
{
p = (struct Test*)0x100000;
printf("%p\n",p+1);//0x00100014
//指针加一,这个一代表多少取决于指针类型
printf("%p\n",(unsigned long) p+1);//0x00100001
//将p存储内容强制转换为unsigned long再加1
printf("%p\n",(unsigned int*) p+1);//0x00100004
//将p存储内容强制转换为unsigned int*再加1;
}
第二题:
#include<stdio.h>
int main()
{
int a[4] = { 1, 2, 3, 4};
int *ptr1 = (int*) ( &a + 1 );
int *ptr2 = (int*) ( (int)a + 1);
//将a的存储内容转换为int然后再加1,
//导致跳过一个字节最后转换为int*的指针;
printf("%x,%x",ptr1[-1],prt2);//4,2000000(十六进制输出)
//ptr1[-1]=*(ptr1-1)
//它跳过一个字节指向途中标记的位置又因为它的数据类型为int访问4字节
//褐色圈计算ptr2访问的内容,
return 0;
}
第三题:
int main()
{
int a[5][5] = {0};
int (*p)[4];
p = a;//p只能指向四个元素,却将a赋值于p
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//&a[4][2]---小伙伴肯定能看懂,它表示第五行第三个元素
//&p[4][2] (是不是看到很懵逼?)
//先说 p[4] = *( p + 4 )
// p[4][2] = *( *(p+4) + 2 (这里式子是互通的)
//由于p每次只能访问4个int类型元素(如图)
//两个地址相减等于-4
//第一个%p 内存存入的是-4的补码,%p是认为这个内容是地址直接打印
//第二个%d 有符号数,输出-4
return 0;
}
最后抛出一道题:(供大家思考,将你的答案留在评论区)
#include<stdio.h>
int main()
{
char *c[] = {"ENTER", "NEW", "POINT", "FIRST"};
char *cp[] = {c+3, c+2, c+1, c};
char ***cppp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *--*++cpp+3);
printf("%s\n", *cpp[-2] + 3);
printf("%s\n", cpp[-1][-1] + 1);
return 0;
}
不懂的评论区见哦;
记得三连哦;