一、关于数组名
数组名是数组首元素的地址,但是有2个例外:
1. sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节
2. &数组名 - 数组名也表示整个数组,取出的是整个数组的地址
除了这个2个例外,你见到的所有的数组名都表示首元素的地址
二、一维数组
int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a));//16,a作为数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节 printf("%d\n", sizeof(a + 0));//a并非单独放在sizeof内部,也没有&,所以数组名a就是数组首元素的地址 //a+0还是数组首元素的地址,是地址大小就是 4/8 个字节 printf("%d\n", sizeof(*a));//a是首元素的地址,*a就是首元素,sizeof(*a)就算的就是首元素的大小 - 4 //a - int* //*a - int printf("%d\n", sizeof(a + 1));//a是首元素的地址,a+1是第二个元素的地址,sizeof(a+1)计算的是指针的大小 - 4/8 //a - int* //a+1, 跳过一个int printf("%d\n", sizeof(a[1]));//a[1]就是数组的第二个元素,sizeof(a[1])的大小 - 4个字节 printf("%d\n", sizeof(&a));//&a取出的数组的地址,数组的地址,也是地址呀,sizeof(&a)就是 4/8 个字节 printf("%d\n", sizeof(*&a));//&a是数组的地址,是数组指针类型,*&a是都数组指针解引用,访问一个数组的大小 //16字节 //sizeof(*&a) ==> sizeof(a) =16 printf("%d\n", sizeof(&a + 1));//&a数组的地址,&a+1跳过整个数组,&a+1还是地址,是 4/8 个字节 printf("%d\n", sizeof(&a[0]));//a[0]是数组的第一个元素,&a[0]是第一个元素的地址,是 4/8 个字节 printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一个元素的地址,&a[0]+1就是第二个元素的地址,是 4/8 个字节 //&a[0] - int* //&a[0]+1 -> &a[1]
三、字符数组
关于sizeof 与 strlen
sizeof 是计算对象或者类型创建的对象所占内存空间的大小,单位是字节
sizeof 是操作符,不是函数
strlen 求字符串长度的,计算的是字符串中\0之前出现的字符的个数
统计到\0为止,如果没有看到\0,会继续往后找
strlen 是库函数 , 访问的实质是地址
ps:地址就是指针,指针就是地址
char arr[] = { 'a','b','c','d','e','f' }; printf("%d\n", strlen(arr));//随机值,arr是数组名,但是没有放在sizeof内部,也没&,arr就是首元素的地址 //strlen得到arr后,从arr数组首元素的地方开始计算字符串的长度,直到直到\0,但是arr数组中没有\0,arr内存的后边是否有\0,在什么位置 //是不确定的,所以\0之前出现了多少个字符是随机的。 printf("%d\n", strlen(arr + 0));//arr是数组首元素的地址,arr+0还是首元素的地址,随机值 printf("%d\n", strlen(*arr));//arr是数组首元素的地址,*arr 是首元素 - ‘a’ - 97 //strlen就把‘a’的ASCII码值 97 当成了地址 //err 会非法访问内存 printf("%d\n", strlen(arr[1]));//arr[1] - 'b' - 98 - err printf("%d\n", strlen(&arr));//随机值,&arr是数组的地址,数组的地址也是指向数组起始位置,和第一个案例一样 printf("%d\n", strlen(&arr + 1));//随机值 printf("%d\n", strlen(&arr[0] + 1));//随机值 printf("%d\n", sizeof(arr));//arr是数组名,并且是单独放在sizeof内部,计算的是数组总大小,单位是字节 - 6 printf("%d\n", sizeof(arr + 0));//arr是数组名,并非单独放在sizeof内部,arr表示首元素的地址,arr+0还是首元素的地址 //是地址大小就是4/8 printf("%d\n", sizeof(*arr));//arr是首元素的地址,*arr就是首元素,sizeof计算的是首元素的大小,是1字节 printf("%d\n", sizeof(arr[1]));//arr[1]是数组的第二个元素,sizeof(arr[1])计算的是第二个元素的大小,1个字节 printf("%d\n", sizeof(&arr));//&arr- 取出的是数组的地址,sizeof(&arr))计算的是数组的地址的大小,是地址就是4/8字节 printf("%d\n", sizeof(&arr + 1));//&arr是数组的地址,&arr+1跳过整个数组,指向'f'的后边,&arr+1的本质还是地址,是地址就是4/8字节 printf("%d\n", sizeof(&arr[0] + 1));//&arr[0]是‘a’的地址,&arr[0]+1是'b'的地址,是地址就是4/8字节
char arr[] = "abcdef"; printf("%d\n", sizeof(arr));//7 arr单独放在sizeof内部,表示整个数组,计算整个数组总大小,‘\0’也要算 printf("%d\n", sizeof(arr + 0));// 4/8 arr没有单独放在sizeof内部,表示首元素地址,+0还是首元素地址,地址就是指针 printf("%d\n", sizeof(*arr));//1 arr没有单独放在sizeof内部,*对arr解引用,表示首元素 printf("%d\n", sizeof(arr[1]));//1 首元素大小 printf("%d\n", sizeof(&arr));//4/8 arr取地址,表示整个数组的地址,是指针 printf("%d\n", sizeof(&arr + 1));//4/8 调过整个数组,但仍是指针 printf("%d\n", sizeof(&arr[0] + 1));//4/8 表示第二个元素的地址,为指针 printf("%d\n", strlen(arr));//6 字符串长度 printf("%d\n", strlen(arr + 0));//6 arr表示首元素地址,+0仍是首元素地址,计算的仍是整个字符串的长度 printf("%d\n", strlen(*arr));//err *arr表示首元素,将arr的ASCI值 97 当做地址,非法访问 printf("%d\n", strlen(arr[1]));//err 同上 printf("%d\n", strlen(&arr));//6 表示整个字符串数组的地址 printf("%d\n", strlen(&arr + 1));//suiji 调过整个数组,但不能确保 ‘\0’位置 printf("%d\n", strlen(&arr[0] + 1));//5 第二个元素地址
char* p = "abcdef"; printf("%d\n", sizeof(p));//p是指针变量,存放字符串的首元素地址,指针 printf("%d\n", sizeof(p + 1));//4/8 p+1指向第二个元素的地址,仍是指针 printf("%d\n", sizeof(*p));//1 表示首元素a printf("%d\n", sizeof(p[0]));//1 p[0]->*(p+0)->*p printf("%d\n", sizeof(&p));//4/8 表示指针变量p的地址,地址就是指针 printf("%d\n", sizeof(&p + 1));//4/8 表示跳过整个p //值得注意的是&p 与&p+1 大小没有直接关系,因为不能保证p的地址中是否含有‘\0' //假如p的地址为0x 0012ff40 ‘\0’表示0,那么 00就表示‘\0' printf("%d\n", sizeof(&p[0] + 1));//4/8 printf("%d\n", strlen(p));//6 printf("%d\n", strlen(p + 1));//5 printf("%d\n", strlen(*p));//err printf("%d\n", strlen(p[0]));//err printf("%d\n", strlen(&p));// 随机值 表示p的地址,相当于是一个二级指针,当无法确定 '\0 '的位置 printf("%d\n", strlen(&p + 1));//随机值 printf("%d\n", strlen(&p[0] + 1)); //5
四、二维数组
int a[3][4] = { 0 }; printf("%d\n", sizeof(a));//a是二维数组的数组名,数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节 //48 printf("%d\n", sizeof(a[0][0]));//a[0][0]是一个整型元素,大小是4个字节 printf("%d\n", sizeof(a[0]));//把二维数组的每一行看做一维数组的时候,a[0]是第一行的数组名,第一行的数组名单独放在sizeof内部 //计算的是第一行的总大小,单位是字节 - 16 printf("%d\n", sizeof(a[0] + 1));//a[0]虽然是第一行的数组名,但是并非单独放在sizeof内部 //a[0]作为第一行的数组名并非表示整个第一行这个数组,a[0]就是第一行首元素的地址,a[0]--> &a[0][0] - int* //a[0]+1,跳过一个int,是a[0][1]的地址 4/8字节 printf("%d\n", sizeof(*(a[0] + 1)));//a[0]+1是第一行第二个元素的地址,所以*(a[0]+1)就是a[0][1],大小是4个字节 printf("%d\n", sizeof(a + 1));//a是二维数组的数组名,没单独放在sizeof内部,也没有&,所以a就是数组首元素的地址 //二维数组,我们把它想象成一维数组,它的第一个元素就是二维数组的第一行 //a就是第一行的地址,a+1 是第二行的地址,是地址,大小就是 4/8 个字节 //a - &a[0] //a+1 - &a[1] //a+2 - &a[2] printf("%d\n", sizeof(*(a + 1)));//a+1是第二行的地址,*(a+1) 找到的就是第二行,sizeof(*(a + 1))计算的就是第二行的大小 //16 //*(a+1) --> a[1] //sizeof(*(a + 1)) --> sizeof(a[1]) // printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一行的地址,&a[0]+1就是第二行的地址,sizeof(&a[0] + 1)计算的第二行地址大小 //单位是字节 - 4/8 printf("%d\n", sizeof(*(&a[0] + 1)));//&a[0] + 1是第二行的地址,*(&a[0] + 1)拿到的就是第二行,大小就是16个字节 //*(&a[0]+1) --> a[1] printf("%d\n", sizeof(*a));//a表示首元素的地址,就是第一行的地址 - &a[0] //*a - 拿到的就是第一行 - 大小就是16个字节 //*a -> *(a+0) -> a[0] // printf("%d\n", sizeof(a[3]));//代码没问题 //a[3]是二维数组的第4行,虽然没有第四行,但是类型能够确定,大小就是确定的。大小就是一行的大小,单位是字节 - 16 //能够分析出 a[3]的类型是:int [4] printf("%d\n", sizeof(*( &a)));//48
总结:
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址
五、指针笔试题
笔试题一:
int main() { int a[5] = { 1, 2, 3, 4, 5 }; int* ptr = (int*)(&a + 1); printf("%d,%d", *(a + 1), *(ptr - 1));//2,5 a表示首元素地址,地址加一调过一个整型,指向第二个元素地址 //取地址a,获得整个数组地址,加一调过整个数组,指向5的后面的地址,强制类型转换(不强有警告)。ptr减一,向后移动一个元素 return 0; }
笔试题二:
struct Test { int Num; char* pcName; short sDate; char cha[2]; short sBa[4]; }*p;//p是一个结构体变量指针 //假设p 的值为0x100000。 如下表表达式的值分别为多少? //已知,结构体Test类型的变量大小是20个字节 int main() { p = (struct Test*)0x100000;//p的值为16进制数字,需强制类型转换为指针类型 printf("%p\n", p + 0x1);//00100014,p为结构体指针,+1跳过一个结构体类型 printf("%p\n", (unsigned long)p + 0x1);//00100001,强制类型转换为长整型,整型加一表示数值加一 printf("%p\n", (unsigned int*)p + 0x1);// 00100004 强制类型转化为整型,整型指针加一表示调过一个整型,加4 return 0; }
笔试题三:
笔试题四:
int main() { int a[3][2] = { (0, 1), (2, 3), (4, 5) };//带‘()’的表达式最后一个值为表达式的解, //则a的实际值为:{{1,3},{5,0},{0,0}} int* p; p = a[0]; printf("%d", p[0]);//p[0]此时表示第一行首元素地址,在sizeof中表示第一行地址 return 0; }
笔试题五:
int main() { int a[5][5]; int(*p)[4];//p的每行有4个元素,p[4][2]指向a中的第四行第三个元素, //相减得到字符个数的相反数-4 p = a; printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); //指针相减得到的是中间的字符个数,将-4以地址形式打印 return 0; }
笔试题六:
int main() { int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int* ptr1 = (int*)(&aa + 1);//获得整个数组地址,加一跳过整个数组; //ptr-1指针大小减一,对地址解引用指向10 int* ptr2 = (int*)(*(aa + 1));//aa表示首行元素地址,加一表示第二行首元素地址; //*ptr减一向后移动一个指针,指向5所在的地址 printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10,5 return 0; }
笔试题七:
int main() { char* c[] = { "ENTER","NEW","POINT","FIRST" };//c为指针变量,c中的元素类型为char* char** cp[] = { c + 3,c + 2,c + 1,c };//cp为指针变量,cp中的元素类型为char*,cp的类型为char** char*** cpp = cp;//cpp为指针变量,cpp中的元素类型为char**,cpp的类型为char*** printf("%s\n", **++cpp);//POINT printf("%s\n", *-- * ++cpp + 3);//ER printf("%s\n", *cpp[-2] + 3);//ST printf("%s\n", cpp[-1][-1] + 1);//EW return 0; }