printf()
int printf(char *format, arg1, arg2, …)
%4d | 字段最小宽度为4,不足左边用空格填充 |
%.4f | 小数点位数 |
%+4d | 有符号数若为正则显示+号,为负显示-号 |
%04d | 不足使用0填充,而不是空格 |
%g(%G) | 根据数值自动选择%f或者%e(%E) |
printf 中的 * 号:printf(“Weight=%*.*f\n”,width,precision,weight); //参数指定精度
printf 参数中 float 被自动转换为 double,用 %f 和 %lf 修饰符均可
printf 返回所打印字符的数目(包括空格和不可见的换行),若输出错误则返回负数
printf 将各种数据类型的参量合成为一个字符串输出
printf 根据format参数的指示进行数据解释)
printf("%d%d", 1,1,1); //warning,提供参数个数多于所需个数
printf("%d%d%d", 1,1); //第三个%d无对应值,结果因系统而异,最好结果是无意义的值
一般认为printf()参数压栈顺序为从右至左
char *p = “abc”
printf(“%c%c%c”,*p,*p,*p++) //输出结果为bba
(注意:一元运算符是由右向左结合的,因此*p++会被解释为*(p++),由于p++是先使用p的原始值再加1,所以*p++取的是*p的值)
一种说法:参数压栈顺序只和编译器相关
https://blog.csdn.net/black2360/article/details/70229354
另一种说法:由于printf是变长参数表,只能通过第一个参数format确定个数,如果从左至右压栈,那么将找不到format,也就不知道究竟需要多少参数
https://www.cnblogs.com/mini-coconut/p/9267469.html (部分认同)
打印长字符串的三种方法:
- 使用多个printf语句
- 用\和键盘回车结束第一行,下一行必须从最左边开始,否则会打印缩进空格
- 采用字符串连接。多个只用空白字符分隔的字符串会被连接成一个
scanf()
scanf 从输入流中读取字符序列,按照 format 中的格式说明符序列进行解释,并把结果保存到其余参数中,用于指定相应输入保存的位置(结合形参实参理解为何用指针)
%10s:最大字段宽度,在达到最大字符宽度或遇到第一个空白字符时(不管哪一个先发生),停止对输入项的输入
%x 要求scanf识别十六进制数字 a~f,A~F
浮点说明符要求scanf识别小数点、指数记法、新的 p 记法
scanf 把字符串放到指定数组中时自动添加’\0’形成C字符串
scanf 中的 * 号:scanf(“%*d%d”,&n); //跳过一个整数,把第二个整数赋给n
scanf(“%d,%d”,&n,&m); //必须输入一个整数,输入一个逗号,再输一个整数
scanf(“%c”,&ch); //读取遇到的第一个字符
scanf(“ %c”,&ch); //读取遇到的第一个非空白字符
scanf读取输入时会在遇到的第一个空白字符(空格、制表符、换行符)处停止读取,因此使用s%读取字符串时只会读取一个单词(从第一个非空白字符开始读)
float 修饰符用 %f,double用 %lf,不通用
当scanf扫描完其格式串,或者碰到某些输入无法与格式控制说明匹配的情况是,函数将终止,同时将成功匹配的输入项的个数作为函数值返回。如果到达文件尾则返回EOF。注意,EOF与0不同,返回0表示下一个输入字符与格式串中的第一个格式说明不匹配。下一次调用scanf时将从上一次转换的最后一个字符的下一个字符开始继续搜索。
举例:假定使用%d读取一个整数,scanf()函数开始每次读取一个输入字符,它跳过空白字符,直到遇到第一个非空白字符,因为它试图读取一个整数,所以scanf期望发现一个数字字符或者+-号,如果发现了,就把它保存下来读取下一个,若是数字则保存,反复读取保存,直到遇到第一个非数字字符为止,此时认为读到了整数尾部,将保存的字符串根据%d要求转换为一个整数存放到指定变量中,并把读入的非数字字符放回输入。这就意味着下次开始读取输入时将从这个被放弃的非数字字符开始。如果第一个非空格字符不是数字或+-号,比如A,scanf读取后会再放回,并终止,下次再读取时仍从A开始读
0 开头表示八进制, 0x 开头表示十六进制
除法(/)和乘法类似,同号得正,异号得负,如果是整数除法,结果直接舍弃小数部分
sizeof返回值可用%zd或%u打印
逗号运算符
逗号运算符保证被它分开的表达式自左至右计算,整个逗号表达式的值是最右边成员的值
如:x=(y=3,(z=++y+2)+5);
首先把y赋值为3,把y递增为4,然后把4加上2,把结果6赋给z,然后把z+5的结果,即11赋给x
x=249, 500;
左子式x=249,右子式500,整个表达式结果为500,x=249。相当于x=249; 500;
逗号优先级低,如果是 x = (249, 500) 那么 x 等于500
如果没有花括号指明,else和它最近的一个if配对
switch
switch(ch)
{
case ‘a’: statement1;
break;
case ‘b’: statement2;
break;
case ‘c’: statement3;
break;
default: break;
}
switch 判断表达式应具有整数值(包括char类型),case 标签必须是整型(包括char)常量或者整数常量表达式
如果没有 break 语句,将继续执行下一个 case 标签如果 switch 语句位于一个循环中,则可以把 continue 用于 switch 语句的一部分,在这种情况下,continue 将导致跳过该次循环其余部分,包括 switch 的其余部分
C99允许指定初始化项目,如int arr[6]={[5]=4};
int arr[sizeof(int)+1]; //允许,sizeof表达式被认为是整数常量
数组只有第一维可以省略长度,原因:只有第一维能数出来,其他维度初值允许省略,比如 int arr[][3] = {{1,2},{3,4,5}};
int arr[] 也可以表示指向 int 的指针,但是只能在声明形式参量时用
如int sum(int *arr)与int sum(int arr[])是完全等价的,后者被自动转化为前者
对指针加1,等价于对指针的值加上它指向的对象的字节大小(指针类型的意义就是指定大小)
打印地址可以用%p(如果不支持可用%u或%lu)
字符常量’\0’数字值为0
char greet[] = "How are " "you?"; //等价于下面的形式
char greet[] = "How are you?"; //最后的 ’\0’ 会自动加上
sizeof(greet) / sizeof(*greet) 的值为13,即包含最后的空字符
char *greet = "How are you?" //也可以用指针建立字符串
若用指针声明数组,不但要为字符串预留存储空间,还要为指针变量greet预留空间。不能通过其修改字符串内容,因为字符串常量存储在静态存储区。可以改变greet的值使其指向其他地方。
字符串常量属于静态存储,在程序整个运行过程中只存储一份,整个引号的内容作为指向该字符串存储位置的指针
gets()读取换行符(不包括换行符)之前的所有字符,最后加一个空字符\0,它将读取换行符并丢弃,这样下一次读取就会在新的一行开始。(C11已将其废弃)
gets()并不检查字符数组容量是否够大。返回值为字符串的地址,如果出错或遇到文件尾返回空指针NULL。函数原型在stdio.h中
puts()显示字符串时自动在最后添加一个换行符【puts(“\n”);会打印两个换行符】
const char *mytal[3]={"how are you", "I am fine", "thank you"};
mytal中存放的其实是3个地址,三个字符串未必连续存放
字符串函数
strlen(s) | 返回字符串s的长度(不包括空字符) |
strcpy(s1,s2) | 将s2(包括空字符)复制到s1指定的位置,返回值为s1 |
strncpy(s1,s2,n) | 复制的字符不超过n个,若s2字符不足n个,则s1中用空字符填充,若大于或等于n个,空字符就不会被复制 |
strcat(s1,s2) | 将s2复制到s1结尾,s2的第一个字符覆盖s1结尾的空字符,不检查s1是否能容下,返回值为s1 |
strncat(s1,s2,n) | s2前n的字符添到s1尾,最后添加一个空字符,返回s1 |
strcmp(s1,s2) | 若s1字符串在机器编码顺序(一般为ASCII码)中落后于s2,返回一个正数,相同返回0,否则返回负数 |
strncmp(s1,s2,n) | 只比较字符串的前n个字符 |
strchr(s,ch) | 在字符串s中搜索ch,返回首次出现的地址,找不到返回NULL |
strstr(s1,s2) | 在s1中搜索s2,返回首次出现的地址,找不到返回NULL |
翻译单元:一个源代码文件和它所包含的头文件
三个重要概念:作用域、链接、存储期
作用域主要包括块作用域和文件作用域,作用域外不可见
链接:外部链接、内部链接(仅翻译单元内可见)、无链接
存储期就是变量的生命周期,主要有静态存储期和动态存储期
块作用域 | 无链接 | 静态存储期(static 声明) |
动态存储期 | ||
文件作用域 | 外部链接 | 静态存储期 |
内部链接(static 声明) |
static的作用:持久(静态存储期)、默认初始化(0)、隐藏(翻译单元外不可见)
struct book{
char *title;
char author[10];
float value;
};
//声明结构体变量
struct book library;
//也可以将以上两步合二为一
struct book{
char *title;
char author[10];
float value;
} library;
//结构体变量初始化
struct book library = {"C primer plus", "name", 99};
struct book library = { .value = 99, .title="C primer plus", .auther="name"}
~ & | ^ 四个位运算符只能用于整型数据,包括char
位运算不改变操作数的值,如 ~a 操作后 a 的值不变
打开位:1|n=1; 0|n=n (n表示0或1)
关闭位:0&n=0; 1&n=n
位转置:1^n=~n; 0^n=n
查看某位的值:用&:要查看的位置1,其他位置0
左移(<<):空位补0,移出位舍弃
右移(>>):移出位舍弃,对于unsigned空位补0,有符号类型依赖于机器(补0或补符号位)
预处理器主要完成宏替换、条件编译、文件包含
1.文件包含
将指定文件中所有内容复制到当前文件
#include"文件名"
#include<文件名>
双引号表示先在源文件所在目录下查找,若找不到,或者用<>,则根据相应规则到指定位置查找
2.条件编译
防止重复包含
#ifndef HEADER
#define HEADER
/*header.h内容*/
#endif
选择性包含
#if SYSTEM == SYSV
#define HDR “sysv.h”
#elif SYSTEM == BSD
#define HDR “bsd.h”
#else
#define HDR “default.h”
#endif
#include HDR
3.宏替换
#define定义的名字作用域从其定义点开始,到被编译的源文件的末尾结束
替换文本可以是任意的,例如:#define FOREVER for(;;)
getchar和putchar(以及ctype.h中的函数)经常被实现为宏,以避免在每次执行输入输出一个字符这样简单的操作时都要调用相应函数而造成系统效率的下降
最好在宏定义中把每个参数都用括号括起来,整个表达式也用括号括起来,以预防与优先级有关的问题,例如 #define max(a,b) ((a)>(b)?(a):(b))
由于作为参数的表达式要重复计算两次,如果表达式存在副作用(比如含有自增运算或输入输出),仍可能出现不正确情况。
例如 max(++a,b)展开后:((++a)>(b)?(++a):(b))
若>成立则++a会被计算两次
而如果将max定义为函数就不会出现这种问题,++a只会在传参时计算一次