本章主要讲了常量和变量和他们的输入、输出,与上一章的关联度较高。
字符串及其相关函数
本书首先引入了一个又老又新的概念“字符串”。它已经在本书前面部分多次出现,在这里有了详细的介绍。
字符串是被双引号给包起来的内容。我们常常把它储存在字符数组之中。数组是一组连续的相同的数据类型的排列,字符数组把字符串内容一个字符一个字符地储存起来。注意,字符串的末尾往往带着一个\0,既代表着字符串的结束,同时也凭此与纯粹的字符区别开来。
测量字符串相关的长度大小涉及到函数strlen()以及运算符sizeof
strlen()函数测量字符串里有几个字符(不包括末尾的\0)
sizeof运算符在上一章已经提到过,只不过上一章是用来测量数据类型的大小。这一章里,我们发现sizeof的作用不仅局限于此,它还能测量特定量的大小,而且此时不用加括号(当然最好加上,以养成统一的编程习惯)。
sizeof返回字符串的大小包括末尾的\0,返回字符数组的大小与之前设定的一致。
char box[40]="艳阳天那个风光好,红的花是绿的草";
printf("%zd",sizeof(box));
如本例,打印出来的是40,即40个字节。
常量与变量
常量预先设定,往往是不会变动的。而变量则在程序运行过程中被赋值和修改。
常量分为数值常量、符号常量(明示常量)。数值常量如3.14。符号常量用一些单词代替数据,会把程序中的相关内容自动替换掉
在C预处理指令用#define声明的常量是明示常量:
#define PI 3.14//这行语句将把程序中所有的 PI 自动替换成 3.14。
与之相类似的一种方式是使用const限定符,限定一个变量为只读:
const int MONTHS = 12; //MONTHS在程序中不可更改,值为12
使用这种方式可以大大提高程序的可读性
格式化输入/输出
接下来是printf()和scanf(),它们一个输入,一个输出。在前面的章节里已经有了粗浅的介绍,接下来进行一些补充。
printf()
转换声明修饰
修饰符 | 含义说明 |
---|---|
标记 | 详情见下一张表 |
数字 | 最小字段宽度 |
.数字 | %e、%E、%f:保留小数点后面第几位 %g、%G:有效数字最大位 %s:字符的最大数量 整型:最小位 |
标记
标记 | 含义说明 |
---|---|
- | 从字段最左边开始打印 |
+ | 表示正负号 |
空格 | 表示正负,正的空格,负的-覆盖空格 |
0 | 用0填充前面的空格(如果出现-标记或指定精度则忽略该标记) |
转换说明起到一个翻译的作用。同一个数值,用不同的转换说明会有不同的效果。
#include <stdio.h>
#define NUM 333
int main(void)
{
printf("num as int and char:%d %c\n",NUM,NUM);
printf("-num as short and unsigned short:%hd %hu\n",-NUM,-NUM);
return 0;
}
输出→
num as int and char:333 M
-num as short and unsigned short:-333 65203
一行一行解释(只解释每行的第二个)。
第一行,转换说明%c表明输出一个字符,然而ASCII编码只有0-255,这个333显然远远超出了。但是这不重要,因为printf()只会查看后8位,也就是0~255。这在数学上相当于对原来的数除以256后取余(以256为模)。
第二行,一个负数被被要求表达成一个非负数,结果输出了65203。这是short int在参考系统的表示方式所致,它本身有两个字节,第一个字节(0-32767)表示正数,第二个字节(32768-65535)表示负数。被解释成无符号数时,-1对应最大的,-2对应倒数第二大的,以此类推。-333 → 65536-333=65203
这启示我们不能乱用转换说明。然而,有时即使用对了转换说明,也会出现问题,看接下来的代码:
敲黑板,马上就到printf()的参数传递原理,这里我认为是本章的重点。
#include <stdio.h>
int main(void)
{
float a=3.2;
double b=2.0;
long c1 =3000000000;
long c2 =2340000000;
printf("%ld,%ld,%ld,%ld",a,b,c1,c2);
return 0;
}
输出→
-1610612736,0,-1294967296,-1954967296
奇怪了,c1、c2命名用对了转换说明,为什么输出的值还是错的呢?
原来,printf()在进行打印之前,先把参数列表中的数据储存在栈里,然后再一个个读取,输出到屏幕上。float类型a变量在储存到栈的过程中自动转换为double,由4字节变为8字节,b变量本身也是8字节。而%ld转换说明告诉计算机应该读4个字节。第一个读a前半部分;第二个读a后半部分;第三个读b前半部分;第四个读b后半部分。其结果就是用了4个%d后仍然没有到达c1、c2对应的栈的储存地点,也就自然无法输出c1、c2。
scanf()
键盘输入的都是字符,转换说明将字符翻译成相应的数据类型。
转换说明大部分和printf()一样。讲几个不一样的。
%e、%E、%g、%G这些一律看成float类型,对于double要加 l 修饰符
读取过程:跳过前面的空白字符(除char类型)一个一个保存数据并读下去,直到遇到与要求的数据类型不同的数据(包括空白),并将其放回输入。
ps:如果转换类型是%c,输入的第一个字符是空白,那么储存的就是空格。如果想要跳过空白,可以在格式字符串中%c的前面加入空格。
printf()和scanf()的 * 修饰符
*在printf()和scanf()中的用法并不相同,先介绍printf()
在printf()中,*用于延缓输出,也就是用于代替修饰符中的数字,这样输出的标准就可以在程序之中被指定了。
#include <stdio.h>
#define PERCENT 1
int main(void)
{
float a;
a=723.88;
printf("%.*f",PERCENT,a);
return 0;
}
输出→ 723.9
而在scanf()中,*放在%和转换说明之间,会使得scanf()跳过相应的输入项