C语言——指针和数组的一些题解

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言


这里主要是给出一些在面试时有可能会遇到的一些小题目。

一、数组部分

我们先看看下面的一个数组,猜测一下会输出什么:

//一维数组 
int a[] = {1,2,3,4}; 
printf("%d\n",sizeof(a)); 
printf("%d\n",sizeof(a+0)); 
printf("%d\n",sizeof(*a)); 
printf("%d\n",sizeof(a+1)); 
printf("%d\n",sizeof(a[1])); 
printf("%d\n",sizeof(&a)); 
printf("%d\n",sizeof(*&a)); 
printf("%d\n",sizeof(&a+1)); 
printf("%d\n",sizeof(&a[0])); 
printf("%d\n",sizeof(&a[0]+1)); 

下面是答案和一些解析

printf("%d\n"sizeof(a));//16
//**sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节**
printf("%d\n"sizeof(a + 0));//4
//a不是单独放在sizeof内部,也没有取地址,所以a就是首元素的地址,a+0还是首元素的地址//是地址,大小就是4/8个字节
printf("%d\n"sizeof(*a));//4
//*a中的a是数组首元素的地址,*a就是对首元素的地址解引用,找到的就是首元素
//首元素的大小就是4个字节
printf("%d\n"sizeof(a + 1));//4/8
//这里的a是数组首元素的地址
//a+1是第二个元素的地址
//sizeof(a+1)就是地址的大小
printf("%d\n"sizeof(a[1]));//4
//计算的是第二个元素的大小
printf("%d\n"sizeof(&a));//4/8//&a取出的数组的地址,数组的地址,也就是个地址
printf("%din"sizeof(*&a));//16
//&a----> int(*)[4]//&a拿到的是数组名的地址,类型是 int(*)[4],是一种数组指针
//数组指针解引用找到的是数组
//*&a --->~a
//2.另一种理解
//&和*抵消了
//*&a --->a
printf("%d\n"sizeof(&a + 1));//4/8
//&a取出的是数组的地址//&a--> int(*)[4]
//&a+1 是从数组a的地址向后跳过了一个 (4个整型元素的) 数组的大小
//&a+1还是地址,是地址就是4/8字节/
printf("%d\n"sizeof(&a[0]));//4/8
//&a[]就是第一个元素的地址
//计算的是地址的大小
printf("%d\n"sizeof(&a[0] + 1));//4/8
//&a[e]+1是第二个元素的地址
//大小是4/8个字节
//&a[0]+1 ---> &a[1]

我们再来看看另一个字符数组的:

在这之前先说明一下:

strlen是求字符串长度的,关注的是字符串中的\0,计算的是\0之前出现的字符的个数
strlen是库勇数,只针对宁符串
sizeof只关注占用内存空间的大小,不在乎内存中放的是什么
sizeof是操作符
#include<stdio.h>
#include<string.h>
int main(){
    char arr[] = {'a',' b','c','d','e','f'};
    printf("%d\n",strlen(arr));//随机值
    printf("%d\n",strlen(arr+0));//随机值
   // printf("%d\n",strlen(*arr));//--> strlen('a');-->strlen(97);//野指针
   // printf("%d\n",strlen(arr[1]));//-->strlen("b')-->strlen(98);
    printf("%d\n",strlen(&arr));//随机值
    printf("%d\n",strlen(&arr + 1));//随机值-6
    printf("%d\n",strlen(&arr[0] + 1));//随机值-1
//下面稍作一些改变
  char arr1[]="abcdef";
  //[a b c d e f \0]
     printf("%d\n",sizeof(arr1));//7
	printf("%d\n",,sizeof(arr1 + 0));//4/8
	printf("%d\n",sizeof(*arr1));//1
	printf("%d\n",sizeof(arr1[1]));//1
	printf("%d\n",sizeof(&arr1));//4/8
	printf("%d\n",sizeof(&arr1 + 1));//4/8
	printf("%d\n",sizeof(&arr1[0] + 1));//4/8
    return 0;
}

在这里插入图片描述
注释掉的两个是会导致程序运行错误的,他会报一个Segmentation fault(段错误),这是一种常见的程序错误,通常在C或C++等编程语言中出现。 它表示程序试图访问不允许的内存位置,或者试图操作未分配给程序的内存区域。 当程序访问了未分配的内存、已释放的内存、越界访问数组或字符串等场景时,可能会导致段错误。我们也可以叫这为野指针导致的错误。野指针就是指针指向的位置不可知的。(随机的、不正确的、没有明确限制的)

这里我们再看一个二维数组,进一步体会一下:

  int a[3][4] ={ 0 };
  printf("%d\n",sizeof(a));//3*4*4=48,这里计算的是整个数组的大小,3行4列的整形
  printf("%d\n",sizeof(a[0][0]));//这里算的是数组读一个元素的大小,整形,4个字节
  printf("%d\n",sizeof(a[0]));//16,a[0],是二维数组的第一行,这里是算的第一行的大小,4*4=16
  //a[0]是第一行这个一维数组的数组名,单独放在sizeof内部,a[0]表示第一个整个这个一维数组
  //sizeof(a[0])计算的就是第一行的大小
  printf("%d\n",sizeof(a[0] + 1));
  //a[0]1并没有单独放在sizeof内部,也没取地址,al01就表示首元素的地址
  //就是第一行这个一维数组的第一个元素的地址,a[] + 就是第一行第二个元素的地址printf("%d\n",sizeof(*(a[] + 1)));
  //a[0] + 1就是第一行第二个元素的地址
  //*(a[0] + 1))就是第一行第二个元素
  printf("%d\n",sizeof(a + 1));//4/8
  //a虽然是二维数组的地址,但是并没有单独放在sizeof内部,也没取地址
  //a表示首元素的地址,二维数组的首元素是它的第一行,a就是第一行的地址
  //a+1就是跳过第一行,表示第二行的地址
  
  printf("%d\n",sizeof(*(a + 1)));//16
  //*(a + 1)是对第二行地址的解引用,拿到的是第二行
  //*(a+1)-->a[1]
  //sizeof(*(a+1))-->sizeof(a[1])
  printf("%d\n",sizeof(&a[0] + 1));//4/8
  //&a[0] - 对第一行的数组名取地址,拿出的是第一行的地址
  //&a[0]+1 - 得到的是第二行的地址
  printf("%d\n",sizeof(*(&a[0] + 1)));//16
  //这里参考上面,是对第二行地址的解引用,拿到的是第二行
  printf("%d\n",sizeof(*a));//16
//a表示首元素的地址,就是第一行的地址
//*a就是对第一行地址的解引用,拿到的就是第一行
  printf("%d\n",sizeof(a[3]));//16
  //这里我们拿的是第四行,虽然没有,但是他的类型是定下来的了,是一行有四个整形,这就和printf("%d\n",sizeof(a[0]))一样,都是16,这里只是计算一行的大小。

    return 0;

知道并理解上面的输出,就应该对数组有比较清楚的一些理解了。

二、指针部分

接下来我们再看看指针部分:
阅读一下下面的代码:

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int *ptr = (int *)(&a + 1);
    printf( "%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}

如下图,我们的ptr指针指向了a数组+1的地址,那么指针ptr-1,就是元素5的地址了,另外,数组名表示的是首元素地址,那么首元素地址加一就是第二个元素的地址,所以上面代码的运行结果是**2和5.**
在这里插入图片描述
我们再看看另一个代码,是关于一个结构体指针的:



#include<stdio.h>
struct Test 
{ 
int Num; 
char *pcName; 
short sDate; 
char cha[2]; 
short sBa[4]; 
}*p=struct Test*)0x100000; 
//假设p 的值为0x100000。 如下表表达式的值分别为多少? 
//已知,结构体Test类型的变量大小是20个字节 
int main() 
{ 
printf("%p\n", p + 0x1); 
//这里的话,p的地址是0x100000,加1就是跳过一个结构体的大小20,
//0x100000+20-->0x100014,所以输出0x100014。
printf("%p\n", (unsigned long)p + 0x1); 
//这里把p强制类型转换成无符号的long类型,0x100000的整形是1048576,
//1048576+1->1048577,也就是0x100004
printf("%p\n", (unsigned int*)p + 0x1); 
//这里的话把p转换成了int*,类型,那么它加1就是加一个整形指针的的大小,就是4,
//所以0x100000+4->0x100004
return 0; 
}

再看一个数组的

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

在这里插入图片描述
解析:关于ptr1,我们定义了一个指针 ptr1,并将 &a + 1 赋值给它。在这里,&a 表示数组 a 的地址,加 1 后就是跳过一整个数组的地址,指针指向了数组 a 的后一个位置。然后,我们将 (int *) 用于类型转换,将指针的类型从数组指针转换为整型指针。那么后面的ptr1[-1],也就减去一个整形的位置,那么就指向4了。关于ptr2,我们看下图,假设a的地址为0x0012ff40,我们把他强制转换成int类型加1,那么就变成了0x0012ff41,由于原数组中每个整形占据四个字节,具体的存储如下,那么0x0012ff41指向的就是01后面的00的地址(存储时1---->0x00 00 00 01),然后我们将 (int *) 用于类型转换,将指针的类型从数组指针转换为整型指针。那么对ptr2解引用*ptr2,那么他就应该会指向一个整形,四个字节,又因为我们这个是小端存储模式,那么它读取出来的就是20 00 00 00
在这里插入图片描述

总结

通过这几个小题,应该会对数组和指针有了初步的认识,对于学习和面试应该会有小小的帮助,后面还有一些比较绕的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值