C语言--指针和数组面试题的解析

本文深入探讨了一维数组、字符数组、字符串数组及二维数组在C语言中的使用方法与特性,通过具体示例展示了不同场景下数组与指针操作的细节。

一维数组

int a[] = { 1,2,3,4 };

  • printf("%d\n", sizeof(a)); //数组a的字节大小:16
  • printf("%d\n", sizeof(a + 0));//数组首元素地址a[0]的地址的大小:4/8,x86占4个字节,x64占八个字节
  • printf("%d\n", sizeof(*a)); //a==(a+0),a[0]的字节大小:4
  • printf("%d\n", sizeof(a + 1));//数组首元素的下一个元素(即a[1]地址所占的字节长度:)4/8
  • printf("%d\n", sizeof(a[1])); //元素a[1]所占字节的大小 :4
  • printf("%d\n", sizeof(&a)); //数组a的地址所占的字节大小:4/8
  • printf("%d\n", sizeof(*&a)); //&a是整个数组的地址,*解引用得到的是整个数组的元素,所以真个数组的大小占16个字节
  • printf("%d\n", sizeof(&a + 1)); //&a表示的是整个数组地址的大小,+1表示指向数组后面的地址,所占字节为:4/8
  • printf("%d\n", sizeof(&a[0])); //&a[0]表示数组的第一个元素的地址所占的字节:4/8
  • printf("%d\n", sizeof(&a[0] + 1)); //&a[0]+1表示数组中a[1]的地址所占的字节:4/8
#include<stdio.h>
int main() {
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));    //数组a的字节大小:16
	printf("%d\n", sizeof(a + 0));//数组首元素地址a[0]的地址的大小:4/8,x86占4个字节,x64占八个字节
	printf("%d\n", sizeof(*a));   //*a==*(a+0),a[0]的字节大小:4
	printf("%d\n", sizeof(a + 1));//数组首元素的下一个元素(即a[1]地址所占的字节长度:)4/8
	printf("%d\n", sizeof(a[1])); //元素a[1]所占字节的大小 :4
	printf("%d\n", sizeof(&a));	  //数组a的地址所占的字节大小:4/8
	printf("%d\n", sizeof(*&a));  //&a是整个数组的地址,*解引用得到的是整个数组的元素,所以真个数组的大小占16个字节
	printf("%d\n", sizeof(&a + 1));     //&a表示的是整个数组地址的大小,+1表示指向数组后面的地址,所占字节为:4/8
	printf("%d\n", sizeof(&a[0]));		//&a[0]表示数组的第一个元素的地址所占的字节:4/8
	printf("%d\n", sizeof(&a[0] + 1));	//&a[0]+1表示数组中a[1]的地址所占的字节:4/8
	

	return 0;
}

字符数组

char arr[]={‘a’,‘b’,‘c’,‘d’,‘e’,’'f};
在内存中显示如下:
1

  • printf("%d\n", sizeof(arr)); //显示数组的所占的大小:6
  • printf("%d\n", sizeof(arr + 0)); //arr[0]所在地址所占字节的大小:4/8
  • printf("%d\n", sizeof(*arr)); //arr==(arr+0)显示数组的第一个元素的所占字节的大小:1
  • printf("%d\n", sizeof(arr[1])); //数组中第二个元素所占字节的大小:1
  • printf("%d\n", sizeof(&arr)); //数组arr的地址所占的字节:4/8
  • printf("%d\n", sizeof(&arr + 1)); //数组arr的地址后面的地址所占的字节:4/8
  • printf("%d\n", sizeof(&arr[0] + 1));//数组元素arr[1]的地址所占的字节元素:4/8
  • printf("%d\n", strlen(arr)); //随机值 由于数组后面不是以’\0’为结尾,strlen直到’\0’才计数结束,所以产生随机值
  • printf("%d\n", strlen(arr + 0)); //从arr[0]开始计数,也是产生的随机值
  • printf("%d\n", strlen(*arr)); //arr==(arr+0)==arr[0]=‘a’,ascii 码为:97 会产生报错
  • printf("%d\n", strlen(arr[1])); //arr[1]=‘b’,ascii 码为:98 会产生报错
  • printf("%d\n", strlen(&arr)); //随机值
  • printf("%d\n", strlen(&arr + 1)); //随机值
  • printf("%d\n", strlen(&arr[0] + 1)); //随机值
#include<stdio.h>
#include<string.h>
int main() {
	


char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));        //显示数组的所占的大小:6
	printf("%d\n", sizeof(arr + 0));    //arr[0]所在地址所占字节的大小:4/8
	printf("%d\n", sizeof(*arr));       //*arr==*(arr+0)显示数组的第一个元素的所占字节的大小:1
	printf("%d\n", sizeof(arr[1]));     //数组中第二个元素所占字节的大小:1
	printf("%d\n", sizeof(&arr));       //数组arr的地址所占的字节:4/8
	printf("%d\n", sizeof(&arr + 1));   //数组arr的地址后面的地址所占的字节:4/8
	printf("%d\n", sizeof(&arr[0] + 1));//数组元素arr[1]的地址所占的字节元素:4/8
	printf("%d\n", strlen(arr));        //随机值 由于数组后面不是以'\0'为结尾,strlen直到'\0'才计数结束,所以产生随机值
	printf("%d\n", strlen(arr + 0));    //从arr[0]开始计数,也是产生的随机值
	//	printf("%d\n", strlen(*arr));		//*arr==*(arr+0)==arr[0]='a',ascii 码为:97 会产生报错
	//	printf("%d\n", strlen(arr[1]));		//arr[1]='b',ascii 码为:98 会产生报错
	printf("%d\n", strlen(&arr));		//随机值
	printf("%d\n", strlen(&arr + 1));	//随机值
	printf("%d\n", strlen(&arr[0] + 1)); //随机值


	return 0;
}

字符串数组

char arr[]={“abcdef”};

2

  • printf("%d\n", sizeof§);//p表示的是指针变量 4/8
  • printf("%d\n", sizeof(p + 1));//p+1表示字符b的地址,所占字节:4/8
  • printf("%d\n", sizeof(*p)); //p==(p+0)表示字符a,字符所占字节为:1
  • printf("%d\n", sizeof(p[0]));//p[0]==*(p+0),表示字符a,所占字节为:1
  • printf("%d\n", sizeof(&p));//表示指向p的指针的地址的指针的地址,所占字节为:4/8
  • printf("%d\n", sizeof(&p + 1));//指向地址:4/8
  • printf("%d\n", sizeof(&p[0] + 1));//字符b的地址指向的所占的字节:4/8;
  • printf("%d\n", strlen§); //字符串的字符为6
  • printf("%d\n", strlen(p + 1));//从b开始,字符串的长度为5
  • printf("%d\n", strlen(*p));//*p表示’a’,产生错误
  • printf("%d\n", strlen(p[0]));//产生错误
  • printf("%d\n", strlen(&p)); //随机值
  • printf("%d\n", strlen(&p + 1)); //随机值
  • printf("%d\n", strlen(&p[0] + 1)); //5 &p[0]==*&(p+0)=p 所以长度为5
#include<stdio.h>
int main() {

	const char *p = "abcdef";
	printf("%d\n", sizeof(p));//p表示的是指针变量 4/8
	printf("%d\n", sizeof(p + 1));//p+1表示字符b的地址,所占字节:4/8
	printf("%d\n", sizeof(*p)); //*p==*(p+0)表示字符a,字符所占字节为:1
	printf("%d\n", sizeof(p[0]));//p[0]==*(p+0),表示字符a,所占字节为:1
	printf("%d\n", sizeof(&p));//表示指向p的指针的地址的指针的地址,所占字节为:4/8
	printf("%d\n", sizeof(&p + 1));//指向地址:4/8
	printf("%d\n", sizeof(&p[0] + 1));//字符b的地址指向的所占的字节:4/8;
	

	printf("%d\n", strlen(p)); //字符串的字符为6
	printf("%d\n", strlen(p + 1));//从b开始,字符串的长度为5
	printf("%d\n", strlen(*p));//*p表示'a',产生错误
	printf("%d\n", strlen(p[0]));//产生错误
	printf("%d\n", strlen(&p)); //随机值
	printf("%d\n", strlen(&p + 1)); //随机值
	printf("%d\n", strlen(&p[0] + 1)); //5 &p[0]==*&(p+0)=p	所以长度为5
	return 0;

}

二维数组

  • printf("%d\n", sizeof(a)); //表示整个数组所占字节的大小:48
  • printf("%d\n", sizeof(a[0][0])); //首元素的大小所占的字节的大小:4
  • printf("%d\n", sizeof(a[0])); //首行元素所占字节的大小:16
  • printf("%d\n", sizeof(a[0] + 1)); //第二行元素所占字节的大小:16
  • printf("%d\n", sizeof(*(a[0] + 1))); //表示第一行第二个元素的所占字节的大小:4
  • printf("%d\n", sizeof(a + 1));//数组名a,并没有单独放在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));//a表示首元素,a==(a+0)==a[0],表示第一行元素所占字节的大小:16
#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
	printf("%d\n", sizeof(a[0] + 1)); //第二行元素所占字节的大小:16
	printf("%d\n", sizeof(*(a[0] + 1))); //表示第一行第二个元素的所占字节的大小:4
	printf("%d\n", sizeof(a + 1));//数组名a,并没有单独放在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));//a表示首元素,*a==*(a+0)==a[0],表示第一行元素所占字节的大小:16
	
	return 0;

}

常见的面试题

#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;
}

1
根据上述的内存空间图可以看出:*(a+1)=a[1], *(ptr-1)=a[4];
所以打印的结果为:2 5

#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);
	printf("%s\n", *--*++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}

这段代码还是比较有难度的,我们可以进行画图进行更容易的理解:(注意:这里的箭头我省略了,因为自己画图实在太难看。。。)这里是刚刚初始时各级指针的指向。

1
当代码执行打印printf("%s\n", **++cpp) 时: cp先自增再使用,所以现在的指针指向为:
2
所以此时此举打印的内容为:POINT

当执行第二句打印函数的时候:**printf("%s\n", ++cpp + 3); 我们继续画图方便理解:
可知cpp首先再自增,然后解引用,事得cp再自减,其次再解引用,让原来的c的原有值再+3:画图得到:

3
所以此时打印的字符串的结果是:ER。

继续分析下一句打印的内容:printf("%s\n", cpp[-2] + 3);
我们可以看出 cpp[-2]==
(cpp-2),此时指针回退两步,(注意:此时的cpp没有发生自增或自减,所以cpp的值没有发生改变),解引用后,使cp中的内容在原来的基础上+3,画图可得:

3
此时打印的字符串为:ST.

终于快要画完图了 哈哈哈,斗志昂扬的分析最后一个打印的函数:printf("%s\n", cpp[-1][-1] + 1);

cpp[-1][-1]=*( *(cpp-1)-1),我们上个打印函数的时候提到,cpp在自增自减的时候之才能发生改变,所以cpp指针依旧指向cp的第三块内存地址:
在这里插入图片描述

可知打印的字符串的结果为:EW

#include <stdio.h>
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
}

同样,我们根据画内存空间的方式来理解此段程序:

初始时,内存图的结构如下:
在这里插入图片描述

运行pa++后:
在这里插入图片描述

所以*pa的打印的字符串为:at

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;
}

在这里插入图片描述
所以打印结果为:10 和5

int main()
{
    int a[5][5];
    int(*p)[4];
    p = a;
    printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    return 0;
}

在这里插入图片描述

而指针-指针等于两个地址之间的元素,所以当打印:&p[4][2] - &a[4][2]的整形的时候:值为:-4
而打印地址的形式的时候:-4的原码为:1000 0000 0000 0000 0000 0000 0100
反码为:1111 1111 1111 1111 1111 1111 1111 1011
补码为:1111 1111 1111 1111 1111 1111 1111 1100
所以打印的地址形式为:FF FF FF FC

#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;
 }

在这里插入图片描述

所以打印结果为:1

struct Test
{
 int Num;
 char *pcName;
 short sDate;
 char cha[2];
 short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
 printf("%p\n", p + 0x1);
 printf("%p\n", (unsigned long)p + 0x1);
 printf("%p\n", (unsigned int*)p + 0x1);
 return 0;
}

结构体指针大小为20个字节,p是一个指向结构体的指针,p加1跳过二十个字节,所以 p + (0x1) =0x10014 。第二个是把结构体指针强制类型转换为(unsigned long)的数,对它加1,就直接给这个数加1,结果为0x100001。第三个是把结构体指针强制类型转换为(unsigned int*)类型,对它加1,偏移4个字节,结果为0x100004

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值