✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。
🍎个人主页: 小嗷犬的博客
🍊个人信条:为天地立心,为生民立命,为往圣继绝学,为万世开太平。
🥭本文内容:C语言竟支持这些操作:C语言神奇程序分享
@TOC
近期在网上冲浪的时候发现几个十分有趣的C语言程序,它们运行之后会产生一些看似不是很合理,但其实是十分合理的结果,让我们一起来看看吧!
1.神奇的死循环
下面这段程序运行之后可能会造成死循环:
#include<stdio.h>
int main()
{
int a[10], i;
for (i = 0; i <= 10; i++)
{
a[i] = 0;
}
return 0;
}
初学C语言的小伙伴可能死活也想不出为什么会有死循环,这个结构隐藏在平时的作业中,让我们的初学者不停挠头。产生错误的原因首先是因为数组的访问越界,大小为
10
的数组只能访问0-9
,毕竟咱们程序员数数是从零开始的。循环中从
0
访问到10
,就造成了访问越界,这在C语言中是可以编译通过的,因为在C语言中,数组名其实是一个地址,编译器不会知道这个地址管到了多少位,它只会把访问的索引看作偏移量进行访问。然后是因为内存地址的问题,在这个程序中,我们先定义了数组
a
,然后又定义了整型变量i
,此时绝大多数编译器都会先为数组a
分配内存地址,然后紧挨着数组a
再为整型变量i
分配内存地址。这就造成了:当我们在访问
a[10]
时,其实是在访问变量i
,循环最后会将a[10]
设置为0
,即将变量i
设置为0
,循环就无法满足退出条件,于是产生了死循环。
2.神奇的隐式转换
在某些弱类型语言中, 隐式转换是十分常见的,如在JavaScript中,用一个数字去减去一个形如数字的字符串,JavaScript会将字符串隐式转换为数字,再进行数字之间的减法运算。下面的C语言程序似乎也出现了隐式转换的现象:
#include<stdio.h>
int main()
{
puts("-0.5" + 1);
// 输出:0.5
return 0;
}
这段程序竟然会输出0.5
,似乎就是将字符串"-0.5"
隐式转换成了浮点数-0.5
,再与数字1
进行加法运算的。难道我们的C语言也支持类似JavaScript那种隐式转换吗?
C语言当然是不支持这样的隐式转换的,那程序为什么会输出
0.5
呢?其实输出的
0.5
并不是一个数字,其实它是以一个字符串的身份输出的。这就要从C语言的字符串说起了,在C语言中,没有字符串类型,字符串其实都是用以
'\0'
结尾的字符数组储存的,而数组名则表示字符串的首地址。字符串常量也是类似,程序中的字符串
"-0.5"
其实也是表示储存了"-0.5"
中的各个字符和字符'\0'
的字符数组的首地址。而其后的
+1
则并不是表示数学运算上的加一,而是表示地址向后偏移一个单位的地址,即原来字符串第二个字符的地址。这样就能让
puts()
函数跳过第一个字符'-'
负号来输出字符串,自然输出的结果就是0.5
了。所以其实上面那个程序就等价于以下程序:
#include<stdio.h>
int main()
{
char* p = "-0.5";
puts(&p[1]);
// 输出:0.5
return 0;
}
3.神奇的**指数运算
在Python中,我们可以使用**
运算符来表示数学上的指数运算,而在C语言中通常是用pow()
函数。下面的程序似乎显示,C语言也可以使用
**
运算符来进行指数运算:
#include<stdio.h>
int main()
{
printf("%d\n", 50 ** "2");
// 输出:2500
return 0;
}
但细看就可以发现一部分问题,首先那个"2"
是字符串,如果真的能够进行**
指数运算,那也一定是两个数字之间的。也许字符串
"2"
被编译器隐式转换成数字2
了呢?在上一节我们提到,C语言中的字符串是用字符数组存的,也就是说字符串
"2"
其实是一个字符数组,其中包含两个字符:'2'
和'\0'
,将一个包含两个字符的字符数组转换成一个数字,这显然是做不到的。在Python中,有字符串类型,通过强转型函数可以将字符串转换为其他类型,但是即便是Python,也不支持上述那种隐式转换,即无法将一个数组与一个字符串进行
**
指数运算。排除了隐式转换,让我们来分析一下原因。
首先,在C语言中没有
**
这种运算,所以两个星号*
应该是拆开来理解。我们知道,在C语言中,星号
*
不止可以用来进行乘法运算,还可以连接地址用来表示取值运算,这也能够解释为什么后面的"2"
是字符串了,上节我们说过,字符串常量在C中也是一个地址,于是正确的运算结合顺序就出来了:
#include<stdio.h>
int main()
{
printf("%d\n", 50 * (*"2"));
// 输出:2500
return 0;
}
"2"
表示一个含有两个字符('2'
和'\0'
)的字符数组的首地址,那么*"2"
就等价于"2"[0]
,即等价于字符'2'
。我们又知道,C语言中的char类型其实是整数到ASCII表的一组映射,其值为它的ASCII码值,刚好字符
'2'
的ASCII码的十进制值为50
。所以我们最终可以得到以下等价关系:
50 ** "2"
=> 50 * (*"2")
=> 50 * "2"[0]
=> 50 * '2'
=> 50 * 50
所以上面的程序也可以写成这样:
#include<stdio.h>
int main()
{
printf("%d\n", 50 * 50);
// 输出:2500
return 0;
}
这样程序运行自然就会输出
2500
了,是不是觉得很合情合理呢?