第四章,主要讲输入输出和字符串,简单
文章目录
字符串
头文件可提供库函数的原型
#include <stdio.h>
#include <string.h>//提供strlen()函数的原型
int main()
{
char word[20]; //用数组存储字符串,占用20个连续的内存单元(字节)
printf("Enter your name:");
scanf("%s", word);//注意数组不需要&!!!这是因为C中数组名就是地址,就是指针!!
printf("%s has %d letters.\n", word, strlen(word));
return 0;
}
Enter your name:mary
mary has 4 letters.
正如圆括号表明前面的标识符代表一个函数名一样的
数组是同类型数据元素的有序序列
null字符
不需要自己加空字符,编译器加
重要工具:C预处理器指令
#define可以定义常量,各种各样的常量,包括数值,字符串
#include <stdio.h>
#include <string.h>
#define PRAISE "you are an extraordinary being."
int main()
{
char word[20]; //用数组存储字符串,占用20个连续的内存单元(字节)
printf("Enter your name:");
scanf("%s", word);//注意数组不需要&!!!这是因为C中数组名就是地址,就是指针!!
printf("%s has %d letters.\n", word, strlen(word));
printf("%s, %s", word, PRAISE);
return 0;
}
一定要注意,scanf遇到空格,换行符,制表符就会停止读取
Enter your name:Mary Green
Mary has 4 letters.
Mary, you are an extraordinary being.
符号常量(详解float double)
(aka 明示常量 manifest constant)
下面代码中被注释的几行都是没有定义的符号常量
UCHAR_MIN, USHRT_MIN, UINT_MIN, ULONG_MIN, ULLONG_MIN都没有定义哦
#include <stdio.h>
#include <limits.h>
#include<float.h>
int main()
{
printf("limits.h\n");
printf("CHAR_BIT: %d bits.\n", CHAR_BIT);
printf("CHAR_MAX: %d.\n", CHAR_MAX);
printf("CHAR_MIN: %d.\n", CHAR_MIN);
printf("SCHAR_MAX: %d.\n", SCHAR_MAX);
printf("SCHAR_MIN: %d.\n", SCHAR_MIN);
printf("UCHAR_MAX: %d.\n", UCHAR_MAX);
// printf("UCHAR_MIN: %d.\n", UCHAR_MIN);
printf("SHRT_MAX: %d.\n", SHRT_MAX);
printf("SHRT_MIN: %d.\n", SHRT_MIN);
printf("USHRT_MAX: %d.\n", USHRT_MAX);
// printf("USHRT_MIN: %d.\n", USHRT_MIN);
printf("INT_MAX: %d.\n", INT_MAX);
printf("INT_MIN: %d.\n", INT_MIN);
printf("UINT_MAX: %u.\n", UINT_MAX);
// printf("UINT_MIN: %d.\n", UINT_MIN);
printf("LONG_MAX: %ld.\n", LONG_MAX);
printf("LONG_MIN: %ld.\n", LONG_MIN);
printf("LLONG_MAX: %lld.\n", LLONG_MAX);
printf("LLONG_MIN: %lld.\n", LLONG_MIN);
printf("ULONG_MAX: %lu.\n", ULONG_MAX);
// printf("ULONG_MIN: %u.\n", ULONG_MIN);
printf("ULLONG_MAX: %llu.\n", ULLONG_MAX);
// printf("ULLONG_MIN: %u.\n", ULLONG_MIN);
printf("\n");
printf("float.h\n");
printf("FLT_MANT_DIG: %d \n", FLT_MANT_DIG);
printf("FLT_DIG: %d \n", FLT_DIG);
printf("FLT_MIN_10_EXP: %d \n", FLT_MIN_10_EXP);
printf("FLT_MAX_10_EXP: %d \n", FLT_MAX_10_EXP);
printf("FLT_MIN: %d \n", FLT_MIN);
printf("FLT_MAX: %u \n", FLT_MAX);
printf("FLT_EPSILON: %e \n", FLT_EPSILON);
printf("DBL_MANT_DIG: %d \n", DBL_MANT_DIG);
printf("DBL_DIG: %d \n", DBL_DIG);
printf("DBL_MAX: %f \n", DBL_MAX);
printf("DBL_MIN: %f \n", DBL_MIN);
printf("DBL_MIN_10_EXP: %d \n", DBL_MIN_10_EXP);
printf("DBL_MAX_10_EXP: %d \n", DBL_MAX_10_EXP);
printf("DBL_EPSILON: %e \n", DBL_EPSILON);
return 0;
}
编译器标准C99, C11都可以识别LLONG_MAX和LLONG_MIN,不像书上说的这样
注意long要用%ld, long long 要用%lld做转换说明
这个epsilon的值用%d和%f做转换说明都是不对的,肯定不会是0呀,必须用%e
limits.h
CHAR_BIT: 8 bits.
CHAR_MAX: 127.
CHAR_MIN: -128.
SCHAR_MAX: 127.
SCHAR_MIN: -128.
UCHAR_MAX: 255.
SHRT_MAX: 32767.
SHRT_MIN: -32768.
USHRT_MAX: 65535.
INT_MAX: 2147483647.
INT_MIN: -2147483648.
UINT_MAX: 4294967295.
LONG_MAX: 2147483647.
LONG_MIN: -2147483648.
LLONG_MAX: 9223372036854775807.
LLONG_MIN: -9223372036854775808.
ULONG_MAX: 4294967295.
ULLONG_MAX: 18446744073709551615.
float.h
FLT_MANT_DIG: 24
FLT_DIG: 6
FLT_MIN_10_EXP: -37
FLT_MAX_10_EXP: 38
FLT_MIN: 0
FLT_MAX: 3758096384
FLT_EPSILON: 1.192093e-007
DBL_MANT_DIG: 53
DBL_DIG: 15
DBL_MAX: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000
DBL_MIN: 0.000000
DBL_MIN_10_EXP: -307
DBL_MAX_10_EXP: 308
DBL_EPSILON: 2.220446e-016
可见float的尾数占24位,则指数占8位, 有效位数至少是6位,表示范围是
1
0
−
37
−
−
−
−
1
0
38
10^{-37}----10^{38}
10−37−−−−1038
double的尾数占53位,则指数占11位, 有效位数至少是15位,表示范围是
1
0
−
307
−
−
−
−
1
0
308
10^{-307}----10^{308}
10−307−−−−10308
double的epsilon比float的小很多,说明double的精度确实更高
printf()函数
#include <stdio.h>
int main()
{
printf("*%d*\n", 789);
printf("*%2d*\n", 789); //字段宽度小于实际需求时, 字段宽度会自动扩大以适应需求
printf("*%10d*\n", 789); //占10个字段宽度(空格宽度)
printf("*%-10d*\n", 789); //左对齐
return 0;
}
*789*
*789*
* 789*
*789 *
printf()的类型不匹配时,可能会出错,
比如:%hd打印int变量
#include <stdio.h>
int main()
{
int x = 3000000;
printf("%hu\n", x);
printf("%d\n", x);
printf("%d", x%65536);
return 0;
}
50880
3000000
50880
但也不是一直错,因为错误原因在于int是4字节,short是2字节,当x大于short的表示范围才会出错,出现取模的效果,因为%hd表示int变量就会只取后两个字节的内容,相当于把x对 2 1 6 2^16 216,即65536取模
下面x=300,咩有超过short的范围,所以就没有出错
#include <stdio.h>
int main()
{
int x = 300;
printf("%c\n", x);
printf("%hd\n", x);
printf("%d\n", ',');
return 0;
}
,
300
44
float类型的值作为printf()的参数会被自动转换为double类型
#include <stdio.h>
int main()
{
float x = 3.2;
double y = 3.2;
long z1 = 100000000;
long z2 = 300000000;
printf("%e, %.1e, %.2e, %e, %.1e, %.2e\n", x, x, x, y, y, y);//float会被自动转换为double,所以x,y完全一样
printf("%ld, %ld\n", z1, z2);
printf("%e, %e\n", z1, z2);//用%e打印long变量会得到错误结果,因为%e使得编译器认为是double类型,于是就要读8字节,可是long早我的系统中是4字节,于是就多读了内存旁边的4字节,自然就得到了意外的结果
printf("%ld, %ld, %ld, %ld\n", x, y, z1, z2);//printf函数中转换说明不对的地方会影响其他正确的地方,使得大家都错了!!!奇怪的很嘞,不懂底层原理就是这么蒙蔽
return 0;
}
3.200000e+000, 3.2e+000, 3.20e+000, 3.200000e+000, 3.2e+000, 3.20e+000
100000000, 300000000
1.524723e-222, 3.200000e+000
-1610612736, 1074370969, -1717986918, 1074370969
下面分析一下为啥printf("%ld, %ld, %ld, %ld\n", x, y, z1, z2);中一个错大家都错的奇怪现象,看书发现:这是由于函数的参数传递机制会把这几个参数放到相邻的内存单元。具体地说,printf函数接收到4个参数, x,y z1,z2,于是把他们存入到一个叫做栈stack的地方,x和y由于被编译器转换为double,所以分别占8字节,而z1, z2分别占4字节,编译器看到%ld,就去读取4字节,于是每一个数据都错了。
好久没接触过栈了!!!
自从大二上了微机原理的课之后就没有接触过,看来果然要系统地看书学习,钻研每一个细节,不图快,才能把底层的东西,原理搞清楚,不然永远都只是在表层动土,没啥长进。
printf()也有返回值!!C函数都有返回值
它的返回值是本次打印的字符总数
#include <stdio.h>
int main()
{
int returnValue = printf("We are talking about the return value of printf().\n");
printf("returnValue=%d\n", returnValue);
return 0;
}
We are talking about the return value of printf().
returnValue=51
注意包括换行符才是51个哈
不可以在双引号之间的某处换行,双引号之间只能用换行符,不可用enter表示换行
像这样
#include <stdio.h>
int main()
{
int returnValue = printf("We are talking about the return
value of printf().\n");
printf("returnValue=%d\n", returnValue);
return 0;
}
下面这样是可以的
#include <stdio.h>
int main()
{
int returnValue = printf("We are talking about the returnvalue of printf().\n");
printf("returnValue=%d\n",
returnValue);
return 0;
}
解决办法:(如果字符串有点长)
- 最简单,用多个printf,有点麻烦
- 加个反斜杠再enter,但是需要再退格使得反斜杠前后的字符不要距离太远
#include <stdio.h>
int main()
{
int returnValue = printf("We are talking about the \
returnvalue of printf().\n");
printf("returnValue=%d\n",
returnValue);
return 0;
}
- 字符串连接,再多加一对双引号,最终把所有双引号去除,得到的字符串作为整体
#include <stdio.h>
int main()
{
int returnValue = printf("We are talking about the "
"returnvalue of printf().\n");
printf("returnValue=%d\n",
returnValue);
return 0;
}
scanf
总之%s是不可以读取多个单词的
scanf的返回值
#include <stdio.h>
int main()
{
int x, y;
y = scanf("%d", &x);
printf("y = %d\n", y);
return 0;
}
34
y = 1
printf和scanf的转换说明中*的作用
说实话,我快被printf 和scanf烦死了!!!!各种转换说明,各种修饰符,还有他们的读取字符的原理,他们可能出错的陷阱,各种各种,没想到简单的两个输入输出函数会有这么多细节,之前只是傻傻的用,······赶紧结束吧
不过这个*还是很有用的
printf中:用于控制输出字段宽度,包括浮点数的精度宽度
#include <stdio.h>
int main()
{
int width = 6, x = 123, precision = 3;
float y = 123.4567;
printf("%*d\n", width, x);
printf("%*.*f\n", width, precision, y);
return 0;
}
可以看到输出参数列表需要说明*的宽度和d的数值,浮点数里面则要用两个 ∗ * ∗,通过这样指定字段宽度还会有四舍五入的效果
123
123.457
scanf中:用于跳过一些转换说明
#include <stdio.h>
int main()
{
int x;
printf("enter 3 numbers:\n");
scanf("%*d %d %*d", &x);
printf("The second number is %d\n", x);
return 0;
}
enter 3 numbers:
1 2 3
The second number is 2
不能在scanf的格式字符串里随意加逗号,格式字符串和参数列表的格式要一致,加了逗号会出错:
#include <stdio.h>
int main()
{
int x;
printf("enter 3 numbers:\n");
scanf("%*d, %d, %*d", &x);
printf("The second number is %d\n", x);
return 0;
}
enter 3 numbers:
1 2 3
The second number is 4194432
使用固定字段宽度使得输出更美观整齐
#include <stdio.h>
int main()
{
printf("%d %d %d\n", 1, 23, 345);
printf("%d %d %d\n", 10, 230, 3450);
printf("%d %d %d\n", 100, 2300, 34500);
printf("%9d %9d %9d\n", 1, 23, 345);
printf("%9d %9d %9d\n", 10, 230, 3450);
printf("%9d %9d %9d\n", 100, 2300, 34500);
return 0;
}
printf和scanf的区别和联系
printf是从各种形式的数据(文本,数值··)到文本
scanf是从文本到数值,文本
注意scanf的参数是指针!!!虽然数组上没用&,但实际也是指针
练习
#include <stdio.h>
#include <float.h>
int main()
{
float x = 1.0/3.0;
double y = 1.0/3.0;
printf("%*.*f, %*.*f\n", 1, 6, x, 1, 6, y);
printf("%*.*f, %*.*f\n", 1, 12, x, 1, 12, y);
printf("%*.*f, %*.*f\n", 1, 16, x, 1, 16, y);
printf("FLT_DIG: %d, DBL_DIG: %d\n", FLT_DIG, DBL_DIG);
return 0;
}
可以看到float最少精确到了小数点后6位,第7位也是3,说明系统多精确了一位,double精确到小数点后15位,和FLT_DIG, DBL_DIG是一样的
0.333333, 0.333333
0.333333343267, 0.333333333333
0.3333333432674408, 0.3333333333333333
FLT_DIG: 6, DBL_DIG: 15
#include <stdio.h>
#include <string.h>
int main()
{
char family[20], given[20];
int x, y;
printf("enter your given name:\n");
scanf("%s", given);
printf("enter your family name:\n");
scanf("%s", family);
x = strlen(given);
y = strlen(family);
printf("%s %s\n", given, family);
printf("%*d %*d\n", x, x, y, y);
printf("%s %s\n", given, family);
printf("%-*d %-*d\n", x, x, y, y);
return 0;
}
enter your given name:
Melissa
enter your family name:
Honeybee
Melissa Honeybee
7 8
Melissa Honeybee
7 8