【C语言】指针(5):sizeof与strlen

1.sizeof和strlen的对比

1.1.sizeof

sizeof 是计算变量所占内存空间大小,单位是字节,如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。sizeof值关注占用内存空间的大小,不在乎内存中放什么数据。

#include <stdio.h>
int main()
{
	int a = 10;
	printf("%d ", sizeof(a));    //4
	printf("%d ", sizeof a);     //4
	printf("%d ", sizeof(int));  //4
	return 0;
}

1.2.strlen

strlen 是C语言库函数,功能是求字符串长度。函数原型如下 :

size_t strlen(const char * str)

统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。 strlen 函数会⼀直向后找 \0 字符,直到找到为止,所以可能存在越界查找。 

#include <string.h>
//strlen
int main()
{
	char arr1[3] = { 'a','b','c' };
	char arr2[] = "abc";//“”最后面还有一个隐藏的\0,{}中没有
	printf("%d ", strlen(arr1));  //35  无\0,会越界访问
	printf("%d ", strlen(arr2));  //3

	printf("%d ", sizeof(arr1));  //3  char类型长度是一个字节
	printf("%d ", sizeof(arr2));  //4  “”最后面还有一个隐藏的\0
	return 0;
}

1.3.sizeof与strlen对比 

sizeof
1.sizeof是操作符
2.sizeof计算的是操作数所占内存的大小,单位是字节
3.不关注内容中存放什么位置
4.sizeof中有表达式的话,表达式不参与计算

strlen
1.strlen是库函数,需要包括头文件string.h
2.strlen是求字符串长度的,统计的是\0之前的字符个数
3.关注内存中是否有\0,如果没有,就会继续往后找,可能会越界
4.strlen的参数是指针,地址

2.数组与指针

2.1.一维数组

数组名是数组首元素的地址,有且仅有两个例外
 - sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小,单位是字节
 - &数组名,数组名表示整个数组,取出的是整个数组的地址

int a[] = { 1,2,3,4 };
printf("%d\n", sizeof(a));      //16 int类型4个字节,数组中有四个整形元素
printf("%d\n", sizeof(a + 0));  //4  不是两个例外之一,a表示数组首元素地址,类型是int*,是地址大小就是4/8,32位的平台是4,64位是8
printf("%d\n", sizeof(*a));     //4  a是首元素的地址,*a就是首元素,其类型是int
	                                //*a == a[0] == *(a+0)
printf("%d\n", sizeof(a + 1));  //4  a+1表示数组第二个元素的地址,4/8
printf("%d\n", sizeof(a[1]));   //4  a[1]表示数组第二个元素,类型是int,大小4个字节
printf("%d\n", sizeof(&a));     //4  &a是地址,是地址就是4/8
printf("%d\n", sizeof(*&a));    //16 1. *和&相互抵消,得到sizeof(a)
                                //   2.&a是整个数组的地址,对其解引用访问的是整个数组
printf("%d\n", sizeof(&a + 1)); //4  &a是整个数组的地址,加一跳过整个数组,得该数组后那个位置的地址,4/8
printf("%d\n", sizeof(&a[0]));  //4  数组首元素的地址,4/8
printf("%d\n", sizeof(&a[0] + 1));//4 数组第二个元素的地址,4/8

//a - 数组名
//a - 首元素的地址,类型是int*
//&a - 数组的地址,int(*)[4]

2.2.字符数组 

代码1:

	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));         //6
	printf("%d\n", sizeof(arr + 0));     //4/8 首元素地址,是地址就是4/8个字节
	printf("%d\n", sizeof(*arr));        //1  数组首元素
	printf("%d\n", sizeof(arr[1]));      //1  数组第二个元素
	printf("%d\n", sizeof(&arr));        //4/8  整个数组的地址,是地址就是4/8个字节
	printf("%d\n", sizeof(&arr + 1));    //4/8  跳过该数组之后那个位置的地址
	printf("%d\n", sizeof(&arr[0] + 1)); //4/8  第二个元素的地址

代码2:

	//size_t strlen(const char *s)
	//{

	//}
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));     //arr是首元素的地址,数组中没有\0,就会导致越界访问
	printf("%d\n", strlen(arr + 0)); //从数组第一个元素开始,数组中没有\0,会越界访问,结果是随机的
	printf("%d\n", strlen(*arr));    //会报错,arr是首元素的地址,*a是首元素,就是'a','a'的ASCLL值是97
	                                 //相当于把97作为地址传递给了strlen,strlen得到的就是野指针
	printf("%d\n", strlen(arr[1]));  //arr[1]是'b',也就是把98传给了strlen
	printf("%d\n", strlen(&arr));    //&arr是整个数组的地址,起始位置是数组第一个元素的位置,没有\0,会越界访问,随机值x
	printf("%d\n", strlen(&arr + 1));//随机值x-6
	printf("%d\n", strlen(&arr[0] + 1));//第二个元素的地址,随机值x-1

代码3:

	char arr[] = "abcdef";
    printf("%d\n", sizeof(arr));         //7 abcdef加上’/0'一共七个字节
    printf("%d\n", sizeof(arr + 0));     //4/8 首元素地址,是地址就是4/8个字节
    printf("%d\n", sizeof(*arr));        //1  数组首元素
    printf("%d\n", sizeof(arr[1]));      //1  数组第二个元素
    printf("%d\n", sizeof(&arr));        //4/8  整个数组的地址,是地址就是4/8个字节
    printf("%d\n", sizeof(&arr + 1));    //4/8  跳过该数组之后那个位置的地址
    printf("%d\n", sizeof(&arr[0] + 1)); //4/8  第二个元素的地址

代码4:

    char arr[] = "abcdef";
    printf("%d\n", strlen(arr));     //6 arr是首元素的地址,从数组第一个元素算起直到\0停止
    printf("%d\n", strlen(arr + 0)); //6 arr+0是首元素的地址,向后在\0之前有6个字符
    //printf("%d\n", strlen(*arr));    //'a' - 97,出错                                  
    //printf("%d\n", strlen(arr[1]));  //出错
    printf("%d\n", strlen((const char *)&arr));//6 &arr是数组的地址,
                                               // 起始位置是数组第一个元素的位置                                                                          
    printf("%d\n", strlen((const char*)&arr + 1));//随机值
    printf("%d\n", strlen(&arr[0] + 1));//5 从第二个元素的位置开始计算
	return 0;

 代码5:

	const char* p = "abcdef";
    printf("%d\n", sizeof(p));         //4/8 p是指针变量,也就是地址,计算指针变量的大小,4/8个字节
    printf("%d\n", sizeof(p+1));       //4/8 p+1是第二个元素的地址
    printf("%d\n", sizeof(*p));        //1  p的类型是const char*,*p的类型就是char
    printf("%d\n", sizeof(p[0]));      //1  首元素
    printf("%d\n", sizeof(&p));        //4/8  &p取出p的地址,是地址就是4/8个字节
    printf("%d\n", sizeof(&p + 1));    //4/8  跳过该指针变量p之后那个位置的地址
    printf("%d\n", sizeof(&p[0] + 1)); //4/8  ‘b'的地址

代码6:  

    const char* p = "abcdef";
    printf("%d\n", strlen(p));     //6 p是首元素的地址,从数组第一个元素算起直到\0停止
    printf("%d\n", strlen(p + 1)); //5 p是首元素的地址,+1得到第二个字符的地址
	//printf("%d\n", strlen(*p));  //'a' - 97,出错                                  
    //printf("%d\n", strlen(p[0]));  //出错,p[0]-->*(p+0)-->*p
    printf("%d\n", strlen(&p));    //随机值,&p是指针变量p的地址,和字符串"abcdef"关系不大,
	//从p这个指针变量的起始位置开始向后数,p变量存放的地址是什么,不知道,所以是随机值
    printf("%d\n", strlen(&p + 1));//随机值
    printf("%d\n", strlen(&p[0] + 1));//5 p[0]就是*(p+0),&p[0]就是第一个字符的地址

2.3.二维数组

二维数组可以看作是由一维数组组合而成的数组

	int a[3][4] = { 0 };
    printf("%d\n", sizeof(a));         //48
    printf("%d\n", sizeof(a[0][0]));   //4  首元素,int类型,4个字节
    printf("%d\n", sizeof(a[0]));      //16  a[0]是第一行数组名,数组名单独放在sizeof中,计算的是数组的大小
    printf("%d\n", sizeof(a[0] + 1));  //4/8  数组名a[0]未单独放在sizeof中,是数组首元素的地址,加1就是a[0][1]的地址
    printf("%d\n", sizeof(*(a[0] + 1))); //4  a[0]表示数组首元素的地址,a[0]+1表示第一行第二个元素的地址
    printf("%d\n", sizeof(a + 1));       //4/8  a作为数组名并没有单独放在sizeof中,表示首元素的地址,是二维数组的首元素
	//是第一行的地址,a+1就是第二行的地址,指向整个第二行,a+1是个数组指针
    printf("%d\n", sizeof(*(a + 1)));    //16  1.a+1是第二行的地址,进行解引用,*(a+1)就是第二行,计算的是第二行的大小
	//2.*(a+1)就是a[1],相当于将a[1]单独放在sizeof中,计算的是第二行的大小
    printf("%d\n", sizeof(&a[0] + 1)); //4/8  a[0]是第一行的数组名,&a[0]就是第一行的地址,加一得到第二行的地址,是地址即使4/8个字节
    printf("%d\n", sizeof(*(&a[0] + 1))); //16 &a[0]+1是第二行的地址,解引用得到第二行,计算的是第二行的大小
    printf("%d\n", sizeof(*a));           //16  a表示二维数组首元素的地址,也就是第一行的地址,*a就是第一行
    printf("%d\n", sizeof(a[3])); //16 a[3]无需真的存在,仅仅通过类型就能推断出长度

a[3][4]数组由a[0],a[1],a[2] 三个数组组成。

3.指针运算

代码1:

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* p = (int*)(&a + 1);
	printf("%d,%d", *(a + 1), *(p - 1));//2,5
	return 0;
}

p中存储的是数组a之后那个位置的地址,-1得到5的地址,在解引用得到5

代码2:

int main()
{
	int a[3][2] = { (0,1),(2,3),(4,5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);//1
	return 0;
}

逗号表达式,其运算规则是 “取最后一个表达式的值”。实际上这个数组是{1,3,5} 

代码3:

int main() {
    int a[5][5];
    int(*p)[5];  // 指向包含5个int类型元素的数组的指针
    p = a;
    printf("%p %d\n", &p[4][3] - &a[4][2], &p[4][3] - &a[4][2]); 
    return 0;
}
int main()
{
	int a[5][5];
	int(*p)[4];//p是一个指针数组,指向的数组是四个整形的数组
	p = a;//会报错,不能将 "int (*)[5]" 类型的值分配到 "int (*)[4]" 类型的实体

	printf("%p %d", &p[4][3] - &a[4][2], &p[4][3] - &a[4][2]);//p[4][2] == *(*(p+4)+2)
	//p[4]从数组首地址开始,向后移动四次,每次四个字节,p[4][2]是p[4]再往后移动两字节
	return 0;
}
//指针-指针得到指针之间的元素个数

 a的类型是int(*)[5],p的类型是int(*)[4].

代码4:

int main()
{
	int a[2][5] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p1 = (int*)(&a + 1);//&a拿到整个数组的地址,+1得到数组后面那个位置的地址,再-1得到10的地址
	int* p2 = *(a + 1);//a是二维数组首元素的地址,也就是第一行的地址,+1得到第二行首元素的地址
	//,再-1得到第一行最后一个元素5的地址
	printf("%d %d", *(p1 - 1), *(p2 - 1));//10 5
	//指针+1加几个字节,取决于指针类型
	return 0;
}

代码5:

int main()
{
	const char* a[] = { "work","at","alibaba" };//指针数组
	const char** pa = a;
	pa++;
	printf("%s\n", *pa);//at
	return 0;
}

代码6: 

int main()
{
	const char* c[] = { "ENTER","NEW","POINT","FIRST" };
	const char** cp[] = { c + 3,c + 2,c + 1,c };
	const 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;
}

(1)cpp中储存的是数组cpp第一个元素的地址,++cpp是cpp中储存到变为数组cp第二个元素的地址,解引用的到数组cp的第二个元素c+2,再解引用得到数组c中的第三个元素“POINT".

(2)++cpp使cpp中储存的变为数组cp中第三个元素的地址,解引用得到数组cp中的第三个元素,即c+1,--(c+1)得到c,即*c+3,对c解引用拿到E的地址,加三得到ER。

(3)cpp[-2]即*(cpp-2),(cpp-2)得到的是数组第一个元素的地址,解引用得到c+3,再解引用得到数组c的第四个元素,加三最后得到ST

(4)cpp[-1][-1]即*(*(cpp-1)-1),*(cpp-1)得到数组c的第三个元素c+2,那*(*(cpp-1)-1)得到的就是c+1的内容,再加1得到EN。

指向字符串的指针(如 c[0])进行 +n 操作:偏移字符串内部的字符(在同一个字符串内移动)。
指针数组 c 本身(如 c)进行 +n 操作:才会指向数组 c 中的第 n 个元素(如 c + 3 指向 c[3])。

内容概要:本文介绍了一种基于带通滤波后倒谱预白化技术的轴承故障检测方法,特别适用于变速工况下故障特征提取困难的问题。该方法通过对振动信号进行带通滤波,抑制噪声干扰,再利用倒谱预白化消除调制效应,提升周期性冲击特征的可辨识度,最后通过平方包络谱分析有效识别轴承故障频率。文中提供了完整的Matlab代码实现,便于读者复现算法并应用于实际故障诊断场景。该技术对于早期微弱故障信号的检测具有较强敏感性,能够显著提高变速条件下轴承故障诊断的准确性。; 适合人群:具备一定信号处理基础,从事机械故障诊断、工业设备状态监测等相关领域的研究生、科研人员及工程技术人员。; 使用场景及目标:①解决变速工况下传统包络谱分析易受频率混叠和噪声干扰导致故障特征难以识别的问题;②实现对轴承早期故障微弱冲击信号的有效提取增强;③为旋转机【轴承故障检测】【借助倒谱预白化技术在变速条件下诊断轴承故障的应用】带通滤波后的倒谱预白化的平方包络谱用于轴承故障检测(Matlab代码实现)械的智能运维预测性维护提供技术支持。; 阅读建议:建议结合Matlab代码逐行理解算法流程,重点关注带通滤波器设计、倒谱预白化处理步骤及平方包络谱的生成过程,同时推荐使用公开数据集(如CWRU)进行验证对比实验,以深入掌握方法优势适用边界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值