学习笔记(C基础+进阶)

Linux C基础&进阶(学习笔记)

  只是为了记录自己的学习过程。如若有网友发现有错误,请帮我指正,十分感谢。全篇并没有将所有的C知识都一一罗列出来,例如不包括一些数据类型等很基础的东西,只是将很重要的知识给整理了。本文章仅适合用来复习C 知识,还是需要一定的基础的。所有代码均在Linux平台上实现,使用的kate编译器。

hello.c

    #include<stdio.h>   //预处理 插入头文件
    int main()  //若没有int,C 默认加上int
    {
        printf("Hello World\n");
        return 0;/*返回0表示正常,非0则不正常*/
    }
kate(Linux中)输入:

gcc -c hello.c: 生成 hello.o目标文件,对应于windows上的xxx.obj文件。
gcc hello.o -o hello2:生成hello2可执行文件。若取消“-o hello2”则默认生成a.out可执行文件。
./hello2:执行hello2文件,打印Hello World;
(gcc hello.c: 将直接生成a.out 可执行文件)

源码&补码

现在计算机中使用补码来存储数字,非负数的补码与源码相同,负数的补码需要按位取反后再加1成为补码。

-1 的源码为 10000001  1 的源码为 00000001
   反码为 11111110    反码为 00000001
   补码为 11111111    补码为 00000001

unsigned int 为非负的int整形

Ascii.c

#include<stdio.h>  
int main()
{
    printf("%c,%c,%c,%c,%c\n",0,32,97,65,48);   //, ,a,A,0
    printf("%c\n",'7'+1);               //8
    printf("%c\n",'7'+'1');             //h  
    /*ASCII 每个字符是一个编码a-z(97-122) ,A-Z(65-90) ,0-9(48-57);*/

    printf("%hd   %hd   %hd   %hd\n",32767,32768,32769,32770);  //32767   -32768   -32767   -32766

    printf("Hello Data\n");
    printf("%i   %f   %c \n",123, 45.6, 65);   //123   45.600000   A
    printf("%i   %g   %c \n",123, 45.6, 's');   //123   45.6   s
    printf("%x   %o",763, 763);           //2fb   1373
    printf("%g\n",45.6);                         //45.6

    return 0;        
} 

计算机管理内存以字节为单位,1byte = 8bit; 一个汉字扩展ASCII GB2312编码 1 个汉字 2 byte;

 float 符号位1,阶码8,小数23
 double 符号位1,阶码11,小数52
 可通过sizeof(类型)查看字节数, sizeof(5 + 4.4)=8, 只关心数据类型;

溢出: 

 分为上溢出和下溢出。对于整数而言一处会导致回绕,小数不会回绕,会变成无穷(正无穷和负无穷);
 整数考虑溢出,小数考虑误差;
 有符号整型的最大值: 2^(位数-1)-1    最小值:-2^(位数-1)
 无符号整型的最大值: 2^(位数)-1     最小值: 0

格式化输出: 

 int、short、long => %i/%d、%hd、%ld    unsigned int => %u
 float、double => %g(去掉尾部的000…) => %f、%lf(保留尾部000…)
 char、char* => %c、%s
 十六进制%x  八进制%o  地址%p

宏定义

  #define 宏替换,预处理的时候替换。这里仅替换,并无优先结合性;

位运算

  与运算 &  同位都为1则该位值为1  00001011 & 00000101 = 00000001;
  或运算 |    同位有1则该值为1    00001011 | 00000101 = 00001111;
  异或  ^   同位不同则为 1       00001011 ^ 00000101 = 00001110;
  取反 ~  按位取反         ~00001011 = 11110100;(~11 = -12)
  左移 <<   所有位向左移动     00001011 << 3 = 01011000;(相当于11*2*2*2)
  右移 >>   所有位向右移动     00001011 >> 3 = 00000001;(相当于11%2%2%2)

scanf.c

#include<stdio.h>  
int main()
{
    int n =0;
    float f =0;
    double d = 0;
    char c =0;
    char str[10] ={0};

    printf("%i  %i  %i\n",n++,n++,n++); //输出2 1 0 ,不同的编译器产生不同的结果

    scanf("%d%f%lf",&n,&f,&d);  //此处一定要对应格式输入,如果第一个输入1.1 , 则只读取1,留下0.1会被f读取
    printf("%d , %f , %lf\n",n,f,d);

    scanf(" %c",&c);    //注意输入的格式类型为c的话,一定要再格式符前加空格“ ”;
    printf("%c , %d\n",c,c);

    scanf("%s",str);
    printf("%s\n",str);

    printf("Num was inputting success : %d\n",scanf("%f %lf",&f,&d));   //这里能读取两个
    printf("Num was inputting success : %d\n",scanf("%f,%lf",&f,&d));   //这里只能读取第一个

    scanf("%d\n",&n);   //这里输入后,不会马上输出接下来的printf内容,而回等待下一个输入。
    printf("aaa %d\n",n);
    scanf("%d,%d\n",&n,&n);
    printf("bbb %d\n",n);

    return 0;
}

  1.比较两个小数是否相等,不能使用精确的比较,而是比较近似;
  2.printf的计算顺序是从右到左;
  3.scanf(“格式符”,地址表); 返回值为成功读取的字符个数
   scanf中如果有空白字符,则直到遇到空白字符才读取;
   scanf中只要格式占位符;如果scanf有空白字符,则跳过所有空白字符,直到遇到非空白字符。
  4.scanf(“%[^\n]%*c”); 表示抛弃,[]表示范围,^表示非,抛弃一个字符
            %*c表示读入一个字符且不保存到变量里
            %*[^\n]表示读入一个字符串,由范围[]内定,即^\n表示非换行符的都能读取。遇到\n,scanf则会返回
   可以使用这个替代
    {
     char c;
     do{
        scanf(“%c”,&c);
     }while(c!=’\n’);
    }

circle.c

#include<stdio.h>

enum TEST{a=2,b=1,c};

int main()
{
    int n;

    do{
        puts("Put in your number:");
        scanf("%i",&n); //此处输入字母会进入死循环。由于输入的字符与对应的格式符不对应,无法取走输入缓冲区中的字符;
    }while(n<100);

    puts("Out of circle1 !!!!");

    do{
        puts("Put in your number:");
        //scanf("%d",&n);
        if(scanf("%d",&n)==1){
            printf("Input Success ,and number is %d\n",n);
        }else{
            printf("Fail Input !!\n");
        }
        scanf("%*[^\n]%*c");    //这个是清空输入缓冲区
    }while(n<100);

    puts("Out of circle1 !!!!");

    enum TEST test = c; //定义枚举变量,enum不能缺少,赋值类型必须对应相同枚举类型
    typedef enum TEST T;    //通过类型定义符给类型起别名
    T test2 = b;

    return 0;
}  

  循环的几种形式:
    for(;;){} 、 do{}while(true) 、 while(true){}

array.c

#include<stdio.h>

int main()
{
    int a[5]={11,22,33,44,55};              //数组的初始化,数据不能多,可以少,少的部分全用数组0填充
    printf("a = %p\n",a);                   //a = 0xbfa2e868
    int i;
    for(i=0;i<5;i++)
    {
        printf("&a[%d] = %p\n",i,&a[i]);    //&a[0] = 0xbfa2e868
    }
    *a+=100;
    printf("%d\n",a[0]);                    //111  a[0]也可以写成0[a],是一样的
    for(i=0;i<5;++i)
    {
        printf("a + %d = %p\n",i,a+i);      //a + 0 = 0xbfa2e868
    }

    printf("\n========================\n");
    int x =123;
    int y =456;
    for(i = -2;i<8;i++)
    {
        printf("%d\n",a[i]);
    }
    printf("\n");
    printf("x = %d , y = %d\n",x,y);        //x=123,y=456
    for(i =-2;i<8;i++)
    {
        a[i]=0;                             //有可能改变x或者y的值,vs编译器会严重报错
    }  
    printf("x=%d , y=%d\n",x,y);            //x=0,y=0  变量再内存中的关系未必按照定义的顺序


    return 0;
}  

  1. a={}; 给数组赋值会出错;(只能初始化)
  2. 数组名代表这个数组的第一个元素的地址;
  3. a + i => &a[i];  *(a + i) => a[i];  a[i] => i[a]; 
  4. 数组的另一种访问形式: int array[3]={1,2,3};  =>  2[array] == 3;
  

string.c

#include<stdio.h>
#include<string.h>

int main()
{
    char a[100]="hello world!!";
    printf("%s\n",a);                           //hello world!!
    printf("%d\n",sizeof("hello world!!"));     //14 字符串会在末尾自动补上‘\0’
    printf("%c\n","hello world!!"[2]);          //l 没有名字的字符串
    printf("%s\n","hello world!!"+3);           //lo world!!

    strcpy(a,"hello google!!");                 //拷贝字符串(并覆盖)
    puts(a);                                    //hello google!!
    strcat(a,"hello Baidu!!");                  //末尾追加字符串(要确保位置足够)
    puts(a);                                    //hello google!!hello Baidu!!
    printf("%d\n",strlen(a));                   //27    字符数,不包括‘\0’
    printf("%s\n",strchr(a,'h'));               //hello google!!hello Baidu!!   从左向右寻找
    printf("%s\n",strrchr(a,'h'));              //hello Baidu!!  从右向左寻找
    printf("%s\n",strstr(a,"goo"));             //google!!hello Baidu!!  字符串查找
    printf("%i\n",strcmp("abc","ab"));          //1     a>b
    printf("%i\n",strcmp("abc","abc"));         //0     a==b
    printf("%i\n",strcmp("ab","abc"));          //-1    a<b

    printf("===========================================");



    return 0;
}  

  1.两个字符串、字符数组不能直接使用 == 来比较,使用比较符比较的都是在比较 ‘地址’;要使用strcmp函数比较;
  2.”abcdefg” 这样单独的字符串是常量,是只读的。例如“ “hello”[0] = ‘H’; ”会报错。

struct.c

#include<stdio.h>
#include<string.h>

typedef char CStr[100];                     //类型重定义char[100]的定义方式!!!!
typedef enum {MALE,FEMALE} gender;
typedef struct {
    char name[20];
    int age;
    double income;
    gender s;
} person;

typedef struct {
    char aa;        //1
    char a;         //1
    short b;        //2
    int c;          //4
    double d;       //8
} test;             //16

typedef struct {
    char a;         //1
    double d;       //8
    char aa;        //1
    int c;          //4
    short b;        //2
} test2;            //24

int main()
{
    person a = {"A",18,8005,MALE};
    person b = { age:15,name:"B",income:9000,s:FEMALE};
    person c =a;
    CStr s="1212";

    printf("person a : %s, %i, %g, %s\n",a.name,a.age,a.income,a.s == MALE?"man":"female");
    printf("person b : %s, %i, %g, %s\n",b.name,b.age,b.income,b.s == MALE?"man":"female");
    printf("person c : %s, %i, %g, %s\n",c.name,c.age,c.income,c.s == MALE?"man":"female");


    printf("%d\n",sizeof(test));    //16    
    printf("%d\n",sizeof(test2));   //24
    return 0;
}  

  1.结构体的成员变量内存分配会进行对齐,会按照结构体中最大的基本数据类型来决定倍数,当超过4时,以4为倍数;
  2.可以使用pragma pack(X)设定对齐倍数x,pragma pack()则使用默认的对齐倍数(4),64位默认为8倍数;
  3.为使结构体能保证效率的同时占用最少空间,一般越小的数据类型放越前面;

myFunc.h

#include<stdio.h>
void sayHello();  

myFunc.c

#include<stdio.h>
#include"myFunc.h"

static char S[10]="For Test";   //此处定义的静态数据,只能在本文件中使用
char w0[7][10]={                //静态数据和全局数据没有垃圾数据
    "Monday",
    "Tuesday",
    "Wednesday",
    "Thursday",
    "Friday",
    "Saturday",
    "Sunday"
};

void sayHello()
{
    printf("Hello World!!!\n");
}  

main.c

#include<stdio.h>
#include"myFunc.h"

extern char w0[7][10];      //告诉编译器w0已在外部文件中定义,而不要报错(或者放在头文件中声明)

char* weekday(int n)
{
    return w0[n];
}

/*====可变长参数的函数====*/
int max(int num, ...)
{
    va_list arg_ptr;                    //va_list是个类型,保存可变长度参数表
    va_start(arg_ptr,num);              //指定从哪个参数后面开始,用arg_ptr保存num之后的参数表
    int i =0;
    int maxvalue = va_arg(arg_ptr,int); //从参数表中取一个int类型的参数
    for(i =0 ; i< num;++i)
    {
        int data = va_arg(arg_ptr,int); //要取出一个什么类型的数据
        if(data>maxvalue)
        maxvalue = data;
    }
    va_end(arg_ptr);                    //释放可变长度参数表
    return maxvalue;
}

/*====递归====*/
int f(int x)
{
    if(x<=0)
        return 5;
    else
        return 2*f(x-1)+3;
}


int main()
{
    sayHello();
    printf("%s\n",weekday(2));

    register int i;                     //寄存器,请求编译器把变量放到寄存器里不放到内存中。有可能成功。不能取地址
    for(i =0;i<5;i+=)
    {
        printf("%d ",i);
    }

    printf("%d\n",max(5,44,23,13,55,23));   //55

    return 0;
}  

以上两个文件要同时编译和链接

kate(Linux中)输入:

  gcc -c myFunc.c :    生成myFunc.o
  gcc -c main.c :       生成main.o
  gcc myFunc.o main.o :   生成a.out
  ( 或者gcc myFunc.c main.c )  直接生成a.out,但是这种办法需要重新编译所有文件
  

volatile关键字:

  1.类型修饰符。被设计用来修饰不同线程访问和修改的变量。编写多线程必须用到。
  2.能够确保本条指令不被编译器优化而省略,且要求每次直接读值。
  3.如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,
  如果这个变量由别的程序更新了的话,将出现不一致的现象。

不定长函数:(stdarg.h)

  1.va_list
   定义了一个指针arg_ptr,用于指示可选的参数;
  2.va_start(arg_ptr, argN)
   使参数列表指针arg_ptr指向函数参数列表中的第一个可选参数,argN是位于第一个可选参数之前的
   固定参数, 或者说最后一个固定参数。如有一个va函数的声明是void va_test(char a, char b, char c, …),
   则它的固定参数依次是a,b,c,最后一个固定参数argN为c, 因此就是va_start(arg_ptr, c);
  3.va_arg(arg_ptr, type)
   返回参数列表中指针arg_ptr所指的参数, 返回类型为type, 并使指针arg_ptr指向参数列表中下一个参数,返回
   的是可选参数,不包括固定参数;
  4.va_end(arg_ptr)
   清空参数列表, 并置参数指针arg_ptr无效;
  

注意:  

  1.函数传递中形参数组和实参数组是同一个数组,数组在使用的时候使用的是值传递。
  2.形式参数是由实参来初始化的,函数调用的时候分配空间,返回时释放空间。

define.h

#ifndef DEFINE_H        //防止同一个C文件中重复包含,而报重复定义的错误
#define DEFINE_H
int i=5;

#endif  

define.c

#include<stdio.h>
#include"define.h"  

int main()
{
    printf("%d\n",i);
    return 0;
}  

add.c

//#include"define.h"  //有一个.c文件包含了,这里又包含,会报重复定义的错误  

条件编译指令:
  #ifndef 、 #ifdef 、 #endif 、 #undef取消宏定义 、 #else 、 #define
其他常用宏定义:
  FILE 文件名 ; LINE 行号(int) ; DATE 当前的日期 ; TIME 当前的时间;
注意:
  1.条件编译是为了防止同一个.c文件中重复包含一个头文件而设定的,当出现两个.c文件包含同一个头文件时,
  将可能报重复定义的错误;
  2.宏定义可以带参数 例如:#define SWAP(T,x,y) {T t=x;x=y;y=t;};括号之间不能有空格符号
  3.宏定义都是替换,如果带参数,一定要带上括号,避免优先级问题导致结果错误;
  4.宏定义数组的格式: #define IARRAY int[100];

pointer.c

#include<stdio.h>

char* func()
{
    char a ='f';
    return &a;                                  //返回局部地址,是错误的
}

void f1(int* a,int* b){int* t=a; a=b; b=t;}     //这里只是函数内的指针指向改变,改变的是局部变量,并不影响外面
void f2(int* a,int* b){int t=*a; *a=*b; *b=t;}  //通过指针指向外部数据,能够改变外部的值
void f3(int a,int b){int t=a; a=b; b=t;}        //改变的只是函数内的局部变量

void showArray(int array[], int num)            //传递进去的还是指针,并不是数组。函数内不能用sizeof()求数组的长度;
{
    return;
}

int main()
{
    int iArray[10] = {1,2,3,4,5,6,7,8,9,0};
    int* iPtr1;                                 //野指针,这样定义很危险
    printf("%p\n",iPtr1);
    int* iPtr2 = NULL;                          //空指针。0代表空地址
    printf("%p\n",iPtr2);
    iPtr2 = iPtr1+3;
    printf("iPtr1= %p, iPtr2= %p, iPtr2-iPtr1= %d\n",iPtr1,iPtr2,iPtr2-iPtr1);

    iPtr1 = iArray;                             //表示数组第一个元素
    printf("iArray[%d]: %d\n",0,iArray[0]);     //1
    printf("iArray[%d]: %d\n",1,*(iArray+1));   //2
    printf("iArray[%d]: %d\n",2,iPtr1[2]);      //3
    printf("iArray[%d]: %d\n",3,*(iPtr1+3));    //4
    printf("iArray[%d]: %d\n",4,(iPtr1+3)[1]);  //5
    printf("iArray[%d]: %d\n",5,*((iPtr1+4)+1));//6

    printf("*iPtr1+1 : %d\n",*iPtr1+1);         //2   *优先级高于 +/- 双目运算符
    printf("*iPtr1++ : %d\n",*iPtr1++);         //1   *优先级低于 前置或后置--/++单目运算符
    printf("*--iPtr1 : %d\n",*--iPtr1);         //1


    int a=10;int b=20;
    f1(&a,&b);                                  //
    printf("%d, %d\n",a,b);                     //10,20
    f2(&a,&b);                                  //
    printf("%d, %d\n",a,b);                     //20,10
    f3(a,b);                                    //
    printf("%d, %d\n",a,b);                     //20,10



    /* const type* \ type const* */
    int iValue1 = 5;
    int iValue2 = 6;
    const int icstValue1 =10;
    const int icstValue2 = 11;

    int* const PtrCst1 = &iValue1;
    int* const PtrCst2 = &icstValue1;   // !!!! warning : "a const pointer to variable"  point to a  "constant" ;
    //int* cp = &icstValue2;            //as same as the last line
    const int* CstPtr1 = &icstValue1;
    const int* CstPtr2 = &iValue1;      //"a pointer to constant" is able to point to a variable;

    //PtrCst1 = &iValue2;               //error : "const pointer" can't change value(address)
    *PtrCst1 = 50;
    *PtrCst2 = 100;                     // !!!! change the constant by pointer 

    CstPtr1 = &icstValue2;
    //*CstPtr1 = 110;                   //error : "pointer to constant" can't change value(variable value)
    //*CstPtr2 = 50;                    //error : as same as the last line

    printf("%d\n",icstValue1);          // !!!! constant was changged

    return 0;
}  

  1.我们使用的内存地址是虚拟地址,是通过内存映射到物理内存上去的。
  2.常量指针:可以指向常量,和变量;(不能改变指向的常量或变量的值)const type*
  3.指针常量:只能初始化时指向变量;(不能改变指向)type* const
  4.多级指针保存比自身少一个 * 的地址;
  5.解析地址符*优先级高于 +/-双目运算符,低于–/++单目运算符;

main.c

#include<stdio.h>

int main(int argc, char* argv[])        //argc:argument count   argv:argument content
{
    printf("argc=%d\n",argc);
    int i;
    for(i = 0;i<argc;i++)
        printf("%d:%s\n",i,argv[i]);

    return 0;
}  
kate(Linux中)输入:

  gcc main.c -o hello : 生成hello
  ./hello world heihei haha : 执行hello文件
  输出:
    argc=4
    0:./hello
    1:world
    2:heihei
    3:haha

practice.c  字符串转整型

#include<stdio.h>
#include<ctype.h>

int str2int(const char* str, const char** q)
{
    int r =0;
    while(isdigit(*str)){                       //isdigit是判断是否为数字,位于ctype.h中
        r=r*10+(*str-'0');                      //此处计算出整型数据的值
        ++str;                                  //此处最终指向原字符串第一个字母开始的位置
    }
    *q = str;                                   //注意,此处改变了传入的指针的指向
    return r;
}

int main()
{
    const char* p=NULL;
    int num =str2int("3926abxys",&p);
    printf("num=%d, p=%s\n",num,p);             //num=3926, p=abxys

    return 0;
}  

practice.c  根据输入的数值输出每个字节的16进制数

#include<stdio.h>

void showbyte(void* addr, int bytes)
{
    while(bytes-- > 0)                          
        printf("%02x ",*(unsigned char*)addr++);//02 表示不足两位,前面补0输出
    printf("\n");
}

int main()
{
    int n =1234567890;
    float f =1234567890;
    double d=1234567890;
    short s=1234567890;
    printf("%x,%hx\n",n,s);
    showbyte(&n,sizeof(n));
    showbyte(&f,sizeof(f));
    showbyte(&d,sizeof(f));
    showbyte(&s,sizeof(s));

    return 0;
}  

pointer2.c

#include<stdio.h>

int main()
{
    int iArray[5] = {1,2,3,4,5};
    int iiArray[3][5] = {{1,2,3,4},{5,6,7},{8}};
    int* pArray[5];                             //pArray是一个数组,每个元素都是int*类型
    int (*iPtr)[5];                             //iPtr是一个指针,只想5个int元素组成的数组

    /*test_1*/
    iPtr = iArray;                              //表示&iArray[0]
    for(int i=0;i<5;++i)
        printf("%d ",*(*iPtr+i));               //== (*iPtr)[i] == iArray[i];
    printf("\n\n");

    /*test_2*/
    iPtr = &iArray;                             //表示数组地址,int(*)[5]类型
    for(int i=0;i<5;++i)
        printf("%d ",*(*iPtr+i));               //== (*iPtr)[i] == iArray[i];
    printf("\n\n");

    /*test_3*/
    iPtr = iiArray;                             //iiArray代表iiArray[0]的地址
    for(int i =0;i<3;++i,printf("\n"))
        for(int j=0;j<5;j++)
            printf("%d ",*(*(iPtr+i)+j));       //iPtr[i][j] //iiArray[i][j]

    return 0;
}

  1.int (*iPtr)[5]; int iArray[5]; *iPtr相当于是iArray首地址。iPtr+1 == *iPtr + 5;
  2.iPtr = iArray 和 iPtr = &iArray 效果是一样的。
  3.iPtr = iiArray;iPtr+1相当于iPtr移动了5个int的距离,指向下一个一维数组元素,*iPtr+1相当于iPtr
   移动了1个int的距离,指向当前一维数组中的下一个int元素;

pointer3.c

#include<stdio.h>

void sayChinese()
{
    printf("ni hao\n");
}
void sayEnglish()
{
    printf("hello\n");
}
void sayJapanese()
{
    printf("kon ni qi wa\n");
}
void getMoney(int num)
{
    printf("Give you %d dallors\n",num);
}

int main(int argc,int* argv[])
{
    int* fun(char);                     //fun是一个函数,形参char,返回值int*
    void (*pFun_vc)(int);               //pFun_vc是一个指针,指向一个形参为char且无返回的函数
    void (*pFun_vv)();                  //pFun_vv是一个指针,只想一个形参为任意且无返回的函数

    printf("release %d times\n",argc);
    if(argc<1) return 0;
    pFun_vv = &main;                    //这里会报assignment from incompatible pointer type的警告
    pFun_vv(argc-1,argv);               //可以调用,没问题

    pFun_vv = &sayChinese;              //此处千万别加括号
    pFun_vv();                          //通过函数指针直接调用函数
    pFun_vv = &sayEnglish;
    pFun_vv();
    pFun_vv = &sayJapanese;
    pFun_vv();
    pFun_vc = &getMoney;
    pFun_vc(100);

    return 0;
}  

pointer4.c

#include<stdio.h>

void print(int* p){printf("%d ",*p);}
void add(int* p){*p+=10;}
void clear(int* p){*p = 0;}
void mul(int* p){*p *=10;}
void fill(int* p){static int n=0; *p = ++n;}

void foreach(int a[],int n,void(*fp)(int*))
{
    int i;
    for(int i=0; i<n; i++)
        (*fp)(a+i);
}

int main()
{
    int a[5];
    int i;

    foreach(a,5,fill);
    foreach(a,5,add);
    foreach(a,5,print);
    printf("\n");          //11 12 13 14 15

    foreach(a,5,clear);
    foreach(a,5,print);
    printf("\n");          //0 0 0 0 0 

    foreach(a,5,fill);
    foreach(a,5,mul);
    foreach(a,5,print);
    printf("\n");          //60 70 80 90 100 

    return 0;
}  

静态变量在多线程的时候会出现问题,我们使用堆区比较多。

practice.c 函数指针作为形参的用法
#include<stdio.h>

//typedef void (*T)();                          //T就是 void(*)()
//typedef int (*U)(char);                       //U就是 int(*)(char)
//U x(T p);                                     //x就是 int(*(void(*p)()))(char)
//int (*x(void(*p)()))(char);                   //这是一个函数,形参void(*p)()指向形参任意的函数

int rule1(double lh,double rh){return (lh>rh);}
int rule2(double lh,double rh){return (rh>lh);}

#define swap(x,y){double t=x;x=y;y=t;}          //宏定义的好处,就是快
void sort(double a[], int n, int (*p)(double,double))
{
    int i,j;
    for(i=0;i<n;++i)
        for(j=i+1;j<n;++j)
            if(p(a[i],a[j])==1)
                swap(a[i],a[j]);
}
void input(double a[], int n)
{
    printf("put in 10 numbers: ");
    int i;
    for(i = 0;i<10;i++)
        scanf("%lf",a+i);                       //&a[i]
}
void show(double a[], int n)
{
    int i;
    for(i = 0;i<n;i++)
        printf("%g ",a[i]);
    printf("\n");
}

int main()
{
    double a[10];
    input(a,10);
    sort(a,10,&rule1);
    show(a,10);
    sort(a,10,&rule2);
    show(a,10);

    return 0;
}

heap.c

#include<stdio.h>
#include<stdlib.h>

int main()
{
    int b =sizeof(double);
    double* p =(double*)malloc(b);
    int cnt;
    printf("Please put in elements numbers: ");
    scanf("%d",&cnt);
    int* a = calloc(cnt,sizeof(int));
    if(a==NULL){
        printf("Faile to apply for room ;\n");
        return 1;
    }
    printf("p=%p, a=%p\n",p,a);
    *p =123.45;
    int i;
    for(i =0;i<cnt;++i)
        a[i] = i+10;
    printf("%g\n",*p);
    for(i=0;i<cnt;++i)
        printf("%d",a[i]);
    printf("\n");
    free(p);
    a=realloc(a,sizeof(int)*10);
    for(i=0;i<10;i++)
        printf("%d ",a[i]);
    printf("\n");
    a = realloc(a,0);
    printf("a=%p\n",a);

    return 0;
}  

堆申请的内存最终需要通过函数free来释放
1.alloca是向栈申请内存,因此无需释放.
2.malloc分配的内存是位于堆中的,并且没有初始化内存的内容,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间.
3.calloc则将初始化这部分的内存,设置为0.
4.realloc则对malloc申请的内存进行大小的调整.

io.c

#include<stdio.h>

int main()
{
    char name[20] = "DaShaBi";
    FILE *ofPtr = fopen("txt/out.txt","w");         //路径不要带着主目录符号
    if(ofPtr != NULL)
        fprintf(ofPtr,"Hello, you are %s ;",name);

    char say[50];
    FILE *ifPtr = fopen("txt/in.txt","r");          //第二个参数表示打开方式
    if(ifPtr != NULL)
        fscanf(ifPtr,"%[^\n]%*c",&say);             //这里指跳过一行,读取一整行,而不会遇到空格就结束

    printf("%s\n",say);
    fclose(ofPtr);                                  //不用文件时,一定要关闭,不然会占用系统资源
    fclose(ifPtr);
    return 0;   
}  

文件描述C语言有一套标准,操作系统有一套标准,C++也有一套标准

有关于scanf fscanf sscanf

io2.c

#include<stdio.h>

int main()
{
    char buffer[50];
    printf("Plase put in a line of words :");
    fgets(buffer,sizeof(buffer),stdin);
    buffer[strlen(buffer)-1] = '\0';        //除去换行符
    puts(buffer);                           //自动换行
    printf("%s\n",buffer);
    fputs(buffer,stdout);                   //不换行

    return 0;
}

有关于fputc fputs putc putchar puts

#

linux中有个命令tee,可以读取标准输入的数据,并将其内容输出成文件。
语法:tee [-ai][–help][–version][文件…]
参数:
   1.-a或–append  附加到既有文件的后面,而非覆盖它啊;
   2.-i-i或–ignore-interrupts  忽略中断信号;
   3.–help  在线帮助;
   4.–version  显示版本信息;

practice.c 将一个文件复制到另一个文件

#include<stdio.h>

int main(int argc, char* argv[])
{
    if(argc!=3){
        printf("%s filename copyfile\n",*argv);
        return 0;
    }
    FILE* ifPtr = fopen(argv[1],"r");
    if(ifPtr==NULL){
        printf("Can't open the file %s\n",argv[1]);
        return 1;
    }
    FILE* ofPtr = fopen(argv[2],"w");
    if(ofPtr == NULL){
        printf("Can't open the file %s\n",argv[2]);
        return 2;
    }

    int c;
    while((c=fgetc(ifPtr))!=EOF){       //将输入文件的内容传输到输出文件中
        putchar(c);
        fputc(c,ofPtr);
    }

    fclose(ifPtr);
    fclose(ofPtr);

    return 0;
}  
kate(Linux中)输入:

gcc -c practice.c 编译成practice.o文件
gcc practice.o 链接生成a.out可执行文件
./a.out ./txt/in.txt ./txt/out.txt 执行a.out文件;

practice.c 将文件中第2个字符跟第10个字符交换

#include<stdio.h>

int main()
{
    FILE* fp = fopen("./txt/write.txt","r+");
    if(fp==NULL){
        printf("Cant open the file\n");
        return 1;
    }

    fseek(fp,1,SEEK_SET);                 //从开头向右偏移1个单位,指向第2个
    char c2, c10;
    c2 = getc(fp);
    fseek(fp,7,SEEK_CUR);                 //当前指向第3,从当前位置向右偏移7个单位,指向第10个
    c10 = getc(fp);                       
    fseek(fp, -9, SEEK_CUR);              //当前指向第11,从当前位置向左偏移9个单位,指向第2个
    putc(c10,fp);
    fseek(fp,9,SEEK_SET);                 //从开头偏移9个单位,指向第10个
    putc(c2,fp);

    fclose(fp);                           //1234567890原来的文件内容
                                          //1034567892更改后的文件内容
    return 0;
}  

1.int fseek(FILE* stream, long offset, int whence); 这个函数用来定位设置当前的读写位置
 参数:文件指针,偏移量,参考位置(SEEK_SET开头、SEEK_END末尾、SEEK_CUR当前);
2.long ftell(FILE* stream);返回文件当前的位置
3.void rewind(FILE* stream);把文件当前位置设置为开头(这个使用再大文件中,比如电影播放器记录拖动操作)

practice.c 成片数据的写入与读取 

#include<stdio.h>
#include<string.h>

int main()
{
    FILE* stream;
    char msg[] = "this is a test";
    char buf[20];
    if((stream = fopen("DUMMY.FIL","w+")) == NULL){
        fprintf(stderr,"Cannot open output file.\n");   //stderr 为标准错误输出设备
        return 0;
    }

    fwrite(msg,strlen(msg)+1,1,stream);     //从内存msg写入文件DUMMY.FIL
    fseek(stream,0,SEEK_SET);               //重新定位在文件开头
    fread(buf,strlen(msg)+1,1,stream);      //从文件DUMMY.FIL读取到neicunbuf中
    printf("%s\n",buf);                     //输出 “this is a test”
    fclose(stream);

    return 0;
}

1.成片的数据操作,使用fread/fwrite在内存和文件之间用成片的数据操作。
  把文件中的东西放到内存叫读
  把内存中的东西放到文件叫写
2.size_t fread(void* buffer, size_t size, size_t count, FILE* stream);
  参数:buffer用于接收数据的内存地址,大小至少是size*count;
     size单个元素的大小,单位是字节;
     count元素的个数,每个元素是size字节;
     stream文件流;
  返回值:实际读取的元素个数。如果返回值与count不同,可能文件结尾或遇到错误。
      从ferror和feof获取错误信息,或检测是否到达结尾;

practice.c 多种类数据存储与读取

#include<stdio.h>

typedef struct person{
    char name[20];  //20b
    char gender;    //1b
    int age;        //4b
    double salary;  //8b
}person; //36 bytes 

int main()
{
    /*     写入文件     */
    person a[5] = {{"Marry",'F',18,3500},{"Tom",'M',20,4500},{"Jack",'M',21,4000},{"Jones",'F',19,3900},{"Lucy",'F',24,6000}};
    FILE* fp = fopen("person.dat","wb");
    if(fp==NULL){
        printf("Failed to open the file");
        return 1;
    }

    int b[10]={11,22,33,44,55,66,77,88,99,1010};
    short s =12345;
    fwrite(a,sizeof(person),5,fp);
    fwrite(b,sizeof(int),10,fp);
    fwrite(&s,sizeof(short),1,fp);
    fclose(fp);


    /*     读取文件     */
    person bufA[5]={};  
    int bufB[10]={};
    short bufS=0;
    fp = fopen("person.dat","rb");
    if(fp==NULL){
        printf("Failed to open the file");
        return 1;
    }

    fread(bufA,sizeof(person),5,fp);            //注意这里的读取顺序。
    fread(bufB,sizeof(int),10,fp);
    fread(&bufS,sizeof(short),1,fp);
    fclose(fp);

    for(int i=0;i<5;i++){
        printf("%s: %s, %d, %g\n",
            a[i].name,a[i].gender == 'M'?"Boy":"Girl",
            a[i].age,a[i].salary);
    }
    for(int i=0;i<10;i++){
        printf("%d ",bufB[i]);                  //11,22,33,44,55,66,77,88,99,1010
    }
    printf("\n");
    printf("%hd\n",bufS);                       //12345

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值