当前位置: 首页 > article >正文

深入C语言:指针与数组的经典笔试题剖析

1. sizeof和strlen的对比

1.1 sizeof

sizeof 是C语言中的一个操作符,用于计算变量或数据类型所占内存空间的大小,单位是字节。它不关心内存中存储的具体数据内容,只关注内存空间的大小。

#include <stdio.h>
int main()
{
    int a = 10;
    printf("%d\n", sizeof(a));  // 输出:4(int类型通常占4个字节)
    printf("%d\n", sizeof a);   // 输出:4(可以省略括号)
    printf("%d\n", sizeof(int));// 输出:4(计算int类型的大小)
    return 0;
}

在上面的代码中,sizeof(a) 和 sizeof(int) 都返回4,因为 int 类型通常占用4个字节。

1.2 strlen

strlen 是C语言标准库中的一个函数,用于计算字符串的长度。它的函数原型如下:

size_t strlen(const char *str);

 strlen 从传入的字符串指针开始,向后查找直到遇到 \0 字符为止,统计 \0 之前的字符个数。如果字符串中没有 \0strlen 会继续向后查找,可能导致越界访问。

#include <stdio.h>
#include <string.h>
int main()
{
    char arr1[3] = {'a', 'b', 'c'};  // 没有\0结尾
    char arr2[] = "abc";             // 自动添加\0
    printf("%d\n", strlen(arr1));    // 输出:不确定,arr1没有\0
    printf("%d\n", strlen(arr2));    // 输出:3
    printf("%d\n", sizeof(arr1));    // 输出:3
    printf("%d\n", sizeof(arr2));    // 输出:4(包含\0)
    return 0;
}

在上面的代码中,strlen(arr1) 的结果是不确定的,因为 arr1 没有以 \0 结尾,strlen 会继续向后查找,直到遇到 \0 为止。而 strlen(arr2) 返回3,因为 arr2 是以 \0 结尾的字符串。

1.3 sizeof和strlen的对比

sizeofstrlen
是操作符是库函数,需包含头文件 <string.h>
计算操作数所占内存的大小,单位是字节计算字符串长度,统计 \0 之前的字符个数
不关心内存中存储的数据内容关注内存中是否有 \0,如果没有 \0 可能会越界

2. 数组和指针笔试题解析

2.1 一维数组

int a[] = {1, 2, 3, 4};
printf("%d\n", sizeof(a));        // 输出:16(4个int,每个4字节)
printf("%d\n", sizeof(a + 0));   // 输出:8(a + 0是首元素地址,指针大小)
printf("%d\n", sizeof(*a));      // 输出:4(*a是首元素,int类型)
printf("%d\n", sizeof(a + 1));   // 输出:8(a + 1是第二个元素的地址,指针大小)
printf("%d\n", sizeof(a[1]));    // 输出:4(a[1]是第二个元素,int类型)
printf("%d\n", sizeof(&a));      // 输出:8(&a是整个数组的地址,指针大小)
printf("%d\n", sizeof(*&a));     // 输出:16(*&a是整个数组,sizeof(a))
printf("%d\n", sizeof(&a + 1));  // 输出:8(&a + 1是跳过整个数组后的地址,指针大小)
printf("%d\n", sizeof(&a[0]));   // 输出:8(&a[0]是首元素地址,指针大小)
printf("%d\n", sizeof(&a[0] + 1));// 输出:8(&a[0] + 1是第二个元素地址,指针大小)

2.2 字符数组

代码块1
char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
printf("%d\n", sizeof(arr));      // 输出:6(6个char,每个1字节)
printf("%d\n", sizeof(arr + 0)); // 输出:8(arr + 0是首元素地址,指针大小)
printf("%d\n", sizeof(*arr));    // 输出:1(*arr是首元素,char类型)
printf("%d\n", sizeof(arr[1]));  // 输出:1(arr[1]是第二个元素,char类型)
printf("%d\n", sizeof(&arr));    // 输出:8(&arr是整个数组的地址,指针大小)
printf("%d\n", sizeof(&arr + 1));// 输出:8(&arr + 1是跳过整个数组后的地址,指针大小)
printf("%d\n", sizeof(&arr[0] + 1));// 输出:8(&arr[0] + 1是第二个元素地址,指针大小)
代码块2 
char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
printf("%d\n", strlen(arr));      // 输出:不确定,arr没有\0
printf("%d\n", strlen(arr + 0)); // 输出:不确定,arr没有\0
printf("%d\n", strlen(*arr));    // 错误:*arr是char,不是指针
printf("%d\n", strlen(arr[1]));  // 错误:arr[1]是char,不是指针
printf("%d\n", strlen(&arr));    // 输出:不确定,arr没有\0
printf("%d\n", strlen(&arr + 1));// 输出:不确定,&arr + 1指向数组末尾之后
printf("%d\n", strlen(&arr[0] + 1));// 输出:不确定,arr没有\0

代码块3

char arr[] = "abcdef";
printf("%d\n", sizeof(arr));      // 输出:7(包含\0)
printf("%d\n", sizeof(arr + 0)); // 输出:8(arr + 0是首元素地址,指针大小)
printf("%d\n", sizeof(*arr));    // 输出:1(*arr是首元素,char类型)
printf("%d\n", sizeof(arr[1]));  // 输出:1(arr[1]是第二个元素,char类型)
printf("%d\n", sizeof(&arr));    // 输出:8(&arr是整个数组的地址,指针大小)
printf("%d\n", sizeof(&arr + 1));// 输出:8(&arr + 1是跳过整个数组后的地址,指针大小)
printf("%d\n", sizeof(&arr[0] + 1));// 输出:8(&arr[0] + 1是第二个元素地址,指针大小)
代码块4
char arr[] = "abcdef";
printf("%d\n", strlen(arr));      // 输出:6
printf("%d\n", strlen(arr + 0)); // 输出:6
printf("%d\n", strlen(*arr));    // 错误:*arr是char,不是指针
printf("%d\n", strlen(arr[1]));  // 错误:arr[1]是char,不是指针
printf("%d\n", strlen(&arr));    // 输出:6
printf("%d\n", strlen(&arr + 1));// 输出:不确定,&arr + 1指向数组末尾之后
printf("%d\n", strlen(&arr[0] + 1));// 输出:5
代码块5
char *p = "abcdef";
printf("%d\n", sizeof(p));        // 输出:8(p是指针,指针大小)
printf("%d\n", sizeof(p + 1));   // 输出:8(p + 1是指针,指针大小)
printf("%d\n", sizeof(*p));      // 输出:1(*p是char类型)
printf("%d\n", sizeof(p[0]));    // 输出:1(p[0]是char类型)
printf("%d\n", sizeof(&p));      // 输出:8(&p是指针的地址,指针大小)
printf("%d\n", sizeof(&p + 1));  // 输出:8(&p + 1是指针的地址,指针大小)
printf("%d\n", sizeof(&p[0] + 1));// 输出:8(&p[0] + 1是指针,指针大小)
代码块6
char *p = "abcdef";
printf("%d\n", strlen(p));        // 输出:6
printf("%d\n", strlen(p + 1));   // 输出:5
printf("%d\n", strlen(*p));      // 错误:*p是char,不是指针
printf("%d\n", strlen(p[0]));    // 错误:p[0]是char,不是指针
printf("%d\n", strlen(&p));      // 错误:&p是指针的地址,不是字符串
printf("%d\n", strlen(&p + 1));  // 错误:&p + 1是指针的地址,不是字符串
printf("%d\n", strlen(&p[0] + 1));// 输出:5

 2.3 二维数组

int a[3][4] = {0};
printf("%d\n", sizeof(a));        // 输出:48(3行4列,每个int占4字节)
printf("%d\n", sizeof(a[0][0])); // 输出:4(a[0][0]是int类型)
printf("%d\n", sizeof(a[0]));    // 输出:16(a[0]是第一行数组,4个int)
printf("%d\n", sizeof(a[0] + 1));// 输出:8(a[0] + 1是第二行首元素地址,指针大小)
printf("%d\n", sizeof(*(a[0] + 1)));// 输出:4(*(a[0] + 1)是第二行首元素,int类型)
printf("%d\n", sizeof(a + 1));   // 输出:8(a + 1是第二行地址,指针大小)
printf("%d\n", sizeof(*(a + 1)));// 输出:16(*(a + 1)是第二行数组,4个int)
printf("%d\n", sizeof(&a[0] + 1));// 输出:8(&a[0] + 1是第二行地址,指针大小)
printf("%d\n", sizeof(*(&a[0] + 1)));// 输出:16(*(&a[0] + 1)是第二行数组,4个int)
printf("%d\n", sizeof(*a));      // 输出:16(*a是第一行数组,4个int)
printf("%d\n", sizeof(a[3]));    // 输出:16(a[3]是第四行数组,4个int)

3. 指针运算笔试题解析

3.1  题目1

#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));  // 输出:2,5
    return 0;
}

  • *(a + 1)a 是数组首元素地址,a + 1 是第二个元素地址,*(a + 1) 是2。

  • *(ptr - 1)ptr 指向数组末尾之后,ptr - 1 是最后一个元素地址,*(ptr - 1) 是5。

 3.2 题目2

#include <stdio.h>
struct Test
{
    int Num;
    char *pcName;
    short sDate;
    char cha[2];
    short sBa[4];
} *p = (struct Test *)0x100000;
int main()
{
    printf("%p\n", p + 0x1);              // 输出:0x100014(结构体大小20字节)
    printf("%p\n", (unsigned long)p + 0x1);// 输出:0x100001(unsigned long类型加1)
    printf("%p\n", (unsigned int *)p + 0x1);// 输出:0x100004(unsigned int*类型加1)
    return 0;
}
  • p + 0x1p 是结构体指针,结构体大小为20字节,p + 1 是 0x100000 + 20 = 0x100014

  • (unsigned long)p + 0x1p 被强制转换为 unsigned long,加1后为 0x100001

  • (unsigned int *)p + 0x1p 被强制转换为 unsigned int*,加1后为 0x100004

 3.3 题目3

#include <stdio.h>
int main()
{
    int a[3][2] = {(0, 1), (2, 3), (4, 5)};
    int *p;
    p = a[0];
    printf("%d", p[0]);  // 输出:1
    return 0;
}

  • a[3][2] 的初始化使用了逗号表达式,实际初始化为 {1, 3, 5}

  • p = a[0]p[0] 是 a[0][0],值为1。

 3.4 题目4

#include <stdio.h>
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]);  // 输出:FFFFFFFC,-4
    return 0;
}

  • p 是指向 int[4] 的指针,p[4][2] 相当于 *(*(p + 4) + 2)

  • &p[4][2] - &a[4][2] 计算的是两个指针之间的元素个数差,结果为-4

 3.5 题目5

#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));
    printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));  // 输出:10,5
    return 0;
}
  • (ptr1 - 1)ptr1 指向数组末尾之后,ptr1 - 1 是最后一个元素,值为10。

  • *(ptr2 - 1)ptr2 指向第二行首元素,ptr2 - 1 是第一行最后一个元素,值为5。

 3.6 题目6

#include <stdio.h>
int main()
{
    char *a[] = {"work", "at", "alibaba"};
    char **pa = a;
    pa++;
    printf("%s\n", *pa);  // 输出:at
    return 0;
}
  • pa 是指向 a[0] 的指针,pa++ 后指向 a[1]*pa 是 "at"

 3.7 题目7

#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);  // 输出:POINT
    printf("%s\n", *--*++cpp + 3);  // 输出:ER
    printf("%s\n", *cpp[-2] + 3);  // 输出:ST
    printf("%s\n", cpp[-1][-1] + 1);  // 输出:EW
    return 0;
}
  • **++cppcpp 指向 cp[1]*cpp 是 c + 2**cpp 是 "POINT"

  • *--*++cpp + 3cpp 指向 cp[2]*cpp 是 c + 1--*cpp 是 c*--*cpp 是 "ENTER"+3 后是 "ER"


http://www.kler.cn/a/572657.html

相关文章:

  • 大语言模型(LLM)和嵌入模型的统一调用接口
  • 谈谈常用的分布式 ID 设计方案?
  • MySQL数据库只能通过localhost访问,无法通过IP访问?两步快速定位和解决
  • stm32主从机硬件IIC实现
  • Windows10下本地搭建Manim环境
  • 新品速递 | 多通道可编程衰减器+矩阵系统,如何破解复杂通信测试难题?
  • 探秘基带算法:从原理到5G时代的通信变革【四】Polar 编解码(二)
  • 【工具】COME对比映射学习用于scRNA-seq数据的空间重构
  • 文件压缩与解压工具7-Zip的安装和使用(免费)
  • 探索DeFi世界:用Python开发去中心化金融应用
  • 第5章 使用OSSEC进行监控(网络安全防御实战--蓝军武器库)
  • 【深度学习CV】【图像分类】从CNN(卷积神经网络)、ResNet迁移学习到GPU高效训练优化【案例代码】详解
  • c语言、c++怎么将string类型数据转成int,怎么将int转成string
  • 基于stm32的多旋翼无人机(Multi-rotor UAV based on stm32)
  • 软考中级-数据库-3.3 数据结构-树
  • Leetcode3270:求出数字答案
  • C#实现语音合成播报器——基于System.Speech的语音交互方案,在windows上实现语音播报指定文本
  • 请谈谈 HTTP 中的请求方法(GET、POST、PUT、DELETE等),它们的区别是什么?
  • Python----数据分析(Matplotlib四:Figure的用法,创建Figure对象,常用的Figure对象的方法)
  • alloc、malloc 与 allocator:内存管理三剑客