当前位置: 首页 > news >正文

C语言:指针(5)

1. sizeof与strlen的对比

1.1 sizeof

sizeof属于是操作符,用于计算变量所占的空间大小,单位为字节。如果操作数是类型的话,计算的是使用类型创建的变量所占内存空间的大小。

sizeof只计算数据在内存中所占的空间大小,而不在乎内存中存放的数据类型。

例如:

int main()
{int a = 10;printf("%d\n",sizeof a);printf("%d\n", sizeof(a));printf("%d\n", sizeof(int));return 0;
}
4
4
4

1.2 strlen

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

 size_t strlen ( const char * str );

strlen函数会统计指针str指向的那个地址向后直到字符 '\0' 之前的所有字符个数。

但是,strlen函数会一直向后寻找直到找到 '\0' 为止,所以说有越界访问的可能性。

int main()
{char arr1[] = "abd";char arr2[] = {'a','b','c'};printf("%d\n", strlen(arr1));printf("%d\n", strlen(arr2));return 0;
}
3
6

这里在创建第二个字符串时没有加上 '\0' ,所以第二行输出的是一个随机结果。像arr1这样创建字符串,编译器会自动在末尾加上一个 '\0' ,所以第一行的输出结果是确定的。

1.3 sizeof 和strlen的对比

sizeof

1.sizeof是操作符

2.sizeof计算操作数所占内存的大小,单位是字节

3.不关注内存中存放什么数据

strlen

1. strlen是库函数,使用需要包含头文件 <string.h>

2.strlen用于求字符串长度,统计字符串中字符 '\0' 之前字符的个数

3.关注内存中是否存在字符 '\0' ,如果不存在,就会一直向后寻找,可能会越界。

2. 数组和指针题目解析

2.1 一维数组

int main()
{int a[] = {1,2,3,4};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a+0));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a+1));printf("%d\n",sizeof(a[1]));printf("%d\n",sizeof(&a));printf("%d\n",sizeof(*&a));printf("%d\n",sizeof(&a+1));printf("%d\n",sizeof(&a[0]));printf("%d\n",sizeof(&a[0]+1));return 0;
}

下面为解析:

int main()
{int a[] = {1,2,3,4};printf("%d\n",sizeof(a));  //sizeof中a单独出现,表示整个数组,结果为16printf("%d\n",sizeof(a+0));//(a+0),这里a表示数组首元素地址,整体属于整型指针,结果为4/8printf("%d\n",sizeof(*a));//(*a),这里的a表示数组首元素地址,*a表示首元素,类型为整型,结果为4printf("%d\n",sizeof(a+1));//(a+1),a表示数组首元素地址,与整数1相加,整体属于整型指针,结果为4/8printf("%d\n",sizeof(a[1]));//a[1],a表示数组首元素地址,利用操作符[1]来访问数组中下标为1的元素,类型为整型,结果为4printf("%d\n",sizeof(&a));//&a,得到的是整个数组的地址,类型为int(*)[4],结果为4/8printf("%d\n",sizeof(*&a));//(*&a),操作符*与&抵消,最终为a,表示整个数组,结果为16printf("%d\n",sizeof(&a+1));//&a+1,&a得到整个数组地址,+1后跳过整个数组,但是其结果类型依旧为 int(*)[4],结果为4/8printf("%d\n",sizeof(&a[0]));//&a[0],a[0]访问数组中下标为0的元素,即首元素,操作符&得到该元素的地址,类型为整型指针,结果为4/8printf("%d\n",sizeof(&a[0]+1));//&a[0]+1,&a[0]得到数组首元素地址,+1后跳过一个元素,结果类型为整型指针,结果为4/8return 0;
}
16
8
4
8
4
8
16
8
8
8

2.2 字符数组

2.2.1 字符串末尾无 '\0'

代码1:

int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr+0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr+1));printf("%d\n", sizeof(&arr[0]+1));return 0;
}

解析:

int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", sizeof(arr));//szieof中数组名arr单独出现,表示整个数组,结果为6printf("%d\n", sizeof(arr+0));//arr+0,表示数组首元素地址,类型为字符指针,结果为4/8printf("%d\n", sizeof(*arr));//*arr,arr表示数组首元素地址,*arr表示首元素,类型为字符,结果为1printf("%d\n", sizeof(arr[1]));//arr[1],表示数组中下标为1的元素,类型为字符,结果为1printf("%d\n", sizeof(&arr));//&arr,&arr得到整个数组的地址,类型为 char(*)[6],结果为4/8printf("%d\n", sizeof(&arr+1));//&arr+1,&arr得到整个数组的地址,+1跳过整个数组,但结果类型依旧为为char(*)[6],结果为4/8printf("%d\n", sizeof(&arr[0]+1));//&arr[0]+1,&arr[0]得到首元素的地址,+1后向后跳过一个元素,结果类型为字符指针,结果为4/8return 0;
}
6
8
1
1
8
8
8

代码2:

int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", strlen(arr));printf("%d\n", strlen(arr+0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));printf("%d\n", strlen(&arr[0]+1));return 0;
}

解析:

int main()
{char arr[] = {'a','b','c','d','e','f'};printf("%d\n", strlen(arr));//arr表示数组首元素地址,但是数组中没有字符'\0',strlen函数会从arr指向的地址向后一直寻找直到找到'\0',结果为随机值printf("%d\n", strlen(arr+0));//arr表示首元素地址,+0后依旧为首元素地址,结果同第一行结果printf("%d\n", strlen(*arr));//arr为首元素地址,*arr得到首元素字符a,被传递给函数后,a会被转化为其ASCII码值97,并且函数会将97作为地址处理,但是这个地址通常是无法访问的,因此编译时会报错,运行会导致程序崩溃printf("%d\n", strlen(arr[1]));//arr[1],为数组中下标为1的元素,即为b,其结果同第三行printf("%d\n", strlen(&arr));//&arr,&arr得到整个数组的地址,strlen函数会从arr指向的地址向后一直寻找直到找到'\0',结果为随机值printf("%d\n", strlen(&arr+1));//&arr+1,&arr得到整个数组的地址,+1后跳过整个数组,strlen函数向后一直寻找直到找到'\0',结果为随机值    printf("%d\n", strlen(&arr[0]+1));//&arr[0]+1,&arr[0]得到数组首元素的地址,+1跳过一个元素,strlen函数向后一直寻找直到找到'\0',结果为随机值    return 0;
}

将编译错误的语句注释之后得到如下的打印结果:

11
11
11
5
10

2.2.2 字符串末尾存在 '\0'

代码3:

int main()
{char arr[] = "abcdef";printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr+0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr+1));printf("%d\n", sizeof(&arr[0]+1));return 0;
}

解析:

int main()
{char arr[] = "abcdef";//这样创建字符串时,编译器为自动在字符串末尾加上一个'\0'printf("%d\n", sizeof(arr));//sizeof中数组名arr单独出现,表示整个数组,数组中一共有"abcdef"以及末尾的'\0',类型都为字符,因此结果为7//其他的打印不受影响,不过&arr的类型变为了char(*)[7],结果与上面一致,printf("%d\n", sizeof(arr+0));printf("%d\n", sizeof(*arr));printf("%d\n", sizeof(arr[1]));printf("%d\n", sizeof(&arr));printf("%d\n", sizeof(&arr+1));printf("%d\n", sizeof(&arr[0]+1));return 0;
}
7
8
1
1
8
8
8

代码4:

int main()
{char arr[] = "abcdef";printf("%d\n", strlen(arr));printf("%d\n", strlen(arr+0));printf("%d\n", strlen(*arr));printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));printf("%d\n", strlen(&arr[0]+1));return 0;
}

解析:

int main()
{char arr[] = "abcdef";//这样创建字符串,编译器会自动在字符串末尾加上'\0'printf("%d\n", strlen(arr));//arr,arr表示数组首元素,字符串末尾中有'\0',结果为6printf("%d\n", strlen(arr+0));//arr+0,arr表示数组首元素,+0后依旧为数组首元素,字符串末尾中有'\0',结果为6printf("%d\n", strlen(*arr));//编译器报错,原因同代码2printf("%d\n", strlen(arr[1]));//编译器报错,原因同代码2printf("%d\n", strlen(&arr));//&arr,&arr得到整个数组的地址,字符串末尾中有'\0',结果为6printf("%d\n", strlen(&arr+1));//&arr+1,&arr得到整个数组的地址,+1后跳过整个数组,strlen函数向后一直寻找直到找到'\0',结果为随机值printf("%d\n", strlen(&arr[0]+1));//&arr[0]+1,&arr[0]得到数组首元素的地址,+1后跳过一个元素,字符串末尾存在'\0',结果为5return 0;
}

将报错语句注释掉后的打印结果:

6
6
6
5
5

这里,倒数二行的输出结果为5属于是巧合,将字符串中的字符删去两个后,其结果如下:

int main()
{char arr[] = "abcf";printf("%d\n", strlen(arr));printf("%d\n", strlen(arr+0));printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));printf("%d\n", strlen(&arr[0]+1));return 0;
}
4
4
4
5
3

可以发现,除了倒数第二行打印结果不变外,其余打印结果都发生了变化,并且刚好是原本结果减去2。

2.2.3 用字符型指针存储常量字符串地址

代码5:

int main()
{char *p = "abcdef";printf("%d\n", sizeof(p));printf("%d\n", sizeof(p+1));printf("%d\n", sizeof(*p));printf("%d\n", sizeof(p[0]));printf("%d\n", sizeof(&p));printf("%d\n", sizeof(&p+1));printf("%d\n", sizeof(&p[0]+1));return 0;
}

解析:

int main()
{char *p = "abcdef";//将字符串首字符地址存储在字符型指针p中printf("%d\n", sizeof(p));//p在sizeof中单独使用,表示字符串首字符的地址,类型为字符型指针,结果为4/8printf("%d\n", sizeof(p+1));//p+1,p代表字符串首字符的地址,+1后跳过一个字符,结果类型依旧为字符型指针,结果为4/8printf("%d\n", sizeof(*p));//*p,p为字符串首字符地址,*p得到首字符,即为a,类型为字符,结果为1printf("%d\n", sizeof(p[0]));//p[0],编译器处理p[0]时,转化为*(p+0),结果同第三行printf("%d\n", sizeof(&p));//&p,得到指针p的地址,类型为char**,结果为4/8printf("%d\n", sizeof(&p+1));//&p,得到指针p的地址,+1后跳过一个char**类型的空间大小即跳过指针p的地址,指向存储指针p处地址后的地址,结果类型依旧为char**,结果为4/8printf("%d\n", sizeof(&p[0]+1));//&p[0]+1,转化为&*(p+0)+1,*与&抵消,因此结果为p+1,指向字符中第二个字符处,即为存储字符b的地址,结果类型为char*,结果为4/8return 0;
}
8
8
1
1
8
8
8

代码6:

int main()
{char *p = "abcdef";printf("%d\n", strlen(p));printf("%d\n", strlen(p+1));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));return 0;
}

解析:

int main()
{char *p = "abcdef";//字符串末尾存在'\0'printf("%d\n", strlen(p));//p为字符串首字符地址,传递给函数strlen,结果为6printf("%d\n", strlen(p+1));//p+1,p为字符串首字符地址,+1后跳过一个字符,传递给函数strlen,结果为5printf("%d\n", strlen(*p));//编译器报错,结果同代码2printf("%d\n", strlen(p[0]));//编译器报错,结果同代码2printf("%d\n", strlen(&p));//&p,&p得到指针p的地址,地址未知,但肯定不与字符串中任意一个字符重叠,传递给函数strlen后,结果为随机值printf("%d\n", strlen(&p+1));//&p,&p得到指针p的地址,+1后地址依旧未知,但肯定不与字符串中任意一个字符重叠,传递给函数strlen后,结果为随机值printf("%d\n", strlen(&p[0]+1));//&p[0]+1,&p[0]得到字符串首字符地址,+1后跳过一个字符,传递给函数strlen,结果为5return 0;
}
6
5
0
5
5

这里的倒数第二行打印结果也属于巧合,可以跳过修改字符串验证:

int main()
{char *p = "abef";printf("%d\n", strlen(p));printf("%d\n", strlen(p+1));printf("%d\n", strlen(&p));printf("%d\n", strlen(&p+1));printf("%d\n", strlen(&p[0]+1));return 0;
}
4
3
0
5
3

2.3 二维数组

代码7:

int main()
{int a[3][4] = {0};printf("%d\n",sizeof(a));printf("%d\n",sizeof(a[0][0]));printf("%d\n",sizeof(a[0]));printf("%d\n",sizeof(a[0]+1));printf("%d\n",sizeof(*(a[0]+1)));printf("%d\n",sizeof(a+1));printf("%d\n",sizeof(*(a+1)));printf("%d\n",sizeof(&a[0]+1));printf("%d\n",sizeof(*(&a[0]+1)));printf("%d\n",sizeof(*a));printf("%d\n",sizeof(a[3]));return 0;
}

解析:

int main()
{int a[3][4] = {0};//创建一个二维数组并将内部元素全部初始化为0printf("%d\n",sizeof(a));//sizeof中数组名a单独出现,表示整个数组,结果为48printf("%d\n",sizeof(a[0][0]));//a[0][0],a[0][0]表示数组中首元素,类型为整型数据,结果为4printf("%d\n",sizeof(a[0]));//二维数组a中,当sizeof中a[0]单独出现,表示数组中的整个第一行,结果为16printf("%d\n",sizeof(a[0]+1));//a[0]+1,此处a[0]表示数组第一行首元素地址,+1后跳过一个元素,最终结果类型为整型指针,打印结果为4/8printf("%d\n",sizeof(*(a[0]+1)));//*(a[0]+1),a[0]+1表示数组中第一行第二个元素的地址,加上*表示该地址处存储的数据,该数据类型为整型,结果为4printf("%d\n",sizeof(a+1));//a+1,a代表二维数组首行的地址,+1后跳过一整行,表示数组中第二行的地址,结果为4/8printf("%d\n",sizeof(*(a+1)));//*(a+1),a+1表示数组中第二行的地址,加上*,得到整个第二行,结果为16printf("%d\n",sizeof(&a[0]+1));//&a[0]+1,&a[0]表示数组中整个第一行的地址,类型为int(*)[4],+1后跳过一整行,但数据类型依旧为int(*)[4],结果为4/8printf("%d\n",sizeof(*(&a[0]+1)));//&a[0]+1,&a[0]表示数组中整个第一行的地址,类型为int(*)[4],+1后跳过一整行,此时指向第二行地址,加上*后,得到整个第二行,结果为16printf("%d\n",sizeof(*a));//*a,a表示二维数组首行的地址,加上*后,表示整个数组首行,结果为16printf("%d\n",sizeof(a[3]));//a[3]表示数组整个第四行,虽然此时属于数组访问越界,但是编译器依旧将其作为数组的行类型处理,结果为16return 0;
}
48
4
16
8
4
8
16
8
16
16
16

数组名的意义:

1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。

2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。

3. 除此之外所有的数组名都表示首元素的地址。

http://www.xdnf.cn/news/1304083.html

相关文章:

  • lcm通信库介绍与使用指南
  • 使用Docker容器化Python测试Pytest项目并配置GitHub Actions CI/CD流程
  • Pytest项目_day16(yaml和parametrize结合)
  • week1-[循环嵌套]蛇
  • Vue2与Vue3生命周期函数全面解析:从入门到精通
  • Linux操作系统--多线程(锁、线程同步)
  • 基本电子元件:贴片电阻器的种类
  • 达梦数据库使用控制台disql执行脚本
  • Mac(二)Homebrew 的安装和使用
  • HDFS数据倾斜导致MapReduce作业失败的排查与优化实践
  • 一个集成多源威胁情报的聚合平台,提供实时威胁情报查询和播报服务、主动拦截威胁IP,集成AI等多项常用安全类工具
  • mac 通过homebrew 安装和使用nvm
  • 16进制pcm数据转py波形脚本
  • 超越模型中心:AI智能体(Agent)革命来临,AgenticOps将如何颠覆你的工作流?
  • Java-JVM是什么JVM的类加载机制
  • PAT 1064 Complete Binary Search Tree
  • 计算机网络:(十五)TCP拥塞控制与TCP拥塞控制算法
  • 【161页PPT】智慧方案企业数字化转型概述(课件)(附下载方式)
  • AutoSar AP平台功能组并行运行原理
  • [论文阅读] 人工智能 | 当Hugging Face遇上GitHub:预训练语言模型的跨平台同步难题与解决方案
  • JVM执行引擎深入理解
  • 剧本杀小程序系统开发:重构推理娱乐生态
  • 大模型幻觉涉及的违约责任探讨
  • 回路自感和回路互感
  • 补充日志之-配置文件解析指南(Centos7)
  • 德州扑克游戏术语
  • 银河麒麟服务器jar包部署自启动配置
  • 第十八讲:哈希2
  • 神经网络 小土堆pytorch记录
  • 开疆智能Ethernet转ModbusTCP网关连接测联无纸记录仪配置案例