先看一段代码,如下:
1 int main() 2 { 3 char *p1 = 1; 4 char *p2 = ‘1‘; 5 char *p3 = "1"; 6 7 printf("%s,%s,%s",p1,p2,p3); 8 printf(‘\n‘); 9 printf("\n"); 10 11 return 0; 12 }
上述代码中首先定义了三个指向char型的指针变量,并给这三个指针变量赋了初值。接下来作者的原意,是想利用printf函数将上面的三个1打印出来,但是很遗憾,没有实现作者想要的效果。那么问题在何处呢?
首先我们来看第3行代码,char *p1 = 1;这是什么意思呢?这条代码可以拆分为以下两条代码:char *p1; p1 = 0x0000 0001;也就是说将内存地址0x0000 0001赋给指针变量p1(此处我们且不考虑要不要强制类型转化,但是原则上肯定是需要的),然后在后面的代码中利用printf将地址0x0000 0001单元里面的内容打印出来。通常在内存低地址处都是预留给操作系统使用的。贸然对其进行访问,有可能造成错误。显然这里实现不了作者想要的效果。
接着我们来看第4行代码,char *p2 = ‘1‘;这又是什么意思呢?同样这条代码可以拆分为两条代码,char *p2;p2 = 0x0000 0031;为什么是0x0000 0031呢?在第四行代码中是将一个单个的字符‘1‘赋给了指针变量p2,而单个的字符的值是一个8位的整数,在ASCII码中数字1的码为0x31,。所以上述第四行代码的意思就是将内存地址0x0000 0031赋给了指针变量p2,同样如上所述,这也无法实现作者想要的效果。
第5行代码,char *p3 = "1";很显然这是我们很熟悉的用法,这条代码同样可以拆分为两条:char *p3; p3 ="1";p3 是一个指向char型的指针变量,而"1"是一个字符串,在之前我们不止说过一次,字符串的值是一个地址,该地址指向该字符串。所以这条代码若是单独的利用printf,肯定是能事先作者的目的的。
接下来我们来看第8行代码,printf(‘\n‘);,这行代码正确吗?我再VS下编译了一下,编译有警告,运行有错误。说到这里,我们来看一下printf函数的原型:
_CRTIMP __checkReturn_opt int __cdecl printf(__in_z __format_string const char * _Format, ...);
关于那些输入输出的修饰符我们就不看了,重点我们看一个东西,就是第一个形参的类型,是一个const char *的变量,后面的...表示可变的参数。类似于C#中的params,当我们执行printf(‘\n‘);时编译器首先会把‘\n‘的值转化成一个const char*的指针变量,而‘\n‘的值是0x0a,同样这里就会系统就会去访问地址为0x0000 000a处的内容,但是很显然,该地址里面的内容不是我们想要的。
OK~进入正题!
1、关于单引号和双引号的不同点
单引号在C语言中用来表示单个的字符常量,想必这个大家都很熟悉了。
例如:‘a‘表示字符常量‘a‘,在内存中占一个字节,‘a‘+1,表示将‘a‘的ASCII码加一,得到‘b‘;
双引号在C语言中用于表示字符串常量。
例如:"a"表示字符串常量,在内存中占两个字节,因为最后还有一个‘\0‘,"a"+1表示指针运算,结果指向字符串"a"的结束符‘\0‘;
2、一些总结
2.1 本质上用单引号括起来的一个字符代表整数。该整数是该字符的ASCII码。
2.2 双引号括起来的字符串代表指针常量。该指针指向该字符串。