文章目录
sizeof 和strlen
sizeof
第一次接触sizeof
是在C语言基础学习中的操作符章节,我们知道:sizeof
可以计算白能量所占内存空间的大小,单位十字街,如果操作舒适类型的话,激素那的是使用类型创建的变得所占空间的的大小
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof a);
printf("%d\n", sizeof(int));
return 0;
}
strlen
strlen
是C语言的库函数,功能是求字符串长度,函数原型如下
size_t strlen ( const char * str );
统计的是从strlen
函数的参数str
中这个地址开始向后,\0
之前字符串中字符的个数。strlen
函数会⼀直向后找\0
字符,直到找到为⽌,所以可能存在越界查找。
比较
- sizeof是一个操作符,而strlen是一个库函数
- sizeof计算的是操作数的占内存空间的大小,单位是字节,strlen计算字符串的长度
- sizeof不关注内存中的数据,strlen关注内存中是否存在\0
数组和指针妙题
一维数组
//一维数组
int main()
{
int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a)); //16
//数据名表示数组首元素的地址,只有两种特殊情况,1是放在sizeof内部表示整个数组,2是&数组名表示整个数组的地址
printf("%d\n", sizeof(a + 0)); //4/8
//a表示首元素的地址 一个地址在内存中所占大小就是4/8
printf("%d\n", sizeof(*a)); //4
//a表示数组首元素地址,解引用表示首元素
printf("%d\n", sizeof(a + 1)); //4/8
//表示第二个元素的地址
printf("%d\n", sizeof(a[1])); //4
//第一个元素
printf("%d\n", sizeof(&a)); //4/8
//&a表示取出了整个数组的地址,不过数组的地址也只是地址,是地址就是4/8个字节
printf("%d\n", sizeof(*&a)); //16
//理解1:*&相互抵消,和第一句代码一样
//理解2:&a是数组的地址,类型是:int(*)[4] 解引用访问的就是整个数组,切记元素类型决定了解引用时的权限大小
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;
}
字符数组
代码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
//第1个元素地址
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 strlen函数会一直向后寻找
printf("%d\n", strlen(arr + 0)); //随机值
printf("%d\n", strlen(*arr)); //err 程序崩溃
//解引用数组首元素地址,得到a-97 strlen函数将97看作地址访问 导致程序崩溃
printf("%d\n", strlen(arr[1])); //同上
printf("%d\n", strlen(&arr)); //随机值
//取数组的地址,数组地址就等于数组首元素地址,二者只是类型不同
printf("%d\n", strlen(&arr + 1)); //随机值
//跳过数组,向后找 \0
printf("%d\n", strlen(&arr[0] + 1));//随机值
//跳过数组第一个元素向后找 \0
return 0;
}
代码3
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr)); //7
//字符串自带 \0
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;
}
代码4
int main()
{
char arr[] = "abcdef";
printf("%d\n", strlen(arr)); //6
//数组名是数组首元素地址,从数组首元素向后找\0
printf("%d\n", strlen(arr + 0)); //6
//printf("%d\n", strlen(*arr)); //崩溃
//数组元素是数组首元素地址,解引用得到a-97 访问97 程序崩溃
//printf("%d\n", strlen(arr[1])); //崩溃
//arr[1] <==> *(arr+1)
printf("%d\n", strlen(&arr)); //6
//数组地址等于数组首元素地址,二者只是类型不同
printf("%d\n", strlen(&arr + 1)); //随机值
//跳过数组向后找\0
printf("%d\n", strlen(&arr[0] + 1)); //5
//从第二个元素向后找\0
return 0;
}
代码5
int main()
{
char* p = "abcdef"; //常量字符串
printf("%d\n", sizeof(p)); //4/8
//p是指针变量,存地址
printf("%d\n", sizeof(p + 1)); //4/8
//第二个元素的地址 因为p的类型是char* 所以+1也之跳过一个char*
printf("%d\n", sizeof(*p)); //1
//p的类型是char* 所以一次只能访问一个字节
printf("%d\n", sizeof(p[0])); //1
//常量字符串中的第一个元素
printf("%d\n", sizeof(&p)); //4/8
//p的地址 是一个二级指针 char**
printf("%d\n", sizeof(&p + 1)); //4/8
//指向一个地址
printf("%d\n", sizeof(&p[0] + 1)); //4/8
//b的地址
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)); //随机值
printf("%d\n", strlen(&p + 1)); //随机值
printf("%d\n", strlen(&p[0] + 1)); //5
return 0;
}
二维数组
int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); //48
//二维数组的大小
printf("%d\n", sizeof(a[0][0])); //4
//第一行第一列的元素
printf("%d\n", sizeof(a[0])); //16
//a[0]单独放在sizeof内部表示是第一行的数组名,计算的是第一行数组元素的大小
printf("%d\n", sizeof(a[0] + 1)); //4/8
//这里的a[0]表示的是第一行第一个元素的地址 a[0][0],在加一得到a[0][1]的地址
printf("%d\n", sizeof(*(a[0] + 1))); //4
//第一行第二个元素
printf("%d\n", sizeof(a + 1)); //4/8
//第二行元素的地址
printf("%d\n", sizeof(*(a + 1))); //16
//数组名表示数组首元素地址也就是第一行地址,+1表示第二行地址,再*表示第二行元素
//a+1是第二行元素的地址,类型是 int(*)[4],指针数组,解引访问的是十六个字节
printf("%d\n", sizeof(&a[0] + 1)); //4/8
//第二行地址
printf("%d\n", sizeof(*(&a[0] + 1))); //16
//第二行元素
printf("%d\n", sizeof(*a)); //16
//数组第一行元素
printf("%d\n", sizeof(a[3])); //16
//越界访问,但是sizeof内部的表达式不会计算,所以不会报错
return 0;
}
指针运算妙题
代码1
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;
}
心得: 类型决定了解引用的权限
代码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); //0x00100014
//p是结构体指针 +1跳过一个结构体
printf("%p\n", (unsigned long)p + 0x1); //0x00100001
//强制类型转换成无符号整形 +1就是+1
printf("%p\n", (unsigned int*)p + 0x1); //0x00100004
//强制转换成无符号整型指针 +1 跳过四个字节
return 0;
}
代码3
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
- 用逗号表达式初始化数组
代码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]); // 0XFFFFFFFC, -4
return 0;
}
- 先创建一个二维数组,五行五列
- 创建一个数组指针,指向的数组存四个元素
- 将a的地址赋值给p,a是数组名,是数组首元素的地址
p[4][2] <==> *(*(p+4)+2)
p+4跳过十六个整形,解引用后再+2,跳过两个整形- 以%p来打印,是看作无符号数打印,-4的补码直接看作地址来打印 所以结果为FFFFFFFC, -4
代码5
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int* ptr1 = (int*)(&aa + 1); // 跳过二维数组,将类型强制转换为int*
int* ptr2 = (int*)(*(aa + 1));//跳过第一行,解引用,指向第二行的第一个元素,将类型强制转换为int*
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); //10 ,5
return 0;
}
代码6
int main()
{
char* a[] = { "work","at","alibaba" }; // 创建一个指针数组,数组中元素类型为char* 常量字符串
char** pa = a; // 数组名表示数组首元素地址,将数组首元素地址赋值给pa
pa++; // 跳过数组第一个元素
printf("%s\n", *pa); // at
return 0;
}
代码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); //POINT
printf("%s\n", *--*++cpp+3); //ER
printf("%s\n", *cpp[-2]+3); //ST
printf("%s\n", cpp[-1][-1]+1); //EW
return 0;
}
- 先画图看内存中的结构
- 需要注意的就是++会改变指针的指向
- []不影响指针的指向
完