【c语言】指针和数组笔试题解析
一维数组:
//数组名a如果既不单独放在sizeof()中,也不与&结合,那么就表示数组首元素的大小
//a一般表示数组首元素地址,只有两种情况表示整个数组,sizeof(arr)表示整个数组的大小,&arr表示数组的地址
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));//16
printf("%d\n", sizeof(a + 0));//注意:此时a是首元素地址;结果:4/8
printf("%d\n", sizeof(*a));//4
printf("%d\n", sizeof(a + 1));//注意:此时a是第二个元素地址;结果:4/8
printf("%d\n", sizeof(a[1]));//4
printf("%d\n", sizeof(&a));//4/8 注意:是地址
printf("%d\n", sizeof(*&a));//16 注意:&a是数组指针,对数组指针解引用访问一个数组的大小
printf("%d\n", sizeof(&a + 1));//4/8
printf("%d\n", sizeof(&a[0]));//4/8
printf("%d\n", sizeof(&a[0] + 1));//4/8
字符数组:
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));//6
printf("%d\n", sizeof(arr + 0));//4/8
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8
printf("%d\n", sizeof(&arr + 1));//4/8
printf("%d\n", sizeof(&arr[0] + 1));//4/8
printf("%d\n", strlen(arr));//随机数
printf("%d\n", strlen(arr + 0));//随机数
//printf("%d\n", strlen(*arr));//err
//printf("%d\n", strlen(arr[1]));//err
//arr是首元素的地址,*arr就是首元素,站在strlen的角度,认为传参进去的'a'-97就是地址,97作为地址直接进行访问就是非法访问
//strlen不能传元素,只能传地址
printf("%d\n", strlen(&arr));//随机数
printf("%d\n", strlen(&arr + 1));//随机数
printf("%d\n", strlen(&arr[0] + 1));//随机数
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7 注意:sizeof()计算字符串大小,有'\0'是需要计算'\0'
printf("%d\n", sizeof(arr + 0));//4/8
printf("%d\n", sizeof(*arr));//1
printf("%d\n", sizeof(arr[1]));//1
printf("%d\n", sizeof(&arr));//4/8
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
printf("%d\n", strlen(*arr));//err
printf("%d\n", strlen(arr[1]));//err
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr + 1));//随机数 注意:&arr+1跳过整个数组,指向的位置何时出现'\0'未知
printf("%d\n", strlen(&arr[0] + 1));//5
char* p = "abcdef";
printf("%d\n", sizeof(p));//4/8
printf("%d\n", sizeof(p + 1));//4/8
printf("%d\n", sizeof(*p));//1
printf("%d\n", sizeof(p[0]));//1
printf("%d\n", sizeof(&p));//4/8
printf("%d\n", sizeof(&p + 1));//4/8
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));//随机数
printf("%d\n", strlen(&p + 1));//随机数
printf("%d\n", strlen(&p[0] + 1));//5
//注意:p,p+1,&p[0]是地址(指针),p[0]是元素,&p,&p+1是二级指针,strlen()中存放指针,计算该指针指向的对象'\0'前的元素个数;如果是二级指针,则计算指向的一级指针'\0'前的元素个数,未知!
二维数组:
//注意:二维数组是一维数组的数组,这个思想非常重要
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]是第一行这个一维数组的数组名,数组名单独放在了sizeof内部,计算整个一维数组的大小
printf("%d\n", sizeof(a[0] + 1));//4/8 注意:a[0]是一维数组的数组名,没有单独放在sizeof中,表示一维数组首元素的地址,+1表示第二个元素的地址
printf("%d\n", sizeof(*(a[0] + 1)));//4 第一行的一维数组的第二个元素
printf("%d\n", sizeof(a + 1));//4/8
printf("%d\n", sizeof(*(a + 1)));//16 第二行大小
printf("%d\n", sizeof(&a[0] + 1));//4/8 第二行一维数组的地址
printf("%d\n", sizeof(*(&a[0] + 1)));//16 第二行一维数组的大小
printf("%d\n", sizeof(*a));//16 第一行的大小
printf("%d\n", sizeof(a[3]));//16
//注意:不会越界,sizeof()中一旦有表达式,就能够确定类型,sizeof(表达式)在编译器编译时就能根据类型计算大小,不需要sizeof真的访问a
//a[3]==a[0]
//int[4]==int[4]
//表达式有两个属性:类型属性,值属性
//sizeof使用的是表达式的类型属性
指针练习题:
练习1:
int main()
{int a[5] = { 1, 2, 3, 4, 5 };int* ptr = (int*)(&a + 1);printf("%d,%d", *(a + 1), *(ptr - 1));return 0;
}
//程序的结果是什么?
// 2 5
练习2:
#include <stdio.h>
//由于还没学习结构体,这里告知结构体的大小是20个字节
//x86环境下演示
struct Test
{int Num;char* pcName;short sDate;char cha[2];short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{p = (struct Test*)0x100000;printf("%p\n", p + 0x1);//注意:0x1是16进制,实际上就是1;结构体指针+1,就是跳过一个结构体(20个字节);p是16进制,20的16进制是14,结果:00100014//x86环境下,32位地址,地址大小是4字节,16进制打印有8位printf("%p\n", (unsigned long)p + 0x1);//将地址强转为整型,整型加1就是加1,以%p格式打印,结果:00100001printf("%p\n", (unsigned int*)p + 0x1);//结果:00100004//注意:以%p格式打印时,前面的0不省略return 0;
}
练习3:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{int a[4] = { 1, 2, 3, 4 };int* ptr1 = (int*)(&a + 1);int* ptr2 = (int*)((int)a + 1);//元素:小端存储:01 00 00 00,02 00 00 00//首元素地址强转为整型int后+1,再强转为int*,表示地址向后跳过一个字节//指向的元素在内存中为00 00 00 02,实际为02000000(16进制)//以%x格式打印,前面的0可以省略printf("%x,%x", ptr1[-1], *ptr2);//4 2000000return 0;
}
练习4:
#include <stdio.h>
int main()
{int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式// 1 3 5//a[3][2]={{1,3},{5,0},{0,0}};int* p;p = a[0];//数组名表示首元素地址,p==&a[0][0]printf("%d", p[0]);//p[0]==*(p+0)==*p==a[0][0]//1return 0;
}
练习5:
#include <stdio.h>
int main()
{int a[5][5];int(*p)[4];p = a;//a-int (*)[5]//p-int (*)[4]//注意:类型不同的指针访问数组的方式不同printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//FFFFFFFC,-4return 0;
}
指针相减得到两指针间的元素个数且数组在内存中从低地址向高地址存储:&p[4][2]-&a[4][2]=-4
-4:
原码:10000000000000000000000000000100
反码:1111111111111111111111111111111111011
补码:1111111111111111111111111111111111100(内存中存储)
以%p格式打印:补码即地址(x82—32位—4字节—8个十六进制位):FFFFFFFC
以%d格式打印:-4
练习6:
#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));//*(aa + 1)==aa[1]==第二行一维数组首元素的地址(注意这个重要变换)/首元素的地址本身就是整型指针,int*强转是迷惑printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10,5return 0;
}
练习7:
#include <stdio.h>
int main()
{char* a[] = { "work","at","alibaba" };//字符指针数组:a的元素时字符指针,分别指向"work","at","alibaba"的首个字符char** pa = a;pa++;printf("%s\n", *pa);//atreturn 0;
}
练习8:
#include <stdio.h>
int main()
{char* c[] = { "ENTER","NEW","POINT","FIRST" };char** cp[] = { c + 3,c + 2,c + 1,c };char*** cpp = cp;printf("%s\n", **++cpp);//注意:前置后置++/--的优先级都高于解引用//地址++,即指向的元素跳过一个//解引用得到c+2,指向c[2],再解引用得到c+2指向的元素,即POINTprintf("%s\n", *-- * ++cpp + 3);//关系运算符的优先级低于解引用和自增操作符//++cp是在上一个cp的前提下++//解引用得到c+1,自减操作得到c,再解引用得到c指向的元素ENTER,+3向后移3个字符,得到ERprintf("%s\n", *cpp[-2] + 3);//*cpp[-2]==*(*(cp-2))==FIRST,+3得到ST//注意:cp-2,cp的值不变printf("%s\n", cpp[-1][-1] + 1);//cpp[-1][-1]==*(*(cpp-1)-1)==ENTER//+1得到EWreturn 0;
}
结果:
POINT
ER
ST
EW