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
之前的字符个数。如果字符串中没有 \0
,strlen
会继续向后查找,可能导致越界访问。
#include <stdio.h>
#include <string.h>
int main()
{char arr1[3] = {'a', 'b', 'c'}; // 没有\0结尾char arr2[] = "abc"; // 自动添加\0printf("%d\n", strlen(arr1)); // 输出:不确定,arr1没有\0printf("%d\n", strlen(arr2)); // 输出:3printf("%d\n", sizeof(arr1)); // 输出:3printf("%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 |
---|---|
是操作符 | 是库函数,需包含头文件 <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,5return 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 + 0x1
:p
是结构体指针,结构体大小为20字节,p + 1
是0x100000 + 20 = 0x100014
。 -
(unsigned long)p + 0x1
:p
被强制转换为unsigned long
,加1后为0x100001
。 -
(unsigned int *)p + 0x1
:p
被强制转换为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]); // 输出:1return 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,-4return 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,5return 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); // 输出:atreturn 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); // 输出:POINTprintf("%s\n", *--*++cpp + 3); // 输出:ERprintf("%s\n", *cpp[-2] + 3); // 输出:STprintf("%s\n", cpp[-1][-1] + 1); // 输出:EWreturn 0;
}
-
**++cpp
:cpp
指向cp[1]
,*cpp
是c + 2
,**cpp
是"POINT"
。 -
*--*++cpp + 3
:cpp
指向cp[2]
,*cpp
是c + 1
,--*cpp
是c
,*--*cpp
是"ENTER"
,+3
后是"ER"
。