C语言学习笔记01

2022-02-05  本文已影响0人  Nefelibatas

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;
}

基本运算符之位运算

表达式的逻辑返回值:

  1. 真true

  2. 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.结果返回:

向下递推与回溯

练习:实现一个数的阶乘

#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;
}
上一篇下一篇

猜你喜欢

热点阅读