[OOP]构造函数

#include <stdio.h>

class Circle
{
public:
    Circle ()
    {
        printf("111111\n");
        x = y = 0;
        radius = 1;
    }
    Circle(int x, int y, int r)
    {
        printf("2222222\n");
        this->x = x;
        this->y = y;
        this->radius = r;
    }
    Circle(int x, int y)
    {
        printf("2222222\n");
        this->x = x;
        this->y = y;
        this->radius = 1;
    }

public:
    int x, y;
    int radius;

};

int main()
{
    Circle  a; 
    Circle  b(1,1, 4);  

    
    int n (10); // int n = 10;
    
    return 0;
}

 

[OOP]类的封装

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

class Circle
{
public:
    int GetX()
    {
        return m_x;
    }
    int GetY()
    {
        return m_y;
    }

    void SetRadius(int r)
    {
        m_radius = r;
    }

    void MoveTo(int x, int y)
    {
        m_x = x;
        m_y = y;
    }

    double Area()
    {
        return 3.14 * m_radius * m_radius;
    }

    // 1, 表示在里面
    int Contain(double x, double y)
    {
        
        return 0;
    }

private:
    int m_x;
    int m_y;
    int m_radius;
};



int main()
{
    Circle obj;
    obj.SetRadius(2);
    obj.MoveTo(0,0);

    int area = obj.Area();
    
    if(obj.Contain(1.2, 1.3))
    {
        printf("yes, in it\n");
    }
    return 0;
}

 

[OOP]面向对象概论

main.c

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

#include "DataStore.h"

int main()
{
    DataStore* store = ds_create();

    Student s;
    s.id = 1;
    strcpy(s.name, "111");
    ds_add(store, &s);

    s.id = 2;
    strcpy(s.name, "222");
    ds_add(store, &s);

    Student* f = ds_find(store, 2);

    ds_print(store);

    ds_destroy(store);

    return 0;
}

DataStore.h

struct Student
{
    int id;
    char name[16];
    Student* next;
};

struct DataStore
{
    Student head;
};


DataStore*  ds_create();
void ds_destroy(DataStore*  store);

void ds_add( DataStore* store, const Student* data);

// (2) 可以按ID来查找一个记录
Student* ds_find(DataStore* store, int id);

//(3) 可以按ID删除一个记录
void ds_remove(DataStore* store, int id);

// (4) 可以打印显示所有的记录
void ds_print(DataStore* store);

DataStore.cpp

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

#include "DataStore.h"

DataStore*  ds_create()
{
    // 动态创建对象
    DataStore* store = (DataStore*)malloc(sizeof(DataStore));
    // 初始化
    store->head.next = NULL;

    return store;
}

void ds_destroy(DataStore*  store)
{
    // 释放所有相关资源
    Student* p = store->head.next;
    while(p)
    {
        Student* next = p->next;
        free(p);
        p = next;
    }
    // 销毁对象
    free(store);
}

void ds_add( DataStore* store, const Student* data)
{
    // 创建对象、复制数据
    Student* copy = (Student*)malloc(sizeof(Student));
    *copy = *data;

    // 插入一个对象到链表中
    Student* cur = store->head.next; // 当前节点current
    Student* pre = &store->head;  // 上一个节点previous
    while(cur)
    {		
        if(copy->id < cur->id) // 找到这个位置
            break;

        pre = cur;
        cur = cur->next;  // 找到最后一个对象
    }

    // 插入到pre节点的后面
    copy->next = pre->next;
    pre->next = copy;

}


// (2) 可以按ID来查找一个记录
Student* ds_find(DataStore* store, int id)
{
    Student* p = store->head.next; 
    while(p)
    {
        if(p->id == id)
            return p;

        p = p->next; // 下一个对象
    }
    return NULL;
}

//(3) 可以按ID删除一个记录
void ds_remove(DataStore* store, int id)
{

}

// (4) 可以打印显示所有的记录
void ds_print(DataStore* store)
{
    Student* p = store->head.next; 
    while(p)
    {
        printf("ID: %d, name: %s\n", p->id, p->name);
        p = p->next; // 下一个对象
    }
}

 

[语法]以文本形式存储

  • 按行存储把每个单元的数据格式化为一行(末尾加上\n),写入文件。例如,保存ip和port
    char ip[]=”192.168.1.100″;
    int port = 8080;char line[256];
    sprintf(line, “ip=%s\n”, ip);
    fwrite(line, 1, strlen(line), fp);sprintf(line, “port=%d\n”, port);
    fwrite(line, 1, strlen(line), fp);

 

  • 也可以直接使用fprintf函数char ip[]=”192.168.1.100″;
    int port = 8080;fprintf (fp, “ip=%s\n”, ip);
    fprintf (fp, “port=%d\n”, port);fprintf:第一个参数是文件指针,后面的参数和printf,直接将数据格式化成字符串并写入文件。
  • 按行解析解析时复杂度相对较高

(1) 按行读取,每次读取一行。
由于不知道每行是多长,所以用fread读取时,需要检测是否已经读到了 \n这个分隔符。(有点复杂)推荐使用fgets函数,这个函数已经把上面的逻辑给封装好了。fgets内部会检查,当读到字符\n时,停止读取。返回实际读取的字节长度。

(2) 对读取到的每一行进行解析(参考15.3讲的解析方法)

 char buf[512];
while(! feof(fp))
{
char* line = fgets(buf, 512, fp);
if(line)
{
printf("got: %s", line);
}
}

注: line末尾有一个\n字符

保存结构体时,可以把结构体的数据格式化为一行。例如,
struct Student
{
    int id; // id
    char gender; // 性别
    char name[16]; // 年龄
};
Student someone = { 20150101, 'M' , "Noname" };

fprintf("id=%d,gender=%c,name=%s\n",
      someone.id,  someone.gender,  someone.name);
保存结构体时,可以把结构体的数据格式化为一行。例如,
struct Student
{
    int id; // id
    char gender; // 性别
    char name[16]; // 年龄
};
Student someone = { 20150101, 'M' , "Noname" };

fprintf("id=%d,gender=%c,name=%s\n",
      someone.id,  someone.gender,  someone.name);
按行解析一行,一般用sscanf是无法胜任的。
比如,
char line[] = "id=123,name=shaofa,hometown=anhui";
int id;
char name[32];
char hometown[32];
sscanf(line, "id=%d,name=%s, hometown=%s", 
     &amp;id, name, hometown);
sscanf只适合提取数字,不能提取字符串!

 

[语法]文件的随机访问-fseek

  • fseekANSI C函数:使用fseek可以实现文件FILE*的随机访问
    int fseek(FILE *stream, long offset, int mode);注:本篇只讨论在“读”文件时候的随机访问技术 

    参数
    stream: 文件指针
    offset: 一个整数,表示偏移值
    mode : 相对位置
    返回值
    0,操作成功;-1,操作失败

     

    跳到第100个字节的位置
    fseek(fp, 100, SEEK_SET);

    跳到倒数100字节的位置
    fseek(fp, 100, SEEK_END);

    跳到当前位置往前100个字节
    fseek(fp, -100, SEEK_CUR);
    跳到当前位置往后100个字节
    fseek(fp, 100, SEEK_CUR);
    什么叫“当前位置”??

文件位置指示器 file-offset indicator
每个被打开的文件对象FILE*,其数据结构里都有一个位置指示器,表示当前的读/写位置。(当前位置到文件头的距离)

当fopen打开文件时,位置指示器的值为0
当fread读取字节时,位置指示器的值会增加相应的字节数
例如,读取128个字节,则位置指示器的值就增加 128,继续fread,则继续增加
当fseek时,会调整位置指示器的值
例如,fseek(fp, 100, SEEK_SET)则位置 指示器的值被设定为100。fseek(fp, 100, SEEK_END)则指示器的值为filesize-100。

 

举例:
文件中按字节保存了100个struct对象的数据,每个对象使用了字节数是一样的。

读第80个对象的数据

Student stu;
fseek(fp, 79* sizeof(Student), SEEK_SET);
fread(&stu, 1, sizeof(Student), fp);

[语法]数据的存储方式

原则:

原则:能够写入,也能够读出并还原。(读出指的是数据的解析和还原)
如果写到文件里,却没有办法读出,那就是一个失败的设计

例如,你2个int变量,值为:12345和6790
失败的存储方法:
char buf[128];
sprintf(“%d%d”, a, b);
fwrite(buf, 1, strlen(buf), fp);

可行的存储方法:
char buf[128];
sprintf(“%d,%d”, a, b); // 以逗号分隔
fwrite(buf, 1, strlen(buf), fp);
如果不以逗号分隔,则无法对数据进行解析。数据白存了!

按字节存储:所有数据,在内存里的表现都是一串字节,因此,只要将这些字节存入即可。

char / short / int : 占1,2,4个字节
float / double: 4,8个字节
数组:
字符数组:
结构体:
指针:另行讨论

  • 基本类型的变量
    char/short/int/float/double型变量的存储
    只需要知道变量地址和大小

// 写入
int a = 0x12345678;
int b = 0x0A0A0A0A;
fwrite(&a, 1, 4, fp); // 4个字节
fwrite(&b, 1, 4, fp); // 4个字节

// 读取
int a ,b;
fread(&a, 1, 4, fp);
fread(&b, 1, 4, fp);

  • 数组的存储
    地址:即数组名
    字节数:手工计算

float arr[4];
fwrite(arr, 1, 4*4 , fp);

// 读取:由于不知道一共存了多个少float,需要循环读取
while(! feof(fp))
{
float a;
if(fread(&a, 1, 4, fp) <= 0)
{
break;
}
}

  • 字符数组的存储
    (注:这里只是描述一种比较简单的存取方式)

定长方式存取:不论有效长度是多少,统一存储32个字节

char text[32];
fwrite(text, 1, 32, fp);

fread(text, 1, 32, fp);

  • 结构体的存储
    有两种办法,都比较简单。
    第一种办法:直接存取整个结构体。
    第二种办法:把每个成员变量依次存储。
    struct Student
    {
    int id;
    char name[16];
    int scores[3];
    };

整体存取
Student s = {201501, “shaofa”, {90,90, 90} };

// 写入
fwrite(&s, 1, sizeof(s), fp);

// 读出
fread(&s, 1, sizeof(s), fp);

把每个成员变量依次分别存取
Student s = {201501, “shaofa”, {90,90, 90} };

// 写入
fwrite(&s.id, 1, sizeof(s.id), fp); // int
fwrite(s.name, 1, sizeof(s.name), fp);// char[]
fwrite(s.score, 1, sizeof(s.score), fp); // int[]

// 读出
fread(&s.id, 1, sizeof(s.id), fp);
fread(s.name, 1, sizeof(s.name), fp);
fread(s.score, 1, sizeof(s.score), fp);

  • 指针的存储
    指针要么不存储,要么存储它指向的对象的内容
    (指针本身没必要存储,它只是一个地址)

struct Car
{
char maker[32]; // 制造商
int price; // 价格
};
struct Citizen
{
char name[32]; // 名字
int deposite; // 存款
Car* car; // NULL时表示没车
};

Car* car = (Car*) malloc(sizeof(Car));
strcpy(car->maker, “Chevrolet”);
car->price = 10;

Citizen who = { “shaofa”, 100};
who.car = car;

如何存储car的信息??显示不能存储指针,因为指针只是一个地址。重启程序之后,要能够从文件中还原出Car的信息才行。

写入文件: 存储car信息
if(who.car != NULL)
{
fwrite(“Y”, 1, 1, fp); // 存入一个字节’Y’
fwrite(who.car->maker, 1, 32, fp);
fwrite(&who.car->price,1, 4, fp);
}
else
{
fwrite(“N”, 1, 1, fp); // 存入一个字节’N’
}

从读文件中读出
char has = ‘N’;
fread(&has, 1, 1, fp);
if(has == ‘Y’) // 先看有没有car的信息
{
Car* car = (Car*) malloc(sizeof(Car));
fread(car->maker, 1, 32, fp);
fread(&car->price, 1, 4, fp);
}

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

struct Student
{
    char gender; // 性别
    int id; // id
    char name[16]; // 年龄
};

struct Car
{
    char maker[32]; // 制造商
    int  price;  // 价格
};
struct Citizen
{
    char name[32]; // 名字
    int  deposite; // 存款
    Car* car;  // NULL时表示没车
};


// 存储数据
int save()
{
    const char* filename = "c:/test/aaa.xyz";
    FILE* fp = fopen(filename, "wb" );
    if(fp == NULL)
    {
        printf("failed to open file!\n");
        return -1;
    }

    Car* car = (Car*) malloc(sizeof(Car));
    strcpy(car->maker, "Chevrolet");
    car->price = 10;

    Citizen  who = { "shaofa", 100};
    who.car = car;

    fwrite(who.name, 1, 32, fp);
    fwrite(&who.deposite, 1, 4, fp);
    if(who.car != NULL)
    {
        fwrite("Y", 1, 1, fp); // 存入一个字节'Y'
        fwrite(who.car->maker, 1, 32, fp);
        fwrite(&who.car->price,1, 4, fp);
    }
    else
    {
        fwrite("N", 1, 1, fp); // 存入一个字节'N'
    }


    fclose(fp);
    return 0;
}

// 读取数据
int load()
{
    const char* filename = "c:/test/aaa.xyz";
    FILE* fp = fopen(filename, "rb" );
    if(fp == NULL)
    {
        printf("failed to open file!\n");
        return -1;
    }

    Citizen who;
    fread(who.name, 1, 32, fp);
    fread(&who.deposite, 1, 4, fp);

    char has = 'N';
    fread(&has, 1, 1, fp);
    if(has == 'Y') // 先看有没有car的信息
    {
        Car* car = (Car*) malloc(sizeof(Car));
        fread(car->maker, 1, 32, fp);
        fread(&car->price, 1, 4, fp);
        who.car = car;
    }
    else
    {
        who.car = NULL;
    }
    

    fclose(fp);
    return 0;
}

int main()
{
    //save();

    load();
    return 0;
}

 

[语法]文件的读取-fread

读取数据
size_t fread(void *buf, // 存储到目标内存地址
size_t size, // 设为1
size_t nelem, // 最多读取多个字节
FILE *stream);

返回值: 实际读取到的字节的个数

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

struct Student
{
    char gender; // 性别
    int id; // id
    char name[16]; // 年龄
};

// 存储数据
int save()
{
    const char* filename = "c:/test/aaa.xyz";
    FILE* fp = fopen(filename, "wb" );
    if(fp == NULL)
    {
        printf("failed to open file!\n");
        return -1;
    }

    int id = 201510;
    fprintf(fp, "%d\n", id);

    float score = 98.5;
    fprintf(fp, "%.1f\n", id);

    fclose(fp);
    return 0;
}

// 读取数据
int load()
{
    const char* filename = "c:/test/aaa.xyz";
    FILE* fp = fopen(filename, "rb" );
    if(fp == NULL)
    {
        printf("failed to open file!\n");
        return -1;
    }

// 	char buf[128];
// 	int n = fread(buf, 1, 128, fp);

// 	char buf[4];
// 	while(! feof (fp)) 
// 	{
// 		int n = fread (buf, 1, 4, fp);
// 		if( n > 0)
// 		{
// 			printf("read %d bytes \n", n);
// 		}		
// 	}

    char buf[256];
    while(1)
    {
        char* line = fgets(buf, sizeof(buf), fp);
        if(!line)
            break;

        printf("%s", line);
    }

    fclose(fp);
    return 0;
}

int main()
{
    //save();

    load();
    return 0;
}

 

[语法]标准C函数time

  • 得到当前系统时间

使用time()函数获取当前时间

time_t now = time(NULL);
printf(“now: %d \n”, (int) now);

time_t类型:是一个整数,可以强转为int型来打印

(time_t的是一个整数,跟编译器有关,通常是32位整数或64位整数)

  • time_t

now的值表示自“1970-01-01 00:00:00”这个时间点的秒值数。

可以发现,每次运行,这个值都是变的。变化的值,就是中间相差的时间(秒数)

但是,我们如何使用它?

 

  • tm: 转化成“年月里时分秒”

可以把time_t转成年月日时分秒
使用localtime()函数
tm * localtime(const time_t *p);

输入参数:time_t*
返回值 : tm*
其中,tm是一个struct类型,包含了年、月、日、时、分、秒、星期等详细信息。

struct tm的定义
struct tm
{
int tm_sec; // 秒: 0~59
int tm_min; // 分:0~59
int tm_hour; // 时: 0~23
int tm_mday; // 日:1~31
int tm_mon; // 月:0~11
int tm_year; // 年: +1900
int tm_wday; // 星期几: 0~6,其中0表示星期日
int tm_yday; // 这一年的第几天: 0~365
};

time_t now = time(NULL);
tm info = *localtime(&now); // 保存结果

int year = info.tm_year + 1900;
int month = info.tm_mon + 1;
int day = info.tm_mday;
int hour = info.tm_hour;
int minute = info.tm_min;
int second = info.tm_sec;

例:求出自今天起、300天以后的日期值。

time_t now = time(NULL); // 当前时间
now += 300 * 24 * 3600; // 加上时间间隔
tm info = *localtime(&now);

  • “年月里时分秒”转换为time_t?time_t的优势:一个整数,容易存储和计算我们可以把一个时间转成time_t, 比如,”2015-02-01 00:00:00″ 转成 time_t使用mktime函数
    time_t mktime(tm * p);

time_t convert(int year, int month, int day,
int hour, int minute, int second)
{
tm info = {0};
info.tm_year = year – 1900;
info.tm_mon = month – 1;
info.tm_mday = day;
info.tm_hour = hour;
info.tm_min = minute;
info.tm_sec = second;
return mktime(&info);
}
注:tm_wday和tm_yday不需要填写

 

例:计算两个日期之间隔了多少天
比如,“2015-4-6”到“2013-2-2”

time_t start = convert(2013,2,2,0,0,0) ;
time_t end = convert(2015,4,6,0,0,0);
// 差值
int diff = (int)(end – start);
// 计算是多少天
int days = diff / (24*3600);

[语法]标准C函数- stdio.h和stdlib.h

实际工程中常用的一对函数:

sprintf – 格式化字符串, 首字母s表示string
sscanf – 解析字符串

例:
char buf[128];
sprintf(buf, “Name:%s , Age:%d”, “shaofa”, 33);


char* buf = (char*)malloc(128); // 参考12章malloc用法
sprintf(buf, “Name:%s , Age:%d”, “shaofa”, 33);

sscanf用于提取字符串的字段
const char* text = “2015-12-13”;
int year,month,day;
int n = sscanf(text, “%d-%d-%d”,
&year, &month, &day);
if( n != 3)
{
printf(“bad format!\n”);
}
sscanf的返回值表示实际解析出的字段的个数

 

 

rand与srand : 随机数的生成

要确保每次程序启动时,传入不同的seed。通常是传入当前的时间。
注: time函数返回一个整数
srand(time(NULL));
for(int i=0; i<10; i++)
{
printf(“%d \n”, rand());
}
srand: 只需要main()启动时运行一次就够了。不需要每次rand()之前都调用srand。

对rand()结果取模

通常地,要对rand()返回的结果取模,使其坐落在一个区间内。
比如,要在100和160之间随机取一个数。
int r = rand() % 60 + 100;
其中,60是区间长度

对rand()结果取模

比如,要在0.00和1.00之间取一个小数
int r = rand() % 100;
double rp = r / 100.0; // 转成小数

或者,使用 RAND_MAX
double rp = (double)rand()/ RAND_MAX ;
RAND_MAX是一个整型数字,表示rand()函数产生的随机数的区间上限。(下限是0)

[语法]字符串的分割

#include<stdio.h>

int split(char text[], char* parts[])
{	
    int count = 0; // 分段的个数
    int start = 0; // 每一分段的首地址
    int flag = 0; // 遍历text,标识当前是否处于有效字符

    int stop = 0; // 是否到达结束
    for(int i=0; !stop ; i++)
    {
        char ch = text[i];
        if(ch == 0) 
            stop = 1; // 结束循环

        if(ch == ',' || ch == '\0' || ch == ' ' || ch == '\t' )
        {
            if(flag) // 遇到分隔符,且当前状态为flag=1
            {
                flag = 0;

                text[i] = 0; // 修改为结束符,完成分段
                parts[count] = text + start; // 记录首地址
                count ++;				
            }
        }
        else
        {
            if(!flag) // 遇到有效字符,且当前状态为flag=0
            {
                flag = 1;
                start = i;
            }
        }
    }
    return count; // 返回分段个数
}

int main()
{
    char text[] = "hello,world,,good,\tmorning ";
    
    char* parts[16];
    int count = split(text, parts);


    return 0;
}