C语言学习笔记01
EOF = end of file
scanf("xxx"); // 可运行
操作系统中0表示真值,OS调用main函数,并返回return 0
cat xxx.c //读取文件内容到终端
练习
写一个程序,读入一行字符串(可能包含空格)输出这个字符串中字符的数量。
#include <stdio.h>
int main(){
char str [100] = {0}; //初始化字符
//加入正则表达,除了换行其余都包含空格输出
while (scanf("%[^\n]s",str) != EOF){ //str既是数组也是地址首字符,所以无需&
getchar(); //获取字符串
printf(" has %d chars!\n",printf("%s",str));
}
return 0;
}
sprintf:s->string,将输出内容打印到字符串里
fprintf: 输出到文件里
应用: 字符串拼接
#include <stdio.h>
int main(){
char str[100] = {0};
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
sprintf(str,"%d.%d.%d.%d",a,b,c,d);
// FILE *fp = fopen(./output,"w"); 打开文件 w+可读可写
FILE *fp = fopen(./output,"a+");
fseek(fp,0,SEEK_END);
fprintf(fp,"str = %s\n",str); //写入文件
fclose(fp); //关闭文件
return 0;
}
sscanf:从字符串中获取输入
fscanf:可进行格式化的读操作
./a.out < input 输入重定向
#include <stdio.h>
int main(){
char str[100] = {0};
int a,b,c,d;
FILE *fp = fopen("./output","a+");
fseek(fp,0,0);
while(fscanf(fp,"%d.%d.%d.%d\n",&a,&b,&c,&d)!= EOF){
printf("%d %d %d %d\n",a,b,c,d);
}
fclose(fp);
return 0;
}
基本运算符之位运算
表达式的逻辑返回值:
- 真true
- 假
bool
false
非0即为真
!真 = 假 【关系运算取非】
!0 = 真(1) !(null) = 真(1)
~位 【数学运算】
位运算仅支持整型!
1.必须二进制表示下进行的一类运算
2.对应二进制位进行计算(有2进1)
二进制计算
1.对应位上的数值(0、1)
2.对应位的权值(2^0, 2^1,... 2^n)
进制转换(如二进制到十进制)
对应位的权值( 2^0 , 2^1 ... 2^n)与对应数值相乘之和
&按位与(二进制位和运算)
|: 有1为1,全0为0
^ : 抑或,相同为0,不同为1
2^2 = 0
0^n = n
【重要】抑或运算是抑或运算的逆运算
即a^b = c,则c^a = b,c^b =a
~按位取反
<<左移 >>右移
其他进制中,A对应11,B对应12....
i++:先完成运算,再加1
++i:先加1,再完成运算
位是最小单元,32位表示二进制位,2^32个状态
第32位表符号,0为负数,1为正数[-2^31 , 2^31-1]
2_(10) = 10_(2),取反为1..(中间都为0)..01(是个负数)
2(10) << 1
左移,低位补0
2(10)=10(2) << 1 = 100(2) = 4(10)
3(10) >> 1
右移,高位补符号位,根据原符号位补充0或者1
3(10) = 11(2)= 0...11(2) >> 1
= 0..01(2) = 1(10)
任何一个字符在ASCII编码下都可以映射为整型值,
'A' = 65
'a' = 97
数学函数
头文件math.h
pow:指数函数
原型:double pow(double a,double b); 返回a^b的结果
double:8个字节,float:4个字节
开平方函数(正值)
原型:double sqrt(double x);返回sqrt(x)
上取整函数,只要有小数位就能向上取
原型:double ceil(double x);返回ceil(x)结果
下取整函数,只要有小数位就能向下取
原型:double floor(double x);返回floor(x)结果
fabs函数,实数绝对值
原型:double fabs(double x);返回fabs(x)
log函数,以e为底对数函数
原型:double log(double x);返回log(x)
log10函数,以10为底对数函数
原型:double log10(double x);返回log10(x)
注意使用换底公式
acos函数,反余弦函数arccos(x)
原型:double acos(double x);返回acos(x)
x:角度的弧度值
abs函数,整数绝对值
头文件:stdlib.h
原型:int abs(int x);返回abs(x)
// x的立方根
#include <stdio.h>
#include <math.h>
int main(){
double x;
scanf("%lf",&x);
printf("%lf",pow(x,1.0/3.0));
return 0;
}
//读入一个角度值,将角度值转为弧度值
#include <stdio.h>
#include <math.h>
// 角度值*Π/180
int main(){
double x;
const double pi = acos(-1); // Π
scanf("%lf",&x);
printf("%lf\n",x*pi / 180);
return 0;
}
//按位取反实现循环
int main(){
int a,b;
while(~scanf("%d%d",&a,&b)){
printf("a = %d,b= %d \n",a,b);
}
}
原码 -1(10) = 10000...01(2)
反码 11111...10
补码 = 反码+1(末位对齐) = 11111...11
位运算速度快!
//利用异或^交换两个数值
int main(){
int a,b;
while (~scanf("%d%d",&a,&b)){
printf("a=%d,b=%d\n",a,b);
a ^= b; // a`= a^b
b ^= a; // b` = b^a`
a ^= b; // a`^= b`
printf("swap : a=%d,b=%d\n",a,b);
}
return 0;
}
int b =2,c = 0;
这是一条语句还是两条? 1条,逗号表达式
逻辑返回值为还是0? 逻辑返回值为0,假值
int b =1,c = 2; 逻辑返回值为2,真值
int b =2,c = 1; 逻辑返回值为1,真值
总结:逗号表示式最后的部分当作逻辑返回值
0 为逻辑返回值假值
1 为逻辑返回值真值
复合语句表现形式{}
if(表达式) + 单语句
if(表达式) + 复合语句表现形式{}
练习
//输入正整数n,根据分数输出分数档位
#include <stdio.h>
int main(){
int n;
scanf("%d",&n);
if(!n){
printf("FOOLISH \n");
} else if (n < 60){
printf("FAIL\n");
} else if (n < 75){
printf("MEDIUM\n");
} else if (n <= 100){
printf("GOOD\n");
}
return 0;
}
switch(a){ //a一定为整型值
case 1: ...;
break; //遇到break退出
case 2: ...;
case 3: ...;
default:...//以上皆不是,则输出..
}
#include <stdio.h>
int main(){
int a,b;
scanf("%d%d",&a,&b);
printf("%s\n",a%b==0?"YES":"NO");
return 0;
}
#include <stdio.h>
int is_val(int n){
int a = n%10;
int b = n/10 %10;
int c = n/100;
return (a==9 || b==9 || c==9);
}
#include <stdio.h>
int main(){
int n;
scanf("%d",&n);
printf("%s\n",is_val(n)?"YES":"NO");
return 0;
}
#include <stdio.h>
int main(){
double n;
scanf("%lf",&n);
printf("%g\n",n<0?-n:n);
return 0;
}
#include <stdio.h>
int is_val(int n){
return ((n%7==0) &&(n & 1) );//等价于n%2
}
int main(){
int n;
scanf("%d",&n);
printf("%s\n",is_val(n)?"YES":"NO");
return 0;
}
帮助cpu的分支预测
#define likely(x) __builtin_expect(!!(x),1) //likely代表x经常成立
#define unlikely(x) __builtin_expect(!!(x),0) //likely代表x不经常成立
//likely(x)中!!(x)逻辑归一化所有真值映射为1,unlikely(x)同理
OS告知CPU当前x是否成立。
CPU如何执行指令?
1.早期 串行。外存转内存,内存到CPU
2.并行 流水线
CPU分支预测,若预测失误,则从并行转为串行
不影响代码可读性的情况下,尽量减少分支结构if
循环结构
while(表达式){ //表达式为真,代码就会被执行一次
// 代码;
}
do{
//代码块
} while (表达式); //每当代码段执行1次,就会判断1次表达式是否为真
for(初始化;循环条件;执行后操作){
//代码块
}
int main(){
int i =0;
for(;;){
if(i>100) break;//终止所有循环
// continue跳出单层循环
printf("%d\n",i);
}
return 0;
}
int main(){
int a=0,b=0;
if((a++) && (b++)){
//a++:先访问a再a+=1,逻辑&&运算,有0则0,最后输出false
//若为++a && ++b,则最后输出true
printf("true\n")
} else{
printf("false\n")
}
printf("a=%d,b=%d\n",a,b);//最后输出a=1,b=0,b++未执行,因为逻辑&&运算遇0 则0
return 0;
}
// 同理运用于其他逻辑运算符中,常见于格式输出
练习:输入一组随机数字,要求输出每个数之间空格,判断其中有多少个奇数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(){
int n,cnt = 0;
srand(time(0)); //随机数,time(0):linux系统中的系统时间,单位(秒)
scanf("%d",&n);
for(int i=0; i<n ;i++){
int val = rand() % 100;
cnt += (val & 1); // %2 等价为 1,%4等价于3,%6不等价于任何操作
i && printf(" ");
printf("%d",val);
}
printf("\n");
printf("cnt : %d\n",cnt);
return 0;
}
#include <stdio.h>
int digit(int n){
int cnt = 0;
do {
n /= 10;
cnt += 1;
} while(n);
return cnt;
}
int main(){
int n;
while(~scanf("%d",&n)){
printf(" %d has %d digits!\n",n,digit(n));
}
return 0;
}
练习:回文数判断
int is_val(int n){
int x = n,sum = 0;
while(x){
sum = sum * 10 + x % 10; // x%10求出最后一位
// x/10 除了最后一位的前n位
// x*10 还原前n位,找出前n位的最后一位
x /= 10;
}
return sum == n;
}
int main() {
int n;
scanf("%d",&n);
for(int i=1; i<= n; i++){
if(!is_val(i)) continue;
printf("%d\n",i);
}
}
}
函数
int is_val(int n){
// int 返回值
// is_val 函数名
// int n 参数声明列表
...
}
return 1;
}
练习:实现一个程序,先读入两个整数:k和b,输出y=k*x+b直线方程中x=1到x=100间y的值。
#include <stdio.h>
int f(int k,int b,int x){
return k*x + b;
}
int main(){
int k,b;
scanf("%d%d",&k,&b);
for( int i =1;i<= 100;++i){
printf(" f(%d) = %d \n",i,f(k,b,i));
}
return 0;
}
函数调用
#include <stdio.h>
void func(){
printf(" func: 2+3 = %d",2+3);
return ;
}
int main(){
func();
return 0;
}
函数声明
#include <stdio.h>
void func(); //函数内容放置主函数下方,需要先声明
int main(){
func();
return 0;//在OS中,0表示真值,c语言中0表示假
}
void func(){
printf(" func: 2+3 = %d",2+3);
return ;
}
如何让其他函数优于主函数去执行
__attribute__((construcotr))//宏定义与预处理命令
void func(){
...
}
__attribute__((construcotr))//宏定义与预处理命令
void func1(){
...
}
int main(){
return 0;//在OS中,0表示真值,c语言中0表示假
}
习题: 464.统计闰年
// 4年1闰,100年1闰,400年再闰
//输入两个年份x和y,统计并输出公元x年到公元y年之间的所有闰年数(包括x年和y年),1≤x≤y≤3000。
#include <stdio.h>
int check_year(int y){
return !!((y % 4 == 0 && y % 100) || y % 400 == 0);//逻辑归一化
}
int main(){
int x,y,cnt = 0;
scanf("%d%d",&x,&y);
for(int i = x;i <= y; i++){
cnt += (check_year(i));
}
printf("%d\n",cnt);
return 0;
}
练习:定义一函数digit(n,k)分离出整数n从右边数第k个数字。
如digit(2076,1)等于6,而digit(2076,5)等于0。
main函数输入n和k,调用digit(n,k)输出答案,n在long long范围内。
#include <stdio.h>
int digit(long long n, int k){
int temp = 0;
for (int i = 0; i< k; i++){
temp = n % 10;
n /= 10;
}
return temp;
}
int main(){
long long n,k;
scanf("%lld%lld",&n,&k);
printf("%d\n",digit(n,k));
return 0;
}
练习:输入一个正整数n,求1~n之间的"回文数"的个数。
回文数是指一个数倒过来和原数一样,如12121、11、1221、1是回文数。
#include <stdio.h>
int is_val(int n){
int x = n,temp = 0;
while(x){
temp = temp * 10 + x % 10;
x /= 10;
}
return temp == n;
}
int main(){
int n,cnt = 0;
scanf("%d",&n);
for(int i=1;i <= n;i++){
cnt += (is_val(i));
}
printf("%d\n",cnt);
return 0;
}
递归
程序调用自身的编程技巧
递归程序的组成部分
给予相关的语义信息
1.边界条件处理
2,针对于问题的【处理过程】和【递归过程】
3.结果返回:
- return
- 传出参数(传地址)
- 全局变量
- 引用
向下递推与回溯
练习:实现一个数的阶乘
#include <stdio.h>
int fac(int n){
if (n == 1) return 1;
return n * fac(n-1);
}
int main(){
int n;
while(~scanf("%d",&n)){
printf("fac(n):%d\n",fac(n));
}
return 0;
}
n阶层 循环与递归实现的区别:
递归好实现,但会频繁使用栈结构,时间复杂度高
系统栈内存:linux里8mb、WIN中2mb
函数内定义的局部变量都是占用系统栈空间
除了malloc、calloc、realloc动态。
8mb = 800w字节,在函数内数组定义不得超过200w
爆栈错误,栈溢出
函数指针:将函数作为参数进行传递
变量的作用:存值
函数指针的作用:存储一个函数
** 指针数组:本质是数组**
int (*f1)(int) f1是函数指针变量名,表示接收的函数
(int):应接收函数的参数列表的形式
int g( int (*f1)(int), int (*f2)(int),int (*f3)(int)){
if(x < 0){
return f1(x); //f1(x)函数调用
if(x > 0){
return f2(x);
}
return f3(x)
}
变参函数
实现可变参数max_int,从若干个传入的参数中返回最大值。
int max_int(int a, ...);
如何获得a往后的参数列表? va_list类型的变量
如何定位a后面第一个参数的位置? va_strat函数
如何获取下一个可变此参数列表中的参数?va_arg函数
如何结束整个获取可变参数列表的动作?va_end函数
#include <stdio.h>
#include <inttypes.h>
#include <stdarg.h>
int max_int(int n,...){
int ans = INT32_MIN,temp;//INT32_MIN整型符号常量
va_list arg;
va_strat(arg,n);
while (n--){
temp = va_arg(arg,int); // va_arg预处理命令
if(temp > ans) ans = temp;
}
va_end(arg);
return ans;
}
int main(){
printf("%d",max_int(5,1,2,3,2,14));
return 0;
}