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

1. sizeof和strlen的对比

1.1 sizeof

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

#include 
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 
#include 
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的对比

sizeof strlen
是操作符 是库函数,需包含头文件 
计算操作数所占内存的大小,单位是字节 计算字符串长度,统计 \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 
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 
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 
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 
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 
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 
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 
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"

你可能感兴趣的:(C语言编程启航,算法)