sizeof和strlen区别
对比项 | sizeof | strlen |
---|---|---|
本质 | 操作符 | 库函数,使用需包含头文件 string.h |
功能 | 计算操作数所占内存的大小,单位是字节 | 求字符串长度,统计的是从当前地址向后中找, \0 之前字符的个数 |
关注点 | 不关注内存中存放什么数据 | 关注内存中是否有 \0 ,若没有就会持续往后找,可能会越界 |
数组和指针笔试题
== 数组名的意义:==
- sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩。
- &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。
- 除此之外所有的数组名都表⽰⾸元素的地址。
以下答案均已64位系统为标准(指针变量8字节)
整形数组
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main() {
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //单个数组名整个数组,4*4=16
printf("%d\n", sizeof(a + 0));
// a作为表达式使用时,会隐式转换为指向数组首元素的指针,8!!!
printf("%d\n", sizeof(*a));
// a作为表达式使用时,会隐式转换为指向数组首元素的指针,解引用就得到首元素,4
printf("%d\n", sizeof(a + 1));
// // a作为表达式使用时,会隐式转换为指向数组首元素的指针,+1指向数组第二个元素的指针,8
printf("%d\n", sizeof(a[1])); // 数组元素,4
printf("%d\n", sizeof(&a)); // 整个数组的地址,8
printf("%d\n", sizeof(*&a)); // 整个数组,4*4=16
printf("%d\n", sizeof(&a + 1)); // 指向数组末尾之后的地址,同样是指针8
printf("%d\n", sizeof(&a[0])); // 首元素地址,8
printf("%d\n", sizeof(&a[0] + 1)); // 指向数组第二个元素的地址,8
return 0;
}
字符数组
#include <stdio.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是首元素地址,8
printf("%d\n", sizeof(*arr)); // *arr是首元素,1
printf("%d\n", sizeof(arr[1])); // arr[1]是第二个元素,1
printf("%d\n", sizeof(&arr)); // &arr是数组的地址,8
printf("%d\n", sizeof(&arr+1)); // &arr+1是跳过整个数组后的地址,8
printf("%d\n", sizeof(&arr[0]+1)); // &arr[0]+1是第二个元素的地址,8
return 0;
}
#include <stdio.h>
#include <string.h>
int main() {
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr)); // 因为数组无'\0',会越界找'\0',结果是未定义的
printf("%d\n", strlen(arr+0)); // 同上面,arr+0是首元素地址,结果未定义
// 下面代码有错误,strlen需要的是指针参数,而不是字符
// printf("%d\n", strlen(*arr));
// printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr)); // &arr是数组地址,结果未定义,因为无'\0'
printf("%d\n", strlen(&arr+1)); // &arr+1是跳过数组后的地址,结果未定义
printf("%d\n", strlen(&arr[0]+1)); // &arr[0]+1是第二个元素地址,结果未定义
return 0;
}
结论: strlen关注内存中是否有 \0
,若没有会一直往后找,结果未定义。
#include <stdio.h>
int main() {
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); // 字符串包含'\0',有7个元素,7
printf("%d\n", sizeof(arr+0)); // arr+0是首元素地址,8
printf("%d\n", sizeof(*arr)); // *arr是首元素,1
printf("%d\n", sizeof(arr[1])); // arr[1]是第二个元素,1
printf("%d\n", sizeof(&arr)); // &arr是数组的地址,8
printf("%d\n", sizeof(&arr+1)); // &arr+1是跳过整个数组后的地址,8
printf("%d\n", sizeof(&arr[0]+1)); // &arr[0]+1是第二个元素的地址,8
return 0;
}
#include <stdio.h>
#include <string.h>
int main() {
char arr[] = "abcdef";
printf("%d\n", strlen(arr)); // 字符串长度是6,不包含'\0'
printf("%d\n", strlen(arr+0)); // arr+0是首元素地址,长度是6
// 下面三行代码有错误,strlen需要的是指针参数,而不是字符
// printf("%d\n", strlen(*arr));
// printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr)); // &arr是数组地址,长度是6
printf("%d\n", strlen(&arr+1)); // &arr+1是跳过数组后的地址,结果未定义
printf("%d\n", strlen(&arr[0]+1)); // &arr[0]+1是第二个元素地址,长度是5
return 0;
}
#include <stdio.h>
int main() {
char *p = "abcdef";
printf("%d\n", sizeof(p)); // p是指针,8
printf("%d\n", sizeof(p+1)); // p+1是第二个字符地址,8
printf("%d\n", sizeof(*p)); // *p是首字符,1
printf("%d\n", sizeof(p[0])); // p[0]是首字符,1
printf("%d\n", sizeof(&p)); // &p是指针p的地址,8
printf("%d\n", sizeof(&p+1)); // &p+1是跳过指针p后的地址,8
printf("%d\n", sizeof(&p[0]+1)); // &p[0]+1是第二个字符地址,8
return 0;
}
#include <stdio.h>
#include <string.h>
int main() {
char *p = "abcdef";
printf("%d\n", strlen(p)); // 字符串长度是6,不包含'\0'
printf("%d\n", strlen(p+1)); // p+1是第二个字符地址,长度是5
// 下面三行代码有错误,strlen需要的是指针参数,而不是字符
// printf("%d\n", strlen(*p));
// printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p)); // &p是指针p的地址,结果未定义
printf("%d\n", strlen(&p+1)); // &p+1是跳过指针p后的地址,结果未定义
printf("%d\n", strlen(&p[0]+1)); // &p[0]+1是第二个字符地址,长度是5
return 0;
}
二维数组
#include <stdio.h>
int main() {
int a[3][4] = {0};
printf("%d\n", sizeof(a));// a 代表整个二维数组,12 * 4 = 48
printf("%d\n", sizeof(a[0][0]));// a[0][0] :二维数组的第一个元素,4
printf("%d\n", sizeof(a[0])); // a[0] 代表二维数组的第一行,一行4元素,16
printf("%d\n", sizeof(a[0] + 1));
// a[0] 作为表达式使用时,会隐式转换为指向第一行首元素的指针
// a[0] + 1 则是指向第一行第二个元素的指针, 8 字节
printf("%d\n", sizeof(*(a[0] + 1)));
// *(a[0] + 1) 是对指向第一行第二个元素的指针进行解引用,得到的是该元素本身, 4
printf("%d\n", sizeof(a + 1));
// a 作为表达式使用时,会隐式转换为指向二维数组首行的指针
// a + 1 则是指向二维数组第二行的指针,8
printf("%d\n", sizeof(*(a + 1)));
// *(a + 1) 是对指向二维数组第二行的指针进行解引用,得到的是第二行,16
printf("%d\n", sizeof(&a[0] + 1));
// &a[0] 是取第一行的地址,&a[0] + 1 则是指向下一行的指针,8
printf("%d\n", sizeof(*(&a[0] + 1)));
// 9. sizeof(*(&a[0] + 1))
// *(&a[0] + 1) 是对指向第二行的指针进行解引用,得到的是第二行,16
printf("%d\n", sizeof(*a));
// a 作为表达式使用时,会隐式转换为指向二维数组首行的指针,解引用,得到的是第一行,16
printf("%d\n", sizeof(a[3]));
// sizeof 是在编译时计算的,不会实际访问数组元素
// a[3] 可看作是一个包含 4 个 int 元素的一维数组,所以 sizeof(a[3]) 的结果为 4 * 4 = 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;
}
//程序的结果是什么?
//a+1指向第二个元素地址,解引用就是2
//&a指整个数组地址,&a+1就是指向整个数组后一个位置,用int*强转后,再-1,就只是回退了一个整形,指向了数组最后一个数,5
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p;
p = a[0];//p类型为int*,指向第一行第一个元素的地址
printf("%d", p[0]);
return 0;
}
//1.逗号表达式的运算规则是从左到右依次计算每个表达式的值,最后整个逗号表达式的值是最后一个表达式的值。
//2.所以(0, 1) 的值是 1,(2, 3) 的值是 3,(4, 5) 的值是 5。
//3.那么数组 a 实际的初始化情况就相当于 int a[3][2] = { 1, 3, 5 };
//a[0] 是二维数组 a 第一行的首地址,这里将其赋值给指针 p ,也就是让 p 指向了 a[0][0] 。
//p[0] 等价于 *(p + 0) ,也就是 p 所指向的元素,也就是 a[0][0] ,其值为 1。
//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
int main()
{
int a[5][5];
int(*p)[4]; //数组指针
p = a; //指向a前四个元素构成的数组
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
//计算 &p[4][2]:
//p 是指向包含 4 个 int 元素的一维数组的指针,
//p[4] 相当于 * (p + 4),也就是让 p 向后移动 4 个包含 4 个 int 元素的一维数组,总共移动了 4 * 4 = 16 个 int 元素的位置。
//p[4][2] 则是在 p[4] 所指向的一维数组里偏移 2 个 int 元素的位置,
//所以 & p[4][2] 相对于 p 起始位置偏移了 16 + 2 = 18 个 int 元素的位置。
//计算& a[4][2]:
//& a[4][2] 相对于 a 起始位置偏移了 4 * 5 + 2 = 22 个 int 元素的位置。
//指针相减的结果是两个指针之间相差的元素个数,所以 &p[4][2] - &a[4][2] = 18 - 22 = -4。
//在计算机中,负数是以补码形式存储的,-4 的 32 位补码是 0xFFFFFFFC。
//综上所述,程序的输出结果是 0xFFFFFFFC,-4。
#include <stdio.h>
int main()
{
int aa[2][5] = {
1, 2, 3, 4, 5,
6, 7, 8, 9, 10
};
int* ptr1 = (int*)(&aa + 1); //&aa取得是整个二维数组地址,+1就指向了整个二维数组末尾后一个元素的地址
int* ptr2 = (int*)(*(aa + 1)); //aa这里再表达式中就隐式转换为指向第一行数组,+1就是指向第二行数组
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
//*(ptr1 - 1):回退一个元素,就指向10
//*(ptr2 - 1):会退一个元素就指向5
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a;// pa 指向了 a[0]
pa++;
printf("%s\n", *pa);
return 0;
}
//由于 pa 是一个指向 char* 类型的指针,
//pa++ 会让 pa 向后移动一个 char* 类型的大小,也就是让 pa 指向 a[1]。
//*pa 是对 pa 进行解引用操作,得到的是 pa 所指向的内容 "at"
#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;
}
c+3:c[3]的地址,即 “FIRST”
c+2:c[2]的地址,即 “POINT”
c+1:c[1]的地址,即 “NEW”
c:c 指向 c[0](即 “ENTER”)
cpp:指向cp[0]
== **++cpp:point==
++cpp:指向cp[1]
一次解引用就是cp[1]本身,c+2
两次解引用,就是c[2],point
*-- * ++cpp + 3:ER
++cpp:将 cpp 再向后移动一个位置,此时 cpp 指向 cp[2]。
- cpp:对 cpp 解引用,得到 cp[2],也就是 c + 1。
– * cpp:对 * cpp 进行减 1 操作, * cpp 原本指向 c[1],减 1 后指向 c[0]。- – * ++cpp:对-- * cpp 解引用,得到 c[0]。
- – * ++cpp + 3:将指向 “ENTER” 的指针向后移动 3 个位置,指向字符 ‘E’。
所以 printf(“%s\n”, *-- * ++cpp + 3); 输出 “ER”。
*cpp[-2] + 3:“ST”
cpp[-2]:等价于* (cpp - 2),此时 cpp 指向 cp[2],cpp - 2 指向 cp[0](即 c + 3,指向 “FIRST”)。
- cpp[-2]:对 cpp[-2] 解引用,得到 c[3],指向字符串 “FIRST”。
- cpp[-2] + 3:将指向 “FIRST” 的指针向后移动 3 个位置,指向字符 ‘S’。
cpp[-1][-1] + 1:“EW”
cpp[-1]:等价于* (cpp - 1),此时 cpp 指向 cp[2],cpp - 1 指向 cp[1](即 c + 2,指向 “POINT”)。
cpp[-1][-1]:等价于 * ((cpp - 1) - 1), * (cpp - 1) 得到 c + 2, * (cpp - 1) - 1 得到 c + 1(即 “NEW”), * ((cpp - 1) - 1) 得到 c[1],指向字符串 “NEW”。
cpp[-1][-1] + 1:将指向 “NEW” 的指针向后移动 1 个位置,指向字符 ‘E’。