[语法]写入数据-数字-字符串-结构体

  • 写入数字

有一个int数组,在存储到文件时,一般有两种方式:
int buf[4] = { 0xA001098, 0xB002 ,0xC003, 0xD004 };

方式1 : 直接按字节写入
fwrite(buf, 1, sizeof(buf), fp);
(对照VC的内存窗口来理解)

方式2 :格式化为字符串写入
for(int i=0; i<4; i++)
{
char text[16];
sprintf(text, “%d,” , buf[i]);
fwrite(text,1,strlen(text), fp);
}

对于浮点数(float,double),也可以使用这两种写法

double a = 3 / 4.0;
fwrite(&a, 1, sizeof(a), fp);


char text[16];
sprintf(text, “%.2lf”, a);
fwrite(text, 1, strlen(text), fp);

比较:后者在保存数据时,丢失了精度,无法还原

  • 写入字符串

字符串的两种存储方式
char buf[16] = “hello”;

方式1:按实际有效长度写入
fwrite(buf, 1, strlen(buf), fp);

方式2:按固定长度写入
fwrite(buf, 1, 16, fp);
(占用了较多的空间)

  • 结构体写入

结构体数据的写入:
struct Student
{
int id; // id
char gender; // 性别
char name[16]; // 年龄
};
Student someone = { 20150101, ‘M’ , “Noname” };

方式一:整体写入
fwrite(&someone, 1, sizeof(someone), fp);
(对照VC的内存窗口来理解)

方式二:将各字段分开写入
fwrite(&someone.id, 1, 4, fp);
fwrite(&someone.gender, 1, 1, fp);
fwrite(&someone.name, 1, 16, fp);

[GNU/Linux]静态库的创建与使用

静态库,static library
标准命名: libxxx.a
第一步:编译,得到*.o文件
第二步:打包
ar ‐rcs   libxxx.a   file1.o  file2.o … fileN.o
(注:ar只是将*.o文件打个包而已,并非“链
接”)

演示:
g++ ‐c test1.cpp ‐o test1.o
g++ ‐c test2.cpp ‐o test2.o
ar ‐rcs libtest.a test1.o test2.o
查看静态库中的符号
nm  libtest.a
作者: 邵发

 

静态库的交付物:
‐ 头文件 *.h
‐ 库文件 libxxx.a

 

由于libtest.a本质是就是test1.o和test2.o打个包而
已,因此可以像.o文件一样使用
比较:
g++ main.cpp test1.o  test2.o  ‐o helloworld
g++ main.cpp  libtest.a ‐o helloworld
也就是说,在命令行里直接加上libtest.a的全路
径是可以的

 

如果 libtest.so 和 libtest.a同时存在。。。
则默认优先连接 libtest.so

 

当 libtest.so 和 libtest.a同时存在,以下两种方式可以强制
使用静态库:
① 使用全路径 【推荐】
g++ main.cpp  build/libtest.a ‐o helloworld
② ‐static : 强制所有的库都使用静态库版本【行不通】
g++ main.cpp ‐o helloworld ‐static ‐Lbuild ‐ltest
缺点:
所有的库(包括libc , libstdc++)都必须提供静态库版本,少
一个都不行。。
注:centos默认安装时不带 libc.a libstdc++.a ..

[GNU/Linux]标准Makefile 1.1 生成动态库

######### 标准Makefile Lv1.1 / 生成动态库 ########

EXE=libexample.so
SUBDIR=src 
CXX_SOURCES =$(foreach dir,$(SUBDIR), $(wildcard $(dir)/*.cpp))
CXX_OBJECTS=$(patsubst  %.cpp, %.o, $(CXX_SOURCES))
DEP_FILES  =$(patsubst  %.o,  %.d, $(CXX_OBJECTS))

$(EXE): $(CXX_OBJECTS)
    g++ -shared $(CXX_OBJECTS) -o $(EXE)
    
%.o: %.cpp
    g++  -c -fPIC -MMD $<  -o  $@

-include $(DEP_FILES)

clean: 
    rm  -rf  $(CXX_OBJECTS)  $(DEP_FILES)  $(EXE)

test:
    echo $(CXX_OBJECTS)

 

[GNU/Linux]gdb调试

在Linux使用gdb命令行进行单步调试
准备工作:在compile这一步,添加选项 ‐g
g++ ‐g main.cpp  ‐o helloworld

%.o: %.cpp
g++  ‐c ‐g ‐MMD $<  ‐o  $@

进入gdb界面后,使用命令来操纵调试过程。
gdb  ./helloworld 进入gdb界面
b (break) :  添加断点
r (run): 重头开始运行程序
n (next):  下一步
c (continue): 程序继续运行,直到下一处断点,
或者程序运行结束
q (quit): 退出程序界面
(注:可以输入全称break,也可以只输入b)

添加断点:
b  main    (加上函数名)
b  main.cpp:12  (文件名+行号)
b  12  (当前文件的行号)
b  Object::Create (类名 + 成员函数)
显示断点 info break
删除断点 del  break  2    (后面的2是编号)

next 执行下一步
n  执行下一步
n  3  执行下面三步

p (print)  显示表达式的值
p  a
p  a+b
p  (Object*)p1‐>a
更特殊的用法,像函数一样调用显示
p (“%d”,  12)

disp (display)  监视变量,相当于VC下的watch

x 显示内存的值
x/16xb buf
其中,
16: 显示16个单位
x: 以十六进制显示 (x d u f s …)
b: 单位是1字节 (可选 b h w g )
byte, half‐word, word, giant

help x
help info
可以查看某个子命令的帮助

bt  (backtrace) 查看错误时刻的栈信息

 

内存转储

1. 在运行该程序的shell里运行命令
ulimit ‐c  unlimited
(注:在shell关闭重启后,要重新运行一次)
2. 运行程序
程序发生段错误时,程序退出,同时当发生错误
时的信息转储(dump)到一个core.*文件。
3. 用gdb来查看发生段错误的代码位置
gdb  helloworld   core.***

 

那么,如何判断一个程序是否带有“调试信息”呢?
第一种方法: (直接查看)
objdump ‐h helloworld
(注:gcc g++ gdb objdump nm ar  ld …是一套工具)
第二种方法:
gdb  helloworld
如果调试信息,会提示:
Reading symbols from helloworld…(no debugging
symbols found)…done

[GNU/Linux]动态库的生成和使用

使用g++命令来生成动态库
编译,生成.o文件 (编译选项 ‐fPIC )
g++ ‐c ‐fPIC example.cpp ‐o example.o
链接,生成目标 .so文件 ( 链接选项 ‐shared)
g++ ‐shared example.o ‐o libexample.so
(PIC:Position Independent Code位置无关代码)

使用nm命令查看库中的符号
nm  libexample.so

 

编译:
g++  ‐c main.cpp ‐o main.o
链接:
g++ main.o ‐o helloworld ‐L. ‐lexample
链接选项:
‐lexample  使用libexample.so这个库文件
‐L.  指定库文件的位置

 

拿到一个可执行程序,怎么知道它依赖哪些库呢?
readelf ‐d helloworld
Tag        Type                         Name/Value
0x00000001 (NEEDED)                     Shared library:
[libexample.so]
0x00000001 (NEEDED)                     Shared library:
[libstdc++.so.6]
0x00000001 (NEEDED)                     Shared library: [libm.so.6]
0x00000001 (NEEDED)                     Shared library:
[libgcc_s.so.1]
0x00000001 (NEEDED)                     Shared library: [libc.so.6]

[GNU/Linux]标准Makefile 1.0

######### 标准Makefile Lv1.0 ########
EXE=helloworld
SUBDIR=src object
#CXX_SOURCES=$(wildcard  *.cpp)
CXX_SOURCES =$(foreach dir,$(SUBDIR), $(wildcard $(dir)/*.cpp))
CXX_OBJECTS=$(patsubst  %.cpp, %.o, $(CXX_SOURCES))
DEP_FILES  =$(patsubst  %.o,  %.d, $(CXX_OBJECTS))

$(EXE): $(CXX_OBJECTS)
    g++  $(CXX_OBJECTS) -o $(EXE)
    
%.o: %.cpp
    g++  -c  -MMD $<  -o  $@

-include $(DEP_FILES)

clean: 
    rm  -rf  $(CXX_OBJECTS)  $(DEP_FILES)  $(EXE)

test:
    echo $(CXX_OBJECTS)

 

[OOP]STL-string

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

#include <string>
using namespace std;

// 1号方式
void test(const string& t)
{
}
// 2号方式
void test(string& t)
{
}
// 这种方式避免使用,请参考1号方式
void test(string t)
{
}

int main()
{
// 	string str1 ("LiMing");    // 以一个C风格字符串构造
// 	string str2 = "WangHua";  // 同上一种方式
// 	string str3 ("abcde", 3);    // 参数1是C字符串,参数2参数是长度
// 	string str4;  // 空字符串
// 	string str5 = "";  // 同上,空字符串

    // string str6 = NULL; // 不能初始化为NULL

    // 取得字符串内部的char*
    string text("hello,world!\n");
    const char* p = text.c_str();
    printf("str: %s \n", text.c_str());

    // 
    string t1;
    t1.append("something else");	// 附加一个字符串
    t1.append("abcde", 5);    // 附加一个字符串:拷贝前5个字符
    t1.append("abcde", 1, 3);  // 附加一个字符串:
    // 起点offset=1, 长度3,即拷贝"bcd"
    t1.append(4, 'h'); // 附加2个’h’字符

    // 考虑效率问题
    string t2;
    t2.resize(1024);
    t2.clear();
    t2.append("ab");
    t2.append("cd");

    // 
    string t3 = "hello";
    t3[1] = 'i'; // 推荐使用
    t3.at(1) = 'c'; // 不推荐

    // 字符串的比较
    string t4 = "yes";
    if( t4 == "yes")
    {
        printf("相等\n");
    }

    // 字符串的查找
    string t5 = "LiMing is doing homework";
    int p1 = t5.find('i');   // 返回1
    int p2 = t5.rfind('e');   // 
    // 存在,则返回其位置;否则,返回-1

    // 返回子串
    string t6 ("abcdefg");
    string r1 = t6.substr(4); // 返回"efg"
    string r2 = t6.substr(4,2); // 返回"ef"

    return 0;
}

 

[OOP]STL-list

list: 封装链表相关的功能

http://joshliu357.8910.io/wordpress/wp-content/uploads/2018/08/%E6%8D%95%E8%8E%B7.png

链表不能随机访问,只能按顺序链式访问,所以,只能使用迭代器iterator

list<T>::iterator, list内的迭代器

list比vector用适合插入删除操作

list链表中插入/删除一个节点
遍历list,找到目标位置
调用insert/erase,插入/删除一个节点

vector可有可无,但list不可或缺

vector不常用到,使用普通的数组完全没有问题。
list经常用到,如果没有list,那么每个链表都需要从头写起,非常麻烦。

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

#include <list>
using namespace std;

int main()
{
    list<int> lst;

    lst.push_back(1);
    lst.push_front(3);
    lst.push_front(2);
    //lst.pop_back();

    for(list<int>::iterator iter = lst.begin();
        iter != lst.end(); iter ++)
    {
        int& value = *iter;
        printf("%d, ", value);
    }

    // 删除值为3的节点
    for(list<int>::iterator iter = lst.begin();
        iter != lst.end(); iter ++)
    {
        int& value = *iter;
        if(value == 3)
        {
            lst.erase(iter);
            break;
        }
    }

    return 0;
}
#include <stdio.h>
#include <string.h>

#include <list>
using namespace std;


class Object
{
public:
    Object()
    {		
    }
    Object(int id, const char* name)
    {
        this->id = id;
        strcpy(this->name, name);
    }
public:
    int id;
    char name[32];
};

int main()
{
    list<Object> lst;
    lst.push_back(Object(1, "shaofa"));
    lst.push_back(Object(2, "xxxx"));
    lst.push_back(Object(3, "yyyy"));


    for(list<Object>::iterator iter = lst.begin();
        iter != lst.end(); iter ++)
    {
        Object& value = *iter;

        printf("%d, %s \n", value.id, value.name);
    }


    return 0;
}

 

[OOP]STL-vector

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

#include <vector>
using namespace std;

int main()
{
    vector  <int >   arr(16);   // capacity:16
    arr.clear(); // size = 0

    arr.push_back (1) ;   //  capacity:16 , size : 1
    arr.push_back (2) ;   //  capacity:16 , size : 2
    int capacity = arr.capacity();
    int size = arr.size();

    // 迭代器遍历
    vector<int>::iterator iter;
    for(iter = arr.begin();  iter != arr.end(); iter ++)
    {
        int& value = *iter;
        printf("%d, ", value);
    }

    // 
// 	for(int i=0; i<arr.size(); i++)
// 	{
// 		int& value = arr[i];
// 		printf("%d, ", value);
// 	}

    return 0;
}

 

[OOP]类模板

main.c

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

#include "Array.h"

int main()
{
    Array<int> a; // 长度可变的数组
    a.PushBack(1);
    a.PushBack(2);
    a.PushBack(3);
    a.PushBack(4);
    a.PushBack(5);

    int size = a.Size();

    Array<float> b;
    b.PushBack(2.3f);

    return 0;
}

Aarry.h

#ifndef _ARRAY_H
#define _ARRAY_H

// 用于存放double类型的对象
template <typename _TT>
class Array
{
public:
    Array(int capacity=4)
    {
        m_buffer = new _TT[capacity];
        m_capacity = capacity;
        m_size = 0;
    }

    void PushBack(_TT val)
    {
        if(m_size >= m_capacity)
        {
            Resize();
        }
        m_buffer[m_size] = val;
        m_size ++;
    }
    int Capacity()
    {
        return m_capacity;
    }
    int Size()
    {
        return m_size;
    }

private:
    void Resize()
    {
        // 创建一个更大的缓冲区
        int n = m_capacity + 4;
        _TT* buf = new _TT[n];

        // 拷贝原有的内容
        memcpy(buf, m_buffer, m_capacity * sizeof(_TT) );

        // 删除旧的内容,释放其内存
        delete [] m_buffer;

        // 更新一下成员变量
        m_capacity = n;
        m_buffer = buf;
    }

private:
    _TT* m_buffer;
    int m_capacity; // 容量
    int m_size; // 已经存放的对象的个数
};


#endif