1.sizeof 和strlen 的对比
1.1 sizeof
在学习操作符的时候,我们学习了sizeof, sizefo 计算变量所占用内存空间大小,单位是字节,如果操作符是类型,计算的是使用类型创建的变量所所占内存空间的大小。
sizefo 只关注占用内存空间的大小,不在乎内存中存放什么数据。
比如:
#include <stdio.h>
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);
printf("%d\n", sizeof(int));
//如果要计算的是类型的话,则必须放入括号内
return 0;
}
1.2 strlen
strlenC语言库函数,功能是求字符串长度。函数原型如下:
统计的是从strlen函数的参数str 中这个地址开始向后,'\0' 之前字符串中字符的个数。
strlen函数会一直向后找'\0'字符,直到找到为止,所以可能存在越界查找。
如果要查找的字符串中没有‘\0’字符的话,strlen就会超出参数str的数组范围,直到在内存中找到一个'\0'字符为止,但这‘\0’字符会在哪找到谁也不知道,所以当str字符串中没有\0字符时,返回值将是随机值。
int main()
{
char arr1[3] = { 'a','b','c' };
char arr2[] = "abc";
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
printf("%d\n", sizeof(arr1));
printf("%d\n", sizeof(arr1));
return 0;
}
运行结果:
1.3sizeof 和strlen 的对比
sizeof | strlen |
1.sizeof 是操作符 | 1.strlen是库函数,需要包含头文件 string.h |
2.sizeof计算操作数所占内存的大小,单位是字节 | 2.strlen是求字符串长度的,统计的是‘\0’之前字符的个数 |
3.不关注内存中存放的数据 | 3.关注内存中是否有\0,如果没有\0,就会持续往后找,可能会越界 |
sizeof 不调类型,strlen 只针对字符串
2.数组和指针笔试题解析
2.1 一维数组
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //单独数组名,代表整个数组大小,16
printf("%d\n", sizeof(a + 0));//不是单独数组名,仅代表数组首地址,4/8
printf("%d\n", sizeof(*a)); //代表数组首元素 int类型所以 4
printf("%d\n", sizeof(a + 1)); //代表数组第二个元素地址 4/8
printf("%d\n", sizeof(a[1])); //等价于*(a+1) 代表数组第二个元素 4
printf("%d\n", sizeof(&a)); // 代表整个数组的地址,但是只要是地址就是4/8
printf("%d\n", sizeof(*&a)); // 代表整个数组 16 可以理解为 *和&相互抵消了
printf("%d\n", sizeof(&a + 1)); //代表整个数组后面的地址,4/8
printf("%d\n", sizeof(&a[0])); // 代表数字第一个元素的地址 4/8
printf("%d\n", sizeof(&a[0] + 1)); //代表数组第二个元素的地址 4/8
return 0;
}
2.2字符数组
代码1:
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr)); //代表整个数组的大小 6
printf("%d\n", sizeof(arr + 0)); //代表数组首元素地址 4/8
printf("%d\n", sizeof(*arr)); //代表数组首元素 1
printf("%d\n", sizeof(arr[1])); // 代表数组第二个元素 1
printf("%d\n", sizeof(&arr)); // 代表整个数组的地址 4/8
printf("%d\n", sizeof(&arr + 1)); //代表整个数组后面的地址,4/8
printf("%d\n", sizeof(&arr[0] + 1)); //代表数组第二个元素地址 4/8
return 0;
}
代码2:
int main()
{
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr)); //因为字符串中没有'\0',所以是随机值
printf("%d\n", strlen(arr + 0)); //同上
printf("%d\n", strlen(*arr));
//因为 strlen 函数中参数类型是const char* str,
//所以该次返回不是地址,但是strlen会把它当成地址,然后去访问,但是a转成整数 97 0x97是不允许用户访问的
//所以会出错
printf("%d\n", strlen(arr[1])); //同上
printf("%d\n", strlen(&arr)); //随机值
printf("%d\n", strlen(&arr + 1)); // arr字符串整个地址后面一个地址,然后去找'\0',随机值,但是应该比前面小
printf("%d\n", strlen(&arr[0] + 1)); //在第二个元素地址后面去找'\0',随机值
return 0;
}
代码3:
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); //代表整个数组的大小 6+1('\0')
printf("%d\n", sizeof(arr + 0)); // 代表数组首元素的地址 4/8
printf("%d\n", sizeof(*arr)); //等价于 sizeof a 所以返回值 1
printf("%d\n", sizeof(arr[1])); //同上 1
printf("%d\n", sizeof(&arr)); //代表整个数组的地址 4/8
printf("%d\n", sizeof(&arr + 1)); //代表整个数组地址后一个地址 4/8
printf("%d\n", sizeof(&arr[0] + 1)); //代表数组第二个元素的地址 4/8
return 0;
}
代码4:
int main()
{
char arr[] = "abcdef"; //字符串这样赋值最后会有一个隐形的'\0'
printf("%d\n", strlen(arr)); // 6
printf("%d\n", strlen(arr + 0)); //传入数组首元素地址往后找'\0',6
printf("%d\n", strlen(*arr)); //数组首元素 出错
printf("%d\n", strlen(arr[1])); //出错
printf("%d\n", strlen(&arr)); //6
printf("%d\n", strlen(&arr + 1)); //整个数组地址之后的一个地址 随机值
printf("%d\n", strlen(&arr[0] + 1)); //从数组第二个元素地址开始找'\0',5
return 0;
}
代码5:
int main()
{
char* p = "abcdef";
//将字符串"abcdef"的数组首地址赋给指针p
printf("%d\n", sizeof(p)); //因为p不是数组 只是一个指针,所以返回值是4/8
printf("%d\n", sizeof(p + 1)); //第二个元素的地址 4/8
printf("%d\n", sizeof(*p)); // 相当于 sizeof(a) 1
printf("%d\n", sizeof(p[0])); // 同上 1
printf("%d\n", sizeof(&p)); //整个数组指针的大小 4/8
printf("%d\n", sizeof(&p + 1)); // 代表整个数组指针之后的地址 4/8
printf("%d\n", sizeof(&p[0] + 1)); //代表数组第二个元素地址 4/8
return 0;
}
代码6:
int main()
{
char* p = "abcdef";
printf("%d\n", strlen(p)); //6
printf("%d\n", strlen(p + 1)); // 5
//printf("%d\n", strlen(*p)); // 出错
//printf("%d\n", strlen(p[0])); // 出错
printf("%d\n", strlen(&p)); //从指针本身地址开始找'\0',所以是随机值
printf("%d\n", strlen(&p + 1)); // 从指针本身地址之后的后移一个地址开始找,随机值
printf("%d\n", strlen(&p[0] + 1)); // 从第二个元素地址开始找 5
return 0;
}
2.3二维数组
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); // 代表整个数组大小 48
printf("%d\n", sizeof(a[0][0])); // 代表数组a[0][0]元素的大小 4
printf("%d\n", sizeof(a[0]));
// 我们之前说过二维数组,每个参数其实就是几个一维数组的数组名,
//这里所以相等于数组a[0]的数组名 所以是16
printf("%d\n", sizeof(a[0] + 1)); //相当于数组a[0]整个数组后一个地址,也就是第二个一维数组的首元素地址, 4/8
//单个数组名才是代表整个数组 a[0], a[1] a[2]
printf("%d\n", sizeof(*(a[0] + 1))); // 同上,相等于第二个一维数组的首元素 4
printf("%d\n", sizeof(a + 1)); //这里 a相等于a[0]和上面第四个同理 4/8
printf("%d\n", sizeof(*(a + 1))); //*(a+1) 等价于 a[1] 所以是第二个一维数组名的数组名 16
printf("%d\n", sizeof(&a[0] + 1)); // 相等于第二个一维数组的数组的地址 4/8
printf("%d\n", sizeof(*(&a[0] + 1))); // *(&((*(a+0))+1)) &和* 可以抵消相等于 *(a+1) == a[1] 就是第二个数组的数组名 16
printf("%d\n", sizeof(*a));// 相等于*(a+0) 也就是a[0] 16
printf("%d\n", sizeof(a[3])); //
// 数组虽然没有第四个一维数组,但是sizeof 并不在意我们所传入值的内从,它只管计算,a[3]位置的地址肯定有的,然后通过a本身的范围
// 所以这里它还是以数组第四个一维数组名计算
//数组第四个一维数组的数组名 16
return 0;
}
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3.除此之外所有的数组名都表示首元素的地址
3.指针运算笔试题解析
3.1题目1
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1); // 将a整个数组之后的数组地址强转为(int*) 赋值给ptr
printf("%d,%d", *(a + 1), *(ptr - 1));
// *(a+1) 数组第二个元素, ptr-1 为 a整个数组之后的地址向前移动一个(int) 大小, 就是数组最后一个元素位置
// 所以最后输出就是 2,5
return 0;
}
3.2题目2:
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结构是啥?
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);
// 代表 指针向后移动1个类型,Test 结构体类型大小是14,0x1就等于1
printf("%p\n", (unsigned long)p + 0x1);
//将地址转成 无符号长整形+1 然后当成地址输出,
printf("%p\n", (unsigned int*)p + 0x1);
// 降低至转成无符号整型指针类型,然后向后移动一个类型,所以就是+4
return 0;
}
运行结果:
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,3,5 分别存进了a[0][0],a[0][1],a[0][2]
// 所以这里输出是 1 本体需要细心观察
return 0;
}
int main()
{
int a[3][2] = { {0, 1 }, {2,3}, {4,5} };
int* p;
p = a[0];
printf("%d", p[0]);
//p指针指向a[0]也就是a数组的第一行,然后p[0]就等价于 a[0][0] 所以是0
return 0;
}
3.4 题目4:
//假设环境是x86环境,程序输出的结果是啥?
#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]);
//p=a 代表p的头地址等于a的头地址,因为p是数组指针且大小为4,所以p[4][2]代表在a数组的地址上后移4次p个地址大小,然后的第2个地址
//即 p+4*4+2 的位置,减去a[4][2]的结果输出地址,和整数(也就是之间隔多少个地址)
//最后输出是FFFFFFFC -4的补码,内存中都是存储的补码
// -4
return 0;
}
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));
//prt1等于整个数组之后的一个地址,ptr-1 则是数组中最后一个元素 ——10
//ptr2 则是二位数组中第二个一维数组的数组名 ptr2-1 则是第一个一维数组的最后一个元素——5
// 10,5
return 0;
}
3.6题目6
#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
char** pa = a; // *pa="work" **pa="w"
pa++; // pa++ 相等于a++ 如果要指向o的话则要 *pa++
printf("%s\n", *pa); // at
return 0;
}
3.7 题目7
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };
char** cp[] = { c + 3,c + 2,c + 1,c };
char*** cpp = cp;
printf("%s\n", **++cpp); // *(++cpp)= c+2 再取数之后 就是PINOINT
// POINT
printf("%s\n", *-- * ++cpp + 3); //+3运算级最低,最后计算
// ER
printf("%s\n", *cpp[-2] + 3); // cpp[-2]等价于 *(cpp-2)
// ST
printf("%s\n", cpp[-1][-1] + 1); // cpp[-1][-1] 等价于 *(*(cpp-1)-1)
///EW
return 0;
}