LeetCode-206-单链表逆序

问题描述

LeetCode – Reverse Linked List
将单链表逆序

算法1 – 插入法

性能:Runtime: 4 ms, faster than 89.98% of C online submissions for Reverse Linked List.

算法思路:类比于插入排序,两个指针 first 和 last 分别指向已经逆序好的开头和结尾,即 first->x->x->last->NULL
1. 开始时,first和last都指向head。
2. 然后从 head->next 开始,遍历链表,将每个节点 p 插入到(first,last)的开头,即p->fist->x->x->NULL

代码如下

struct ListNode* reverseList(struct ListNode* head){
    if (head->next == NULL) return head;

    struct ListNode* first = head;  // (first, last) 是已经逆序的部分,first->xx->last
    struct ListNode* last = head;
    
    // 遍历链表,往前插入
    struct ListNode* p = head->next; 
    last->next = NULL;

    while(p != NULL) {
        // first->xxx->head, 插入p,变为 p->first->xx->head

        struct ListNode* pNext = p->next; // 先记录p->next 
        p->next = first;
        first = p;

        p = pNext;
    }

    return first;
}

C solution fast than 89.98%, Similar to Insert Sort algorithm

算法2 新增一个头节点pre,不断将数据插入pre后面

C++ Iterative and Recursive
这是discuss区一个vote较高的答案,思路和我的算法很像,都是插入。
不过,它是新增了一个头结点pre,然后遍历链表,不断的把节点,插入到pre后面

(1) 新增节点pre, pre->next 指向 head, 即 (pre->head)
(2) cur 指向 head,不断的把 cur->next 插入到 pre 和 head 之间. ( cur 位置不变,始终是指向head的。但是随着插入的进行,cur->next 不断往后了)

比如原List是 1->2->3->4->NULL,那么新增一个pre,变成pre->1->2->3->4->NULL,cur始终指向head,即 1,但是随着插入的进行,cur->next 依次变成了2,3,4

代码

struct ListNode* reverseList(struct ListNode* head) {
        struct ListNode *pre = malloc(sizeof(struct ListNode));
        struct ListNode *cur = head;
        pre -> next = head;
        while (cur && cur -> next) {
            struct ListNode* temp = pre -> next;
            pre -> next = cur -> next;
            cur -> next = cur -> next -> next;
            pre -> next -> next = temp;
        }
        return pre -> next;
}

算法4 插入法更精简写法

从所有递交结果中捞出的速度最快的算法

点击0位置的阴影块即可看到算法

思路和算法1一样,相当于 prev 就是first,head就是last。只是更精简而已。
(prev, head) 是已经逆序的,然后不断head往后走,然后把head->next插入到开头。

struct ListNode* reverseList(struct ListNode* head) {                                       
    if (head == NULL || head->next == NULL) return head; 
    
    struct ListNode* prev = NULL;
    while (head != NULL) {
        struct ListNode* temp = head;
        head = head->next;
        temp->next = prev;
        prev = temp;
    }
    return prev;
}

算法5 递归

struct ListNode* reverseList(struct ListNode* head) {                                       
    if (head == NULL || head->next == NULL) return head;
    struct ListNode* node = reverseList(head->next);
    if (head->next->next == NULL) printf("NULL\n");
    head->next->next = head; // head->next 就是 node所逆序链表的最后一个元素
    head->next = NULL;
    return node;
}

LeetCode-26-已排序数组去重

问题描述

LeetCode – Remove Duplicates from Sorted Array
对已经排好序的数组,去除其中重复的元素,并返回去重后的长度如[1,2,2,3,3],去重后为[1,2,3] ,去重后长度为3

算法1

两个指针,前者(p)记录当前去重后的最后一个元素,后者(i)不断遍历数组,寻找新元素。

int removeDuplicates(int* nums, int numsSize){
    if(numsSize == 0) return 0;

    int p = 0; // 指向当前去重后数组的最后一个元素,初始为0位置

    for(int i = 1; i < numsSize; i++){
        // 发现新的元素,放到p+1位置
        if( nums[i] != nums[p]) {
            p ++;
            nums[p] = nums[i];
        }
    }

    return p + 1;
}

算法2

来自Discuss区

遍历数组,使用count记录当前出现了多少次重复
如果出现重复,则count++
如果没有重复,则把当前的元素 i 放到它在去重后数组的位置 i-count处

int count = 0;
for(int i = 1; i < n; i++){
    if(A[i] == A[i-1]) count++;
    else A[i-count] = A[i];
}
return n-count;

算法-螺旋队列

问题

21  22  …
20  7   8   9   10
19  6   1   2   11
18  5   4   3   12
17  16  15  14  13

看清以上数字排列的规律,设 1 点的坐标是 (0,0),x 方向向右为正,y 方向向下为正。例如,7 的坐标为 (-1,-1),2 的坐标为 (0,1),3 的坐标为 (1,1)。编程实现输入任意一点坐标 (x,y),输出所对应的数字。

分析

以(0,0) 即 数字 1 为中心,螺旋的形式如图

圆心为第0圈,往外为第1圈,再往外为第2圈,依次类推

每一圈数字的右上角的点p, 它的数值是可以推导出来的

n = 0  -> p =  1   即 (2 * 0 + 1) ^ 2

n = 1  -> p = 9    即 (2 * 1 + 1) ^ 2

n = 2  -> p = 25  即 (2 * 2 + 1) ^ 2

所以,第n圈,右上角的数值为 p = (2n+1)^2,故而,

p1 = p – n

p2 = p – 3n

p3 = p – 5n

p4 = p – 7n

知道了以上点的数值之后,就可以计算任意一点(x,y)处的数值了。

代码

Github

mac 系统升级后,gcc/clang 提示 ld:malformed file 错误

问题

升级了系统到 macOS Mojave 10.14.4,使用gcc 和 clang 编译时,总是提示如下错误

➜ tmp gcc main.c
ld: malformed file
/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/lib/libSystem.tbd:4:18: error: unknown enumerated scalar
platform: zippered
^~~~~~~~
file ‘/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/lib/libSystem.tbd’
clang: error: linker command failed with exit code 1 (use -v to see invocation)

问题原因

这是因为xcode没有升级导致。系统中用的xcode还是9.x版本,需要升级到10.x 版本

解决方案

升级xcode。

app store -> 搜索 xcode  -> 安装或升级

 

生活的单调线

最能还原一段心情的,莫过于旧事的文字和熟悉的旋律

无意翻看到以前的文字,旧时光与心情瞬息间涌出,除了周围的人事,仿佛一下子回到那些时刻。

等醒悟过来,明白只能偶然的重入,而永远不能重回,另起几分惆怅。

感叹没有记录的生活,只是一次单调的往前不断延伸的直线,烦于生计的蒙眼前行。而近年莫不如此,或许皆因安于近态,倦于思考。

耳边在播放小刚的老歌,就这样暂时忘了一切吧

C++ 智能指针的实现

1. shared_ptr 的特性

shared_ptr内部维护一个引用计数,当创建、copy、销毁时,引用计数都会变化

拷贝构造函数 p(q),会递增q的引用,pq指向同一个对象

 shared_ptr<string> q = make_shared<string>("smartptr");
shared_ptr<string> p(q); //递增q的引用计数,p和q指向同一个对象
std::cout << "p use_count=" << p.use_count() << std::endl; // 2
std::cout << "q use_count=" << q.use_count() << std::endl; // 2 

赋值构造函数 p=q,会递增q的引用,递减p的引用

 class Man
{
public:
 Man(string name) :_name(name) { std::cout << _name << ": Create" << std::endl; };
 ~Man() { std::cout << _name << ": Delete" << std::endl; };
private:
 string _name;
};

shared_ptr<Man> q = make_shared<Man>("Man_q");
shared_ptr<Man> p = make_shared<Man>("Man_p");
std::cout << "p use_count=" << p.use_count() << std::endl; // 1
std::cout << "q use_count=" << q.use_count() << std::endl; // 1

p = q; //递增q的引用计数,递减p的引用,当p引用为0时,释放其内部的对象。p和q指向同一个对象
std::cout << "p use_count=" << p.use_count() << std::endl; // 2
std::cout << "q use_count=" << q.use_count() << std::endl; // 2

上面这个示例代码,当 执行 p = q时,

(1) 递减p的引用计数,p之前的引用计数为1,递减后为0

(2) 如果p的引用计数为0,则释放p中原有的对象,此时会调用Man的析构,打印”Man_p :Delete”

(3) 递增q的引用计数,即变为2

(4) 将 p 指向 q

2. 实现一个智能指针SmartPtr

理解了上面的过程之后,就可以动手实现一个智能指针

 template <class _Ty>
class SmartPtr
{
public:
	SmartPtr(_Ty* ptr) :_refptr(new RefPtr(ptr)) {};
	SmartPtr(const SmartPtr& obj)
	{
		if (this != &obj)
		{
			if (_refptr)
			{
				*(_refptr->_count) -= 1;
				if (*(_refptr->_count) == 0)
				{
					delete _refptr;
				}
			}

			_refptr = obj._refptr;
			*(_refptr->_count) += 1;
		}	
	}

	SmartPtr& operator=(const SmartPtr& obj)
	{
		if (this != &obj)
		{
			if (_refptr)
			{
				*(_refptr->_count) -= 1;
				if (*(_refptr->_count) == 0)
				{
					delete _refptr;
				}
			}

			_refptr = obj._refptr;
			*(_refptr->_count) += 1;
		}
		return *this;
	}

	~SmartPtr()
	{
		*(_refptr->_count) -= 1;
		if (*(_refptr->_count) == 0)
		{
			delete _refptr;
		}
	}

	int use_count() { return *(_refptr->_count); }

private:
	class RefPtr                   //辅助类,用于保存引用计数 和 实际对象
	{                              //  之所以需要使用辅助类,而不是直接用int _count和 _Ty* 成员变量    
		friend class SmartPtr;     //  是为了能否封装住 _count和_Ty* 这些变量,对外不可见
		RefPtr(_Ty* ptr) :_ptr(ptr) { _count = new int(1);  };
		~RefPtr() { if (_ptr) delete _ptr; delete _count; };
		int* _count;
		_Ty* _ptr;
	};
	RefPtr* _refptr;
};

这里有几个细节
(1) 将SmartPtr声明为RefPtr的友元之后,SmartPtr就可以访问RefPtr的私有变量
(2) RefPtr全部为private,可以很好的封装不对外可见
(3) _count 声明为int* 指针,这样,在拷贝构造和赋值构造中,就可以对 const SmartPtr& obj 这个obj对象的count也进行改变,
因为this->_refptr->_count和 obj的count指向同个内存区;
再者对this->_refptr->_count 做递减时,也会影响到其他指向this的智能指针的引用计数

使用示例如下

 SmartPtr<Man> man1(new Man("Man1"));
{
	std::cout << "Cnt1:" << man1.use_count() << std::endl; // 1

	SmartPtr<Man> man2(man1);  //man2 是man1的copy
	std::cout << "Cnt1:" << man1.use_count() << std::endl;  // 2
	std::cout << "Cnt2:" << man2.use_count() << std::endl;  // 2

	{
		SmartPtr<Man> man3(new Man("Man3"));
		std::cout << "Cnt1:" << man1.use_count() << std::endl; // 2
		std::cout << "Cnt2:" << man2.use_count() << std::endl; // 2
		std::cout << "Cnt3:" << man3.use_count() << std::endl; // 1

		man3 = man1; //man3先析构了Man3, 再指向了man1, 同时引用+1 ; Man3: Delete
		std::cout << "Cnt1:" << man1.use_count() << std::endl; // 3
		std::cout << "Cnt2:" << man2.use_count() << std::endl; // 3
		std::cout << "Cnt3:" << man3.use_count() << std::endl; // 3
	}

	std::cout << "Cnt1:" << man1.use_count() << std::endl; // 2 因为man3 出了作用域,析构了
	std::cout << "Cnt2:" << man2.use_count() << std::endl; // 2

}
std::cout << "Cnt1:" << man1.use_count() << std::endl; // 1 因为man2 出了作用域,析构了

输出如下
Man1: Create
Cnt1:1
Cnt1:2
Cnt2:2
Man3: Create
Cnt1:2
Cnt2:2
Cnt3:1
Man3: Delete
Cnt1:3
Cnt2:3
Cnt3:3
Cnt1:2
Cnt2:2
Cnt1:1

CAS 的跨平台实现方案 以及 基于CAS 的无锁多线程安全日志类

CAS 的跨平台实现方案

CAS, compare-and-swap, 原子的比较和设置变量值。

#include <stdio.h>
#if defined(__linux__) || defined(__unix__)
#define CAS32(ptr, val_old, val_new)({ char ret; __asm__ __volatile__("lock; cmpxchgl %2,%0; setz %1": "+m"(*ptr), "=q"(ret): "r"(val_new),"a"(val_old): "memory"); ret;})
//or using #define CAS __sync_bool_compare_and_swap, this requires -lstdc++
#elif _WIN32
#include <windows.h>
bool CAS32(int* ptr,int val_old, int val_new){
       InterlockedCompareExchange((long *) ptr,val_new,val_old);
       return (*ptr == val_new) ; 
}
#else
#error "unknown os"
#endif
int main()
{
       int v = 10, v_old = 10, v_new = 11;
       if(CAS32(&v,v_old,v_new)){
              printf("CAS32 ok, v=%d\n",v);
       }else{
              printf("CAS32 failed, v=%d\n",v);
       }
       return 0;
}

 

基于CAS 的无锁多线程安全日志类

日志类为单例类,通过对类的成员变量 occupied_ 的原子操作(CAS)来实现线程安全。

写日志时,通过while( !CAS(xx) ) 来检查并设置occupied_参数,如果检测到occupied_为0,即无其它线程使用,则设置为1,防止其它线程使用,日志写完后,再通过CAS将其设置成1,即未占用。

目前就是简单的将日志输出到控制台,如果将日志输出到日志文件,就会发现CAS同步的用处,它保证了日志文件不会被多个线程同时占用。

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#define MAX_LOG_BUF_SIZE 2048
#define CAS __sync_bool_compare_and_swap
class SimpleLockFreeLog
{
private:
       SimpleLockFreeLog(void):occupied_(0),cnt_(0){};
       ~SimpleLockFreeLog(void){};
public:
//     static SimpleLockFreeLog& GetInstance()
//     {
//            static SimpleLockFreeLog instance; //c++ 11
//            return instance;
//     }
       static SimpleLockFreeLog& GetInstance(){ 
              static SimpleLockFreeLog* pInstance = NULL;
              if(NULL == pInstance) pInstance = new SimpleLockFreeLog();
              return *pInstance;
       }
       void LogError(const char* fmt,...){
              while (CAS(&occupied_, 0, 1) != true){}
              va_list args;
              va_start(args,fmt);
              static char bufPrint[MAX_LOG_BUF_SIZE] = {0};
              memset(bufPrint,0,MAX_LOG_BUF_SIZE);
              int n = 0;
              n = vsnprintf(bufPrint,MAX_LOG_BUF_SIZE-1,fmt,args);
              if(n < 0 || n > MAX_LOG_BUF_SIZE) return;
              va_end(args);
              printf("cnt: %d log: %s",cnt_++, bufPrint);
              //printf("%s",bufPrint);
              CAS(&occupied_,1,0);
       }
private:
       unsigned int occupied_;
       int cnt_;
};
#define SimpleLockFreeLog_Error(fmt,...) SimpleLockFreeLog::GetInstance().LogError("[%s][%s:%d][ERROR]:"fmt,__TIME__,__FILE__,__LINE__,##__VA_ARGS__);

 

使用的demo 如下,

#include <pthread.h>
#include "SimpleLockFreeLog.h"



void* routine(void* arg)
{
	const char* pTid = (const char*) arg;
	for(int i = 1; i<= 30; i++){
		SimpleLockFreeLog_Error("%s: %d\n",pTid,i);
	}
	return 0;
}



int main()
{
	SimpleLockFreeLog_Error("Start...\n");
	pthread_t tid1,tid2,tid3;


	pthread_create(&tid1,NULL,routine,(void*)"tid1");
	pthread_create(&tid2,NULL,routine,(void*)"tid2");
	pthread_create(&tid3,NULL,routine,(void*)"tid3");
	
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_join(tid3,NULL);

	SimpleLockFreeLog_Error("End...\n");

	return 0;
}

编译方法

gcc main.cpp -o LogDemo -lstdc++ -lpthread

如果不使用CAS,会发现每个线程的打印会互相打乱,使用CAS时,每个线程的打印都是有序的。

protobuf c++ 笔记

protobuf的github 源码地址为 https://github.com/google/protobuf

本次使用的环境为ubuntu,参照官方教程

1. 编译源码

sudo apt-get install autoconf automake libtool curl #安装工具
git clone https://github.com/google/protobuf #下载源码

进入源码目录,打开autogen.sh脚本,找到下面这行代码,注释掉改行。(因为google被墙,会连接超时)

 curl $curlopts -O https://googlemock.googlecode.com/files/gmock-1.7.0.zip

手动下载gmock-1.7.0.zip文件,放到当前目录,然后继续执行以下命令

./autogen.sh
./configure
make
make check
sudo make install
sudo ldconfig # refresh shared library cache.

2.  proto文件

//person.proto
message Person{
 required string name = 1;
 required uint32 age = 2;
 optional string country = 3;
}

3. 使用protobuf compiler 编译proto文件

protoc --cpp_out=./ person.proto 

指定编译出来的.h和.cc文件放在当前目录

4. main函数

#include <iostream>
#include <fstream>
#include "person.pb.h"

using namespace std;

int main()
{
	GOOGLE_PROTOBUF_VERIFY_VERSION;
	Person p;
	p.set_name("Andy");
	p.set_age(40);
	p.set_country("China");

	{
		cout<<"Save Data"<<endl;
		fstream output("data",ios::out|ios::trunc | ios::binary);
		if(!p.SerializeToOstream(&output)){
			cerr << "Failed to save"<<endl;
			return -1;
		}else{
			cout<<"Save OK"<<endl;
		}
	}

	{
		cout<<"Read Data"<<endl;
		Person p1;
		fstream input("data",ios::in | ios::binary);
		if(!p1.ParseFromIstream(&input)){
			cerr<<"Failed to read"<<endl;
			return -1;
		}

		cout<<"name:"<<p1.name()<<endl;
		cout<<"age:"<<p1.age()<<endl;
		cout<<"country:"<<p1.country()<<endl;

	}

	return 0;
}

 

5. makefile

All: person.pb.cc main.cpp
 pkg-config --cflags protobuf # fails if protobuf is not installed
 g++ person.pb.cc main.cpp -I. -I../protobuf-master/src -o Demo `pkg-config --cflags --libs protobuf`

6. 编译运行

make
./Demo

c++ bind1st 和 bind2nd的用法

std::bind1st 和 std::bind2nd将二元函数转换为一元函数,具体用法参加下面的代码。

代码介绍了两种使用方式,第一种是使用std::less和std::greater,第二种是使用自定义的仿函数。

#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
#include <functional>
 
 
/**  
* std::bind1st  std::bind2nd 就是将一个二元函数的一个参数设置为定值,这样二元函数就转换为了一元函数
* 因为有些算法的参数要求必须是一元函数,但是我们又想用二元函数,那么就可以使用这两个函数
*/
/**
*@brief std::less 仿函数的内部实现
	template <class T> struct less : binary_function <T,T,bool> {
		bool operator() (const T& x, const T& y) const {return x<y;}
	};
*/
 
struct person{
	int age;
	std::string name;
};
 
struct person_filter_func: public std::binary_function<person,std::string,bool>
{
	bool operator()(const person& p,const std::string& key) const{
		return (p.name.find(key) != std::string::npos); 
	}
};
 
void disp(int val){	std::cout<<val<<std::endl; }
void disp_v(const person& p){	std::cout<<p.age<<","<<p.name<<std::endl; }
 
int main()
{
	//使用 std::less 仿函数
	int arr[] = {1,2,3,4,5,6,7,8,9};
	std::vector<int> vec;
	std::copy_if(std::begin(arr),std::end(arr),std::back_inserter(vec),std::bind1st(std::less<int>(),6)); //将6 绑定为第一个参数,即 6 < value
	std::for_each(vec.begin(),vec.end(),disp);  // 7 8 9
 
	vec.clear();
	std::copy_if(std::begin(arr),std::end(arr),std::back_inserter(vec),std::bind2nd(std::less<int>(),6)); //将6 绑定为第二个参数,即 value < 6
	std::for_each(vec.begin(),vec.end(),disp); //1 2 3 4 5
 
 
	//使用自定义的仿函数
	std::vector<person> vecP;
	person p1 = {1,"jack"}; vecP.push_back(p1);
	person p2 = {2,"rose"}; vecP.push_back(p2);
	person p3 = {3,"jane"}; vecP.push_back(p3);
 
	std::vector<person> vecRet;
	std::copy_if(vecP.begin(),vecP.end(),std::back_inserter(vecRet),std::bind2nd(person_filter_func(),"ja"));  //将包含关键字"ja"的person,复制到vecRet容器中
	std::for_each(vecRet.begin(),vecRet.end(),disp_v);//1, jack  3, jane
}

 

参考资料

1. http://blog.csdn.net/simahao/article/details/405455

2. http://www.cplusplus.com/reference/functional/less/

 

日志函数

一个简单的日志函数示例,使用宏实现日志的打印和显示。

不足:没有考虑多线程的文件操作问题;宏较长,使用宏会使得代码体积膨胀。

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

#define __MODULE__ "main"

std::string get_time()
{
	time_t t = time(0);
	char tmp[64];
	strftime(tmp, sizeof(tmp), "%Y/%m/%d %X", localtime(&t));
	return std::string(tmp);
}

 #define LOG_NOTICE 0
 #define LOG_WARNING 1
 #define LOG_ERROR 2

 #define LOG(LEVEL,fmt,...)\
 	do{\
 		FILE * fp = fopen("log.txt", "a+");\
 		if(fp){\
 			if(LOG_NOTICE == LEVEL){\
 				fprintf(stdout,"NOTICE:[%s][%s]:[%s:%d]"fmt"\n",get_time().c_str(),__MODULE__,__FILE__,__LINE__,##__VA_ARGS__);\
 				fprintf(fp,"NOTICE:[%s][%s]:[%s:%d]"fmt"\n",get_time().c_str(),__MODULE__,__FILE__,__LINE__,##__VA_ARGS__);\
 			}\
			if(LOG_WARNING == LEVEL){\
			fprintf(stdout,"WARNING:[%s][%s]:[%s:%d]"fmt"\n",get_time().c_str(),__MODULE__,__FILE__,__LINE__,##__VA_ARGS__);\
			fprintf(fp,"WARNING:[%s][%s]:[%s:%d]"fmt"\n",get_time().c_str(),__MODULE__,__FILE__,__LINE__,##__VA_ARGS__);\
			}\
			if(LOG_ERROR == LEVEL){\
				fprintf(stdout,"ERROR:[%s][%s]:[%s:%d]"fmt"\n",get_time().c_str(),__MODULE__,__FILE__,__LINE__,##__VA_ARGS__);\
				fprintf(fp,"ERROR:[%s][%s]:[%s:%d]"fmt"\n",get_time().c_str(),__MODULE__,__FILE__,__LINE__,##__VA_ARGS__);\
			}\
 			fclose(fp);\
 		}\
 	}while(0)

#define NOTICE(fmt,...)  LOG(LOG_NOTICE, fmt, ##__VA_ARGS__)
#define WARNING(fmt,...)  LOG(LOG_WARNING, fmt, ##__VA_ARGS__)
#define ERROR(fmt,...)  LOG(LOG_ERROR, fmt, ##__VA_ARGS__)

int main()
{
	int i = 2015;
	char * notice = "Hello World";
	NOTICE(" %s %d", notice, i);  	// NOTICE:[2015/09/02 22:08:37][main]:[h:\programming\anything\main.cpp:83] Hello World 2015
	return 1;
}

libjpeg的交叉编译以及jpeg图片的缩放(缩略图)

libjpeg库的交叉编译

libjpeg库主要用于jpeg格式图片的编解码,其交叉编译过程如下

1.  下载源码

从官方网站http://www.ijg.org/files/ 下载libjpeg库的源码,本次编译过程使用的是jpegsrc.v9a.tar.gz

2. 解压源码

2.1 切换到下载目录,执行

 tar -xzvf jpegsrc.v9a.tar.gz 

2.2 切换到源码目录

 cd jpeg-9a/ 

3. 交叉编译

3.1 设置交叉编译器的环境变量

export CC=/home/jarvischu/arm-linux-uclibcgnueabi-gcc

3.2 执行configure,其中–prefix 用来指定编译结果的存放位置; –host 用来指明交叉编译

./configure --prefix=/home/jarvischu/jpeg --enable-shared --enable-static --host=arm-unknown-linux

3.3 执行make命令

make

3.4 执行make install 命令

 sudo make install 

至此,交叉编译结束,编译结果存放在/home/jarvischu/jpeg目录下,该目录包含了编译得到的lib和include

 

使用libjpeg库实现jpeg图片的缩放(缩略图)


#include <stdio.h>
#include "jpeglib.h"
#include <setjmp.h>

struct my_error_mgr {
  struct jpeg_error_mgr pub;	/* "public" fields */

  jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

/*
 * Here's the routine that will replace the standard error_exit method:
 */

METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
 longjmp(myerr->setjmp_buffer, 1);
}

//读取Jpeg图片的数据并返回,如果出错,返回NULL
unsigned char* ReadJpeg(const char* path, int& width, int& height)
{
	FILE *file = fopen( path, "rb" );
	if ( file == NULL )	{
		return NULL;
	}

	struct jpeg_decompress_struct info; //for our jpeg info

// 	struct jpeg_error_mgr err; //the error handler
// 	info.err = jpeg_std_error(&err);

	 struct my_error_mgr my_err;

	 info.err = jpeg_std_error(&my_err.pub);
	 my_err.pub.error_exit = my_error_exit;

	 /* Establish the setjmp return context for my_error_exit to use. */
	 if (setjmp(my_err.setjmp_buffer)) {
		 /* If we get here, the JPEG code has signaled an error.
		 * We need to clean up the JPEG object, close the input file, and return.
		 */
		 printf("Error occured\n");
		 jpeg_destroy_decompress(&info);
		 fclose(file);
		 return NULL;
	 }

	jpeg_create_decompress( &info ); //fills info structure
	jpeg_stdio_src( &info, file );        //void

	int ret_Read_Head = jpeg_read_header( &info, 1 ); //int

	if(ret_Read_Head != JPEG_HEADER_OK){
		printf("jpeg_read_header failed\n");
		fclose(file);
		jpeg_destroy_decompress(&info);
		return NULL;
	}

	bool bStart = jpeg_start_decompress( &info );
	if(!bStart){
		printf("jpeg_start_decompress failed\n");
		fclose(file);
		jpeg_destroy_decompress(&info);
		return NULL;
	}
	int w = width = info.output_width;
	int h = height = info.output_height;
	int numChannels = info.num_components; // 3 = RGB, 4 = RGBA
	unsigned long dataSize = w * h * numChannels;

	// read RGB(A) scanlines one at a time into jdata[]
	unsigned char *data = (unsigned char *)malloc( dataSize );
	if(!data) return NULL;

	unsigned char* rowptr;
	while ( info.output_scanline < h )
	{
		rowptr = data + info.output_scanline * w * numChannels;
		jpeg_read_scanlines( &info, &rowptr, 1 );
	}

	jpeg_finish_decompress( &info );    

	fclose( file );

	return data;
}

/*参数为:
 *返回图片的宽度(w_Dest),
 *返回图片的高度(h_Dest),
 *返回图片的位深(bit_depth),
 *源图片的RGB数据(src),
 *源图片的宽度(w_Src),
 *源图片的高度(h_Src)
 */
unsigned char* do_Stretch_Linear(int w_Dest,int h_Dest,int bit_depth,unsigned char *src,int w_Src,int h_Src)
{
	int sw = w_Src-1, sh = h_Src-1, dw = w_Dest-1, dh = h_Dest-1;
	int B, N, x, y;
	int nPixelSize = bit_depth/8;
	unsigned char *pLinePrev,*pLineNext;
	unsigned char *pDest = new unsigned char[w_Dest*h_Dest*bit_depth/8];
	unsigned char *tmp;
	unsigned char *pA,*pB,*pC,*pD;

	for(int i=0;i<=dh;++i)
	{
		tmp =pDest + i*w_Dest*nPixelSize;
		y = i*sh/dh;
		N = dh - i*sh%dh;
		pLinePrev = src + (y++)*w_Src*nPixelSize;
		//pLinePrev =(unsigned char *)aSrc->m_bitBuf+((y++)*aSrc->m_width*nPixelSize);
		pLineNext = (N==dh) ? pLinePrev : src+y*w_Src*nPixelSize;
		//pLineNext = ( N == dh ) ? pLinePrev : (unsigned char *)aSrc->m_bitBuf+(y*aSrc->m_width*nPixelSize);
		for(int j=0;j<=dw;++j)
		{
			x = j*sw/dw*nPixelSize;
			B = dw-j*sw%dw;
			pA = pLinePrev+x;
			pB = pA+nPixelSize;
			pC = pLineNext + x;
			pD = pC + nPixelSize;
			if(B == dw)
			{
				pB=pA;
				pD=pC;
			}

			for(int k=0;k<nPixelSize;++k)
			{
				*tmp++ = ( unsigned char )( int )(
					( B * N * ( *pA++ - *pB - *pC + *pD ) + dw * N * *pB++
					+ dh * B * *pC++ + ( dw * dh - dh * B - dw * N ) * *pD++
					+ dw * dh / 2 ) / ( dw * dh ) );
			}
		}
	}
	return pDest;
}

bool write_JPEG_file (const char * filename, unsigned char* image_buffer, int quality,int image_height, int image_width)
{

	if(filename == NULL || image_buffer == NULL) return false;

	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	FILE * outfile;		/* target file */
	JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
	int row_stride;		/* physical row width in image buffer */
	cinfo.err = jpeg_std_error(&jerr);
	/* Now we can initialize the JPEG compression object. */
	jpeg_create_compress(&cinfo);

	if ((outfile = fopen(filename, "wb")) == NULL) {
		fprintf(stderr, "can't open %s\n", filename);
		return false;
	}
	jpeg_stdio_dest(&cinfo, outfile);

	cinfo.image_width = image_width; 	/* image width and height, in pixels */
	cinfo.image_height = image_height;
	cinfo.input_components = 3;		/* # of color components per pixel */
	cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */

	jpeg_set_defaults(&cinfo);
	jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

	jpeg_start_compress(&cinfo, TRUE);

	row_stride = image_width * 3;	/* JSAMPLEs per row in image_buffer */

	while (cinfo.next_scanline < cinfo.image_height) {
		row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
		(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
	}

	jpeg_finish_compress(&cinfo);
	fclose(outfile);

	jpeg_destroy_compress(&cinfo);

	return true;
}

//生成图片的缩略图(图片的一个缩小版本)
 bool generate_image_thumbnail(const char* inputFile, const char* outputFile)
{
	if(inputFile == NULL || outputFile == NULL) return false;

	//读取jpeg图片像素数组
	int w=0,h=0;
	unsigned char* buff = ReadJpeg(inputFile,w,h);
	if(buff == NULL) {
		printf("ReadJpeg Failed\n");
		return false;
	}

	//缩放图片,缩放后的大小为(tb_w,tb_h)
	int tb_w = 160, tb_h = 160;
	unsigned char * img_buf = do_Stretch_Linear(tb_w,tb_h,24,buff,w,h);
	free(buff);

	//将缩放后的像素数组保存到jpeg文件
	bool bRetWrite = write_JPEG_file(outputFile,img_buf,10,tb_h,tb_w);
	delete[] img_buf;

	if(bRetWrite){
		return true;
	}else{
		printf("GenerateImageThumbnail: write failed\n");
		return true;
	}
}

这是人间好时节

春有百花秋有月

夏有凉风冬有雪

若无闲事挂心头

便是人间好时节

晚上在西溪跑步的时候,走在一个荷花池中的栈道时,停了下来,两旁是茂密的芦苇草,芦苇长的很旺盛,高高的超过了栈道,向着栈道压了下来。夜色已深,抬头那一轮近圆月挂在不远的天际,一颗星星也没有,月亮却显得那么明亮,明天就是十五,明个它就要圆满了吧。荷花池上凉风不断,满池的芦苇在月色中微微的摇荡,却让周围显现的愈加的静谧。这么晚了,夜跑的人们也大多回程了,路上都已是三三两两零零落落的,何况这偏离主道的荷花池上的隐秘的栈道,若不是那晚的偶然兴起,我亦不会发现还有这样一个安静少人的地方。凉风中夹杂着很清凉的味道,我想这是池水和水草混和后的清香吧。前些日子来的时候,还能偶尔的看见有几只萤火虫在草间闪烁时隐时现,回想起来,上一次看见萤火虫的地方,应该是在夏夜乘凉时老家屋前吧,印象中都是满天的星星,仰躺在全由竹子编制凉床上,看着穹顶之上布满的大小星星,常常会有淡黄偏绿的亮点忽闪忽灭的慢慢从眼前飞过,和远处的点点星光融为一体。 这些可爱的星星便是来屋旁小坡上那茂密的小树间和草丛中的萤火虫。

心情于是大好,脚步愈加不愿前行,这样的月色如许的凉风,让我忽然想起一首诗偈,便是开头那首。人间时时有好景,只待行者驻足寻。近期的生活想来过的确也很自在而愉悦,与以往大不相同。来杭州求学这几年虽看似闲淡无压,实则总有闲事挂在心头,使我不得开心颜。人的不愉快往往就是因为汲汲于改变逃脱的现状和无力改变的环境,因为某些原因,两年来总是迫不及待的期盼毕业,而日子总要一天天去过,谁都无法跳跃。如今,总算找到了属于自己的生活,很知足,很感恩,内心也是宁静的。每每闲下来时,或在西溪夜走时,总会找到一种很放松很平静的状态,这便是“若无闲事挂心头”吧,很珍惜现在的一切,稳定的工作、稳定的生活,三两知己,一份让我愿守终生的感情,这便是人间最好的时节吧!

晚点,但不会遗漏

这是我今晚和一个朋友时说的,本意是安慰对方,可却把自己的情绪陷进去了,沉静了好久。

“我相信这种种的安排都是为了更好的生活 因为命运知道怎么样的生活才是适合我们的 他给我们安排了这些 我们只是没有理解 只是一厢情愿的沉浸在自己的小情绪中 不懂得命运的安排而已
还不时的埋怨他 其实我们错了
等到所有结局到来的时候 才会明白
这一番安排 是多么的精心 多么的理所当然
而自己当时的各种虐心所为是多么愚昧无知
幸福会晚点 但从不会遗漏
调整好自己的生活,期待,时间会带来我们真正需要的

也许真正走出来的模样,是明白,是即便回望,更多的也是感激和感悟,还有对曾经自己那段年少痴狂的感动,无关其他。
庆幸的是,在慢慢找到自己生活步调的过程中,我依稀看到了这个模样。

————

一年后再读,还是希望这些文字能够鼓舞自己

 

假如爱有天意

看了最新一期的我是歌手,李健唱了一首自己填词的“假如爱有天意”,这是电影的同名歌曲,熟悉的旋律,很容易又让人勾起了回忆。

当年和基友LM两个人晚上在宿舍亮着一盏灯对着一台14寸的本本看完,感动的短暂抱在一起,想想简直羞羞的。并决心以后一定要和女友一起好好的安安静静的看一场。后来多年之后吧,在我的提议下,终于有了机会两个人一起看,我还是那么的感动,虽然剧情都是我已经知道的,另一位却看得昏昏欲睡的,果然感动这种东西是不能传染的,我的汲汲于此,也算是告一段落。

假如爱有天意,电影之外,爱有天意么?要说有天意,那也都是人意,人们把自己做的事情强加给天,只求的自己的心安理得。所谓缘分,不是上天的恩赐,而是人们自己在生活中的每个细微的举止、每次不经意的选择,所有的这些叠加起来的结果。所谓的天意弄人,也只是人们自己的自我解脱罢了。

假如爱有天意,不过,还是被那种延续了两代人的感情打动了,未结果的感情总是遗憾,所以若是他们的子女,能不期而遇,在上一代的秘密还未揭开以前,恰巧相爱,最终走到一起。这个收尾,承载了两代人、两段感情,让遗憾得以弥补,让幸福得以继续,怎么能不打动人呢。

又听见李健唱起那段旋律,忍不住有些感概,人越充满幻想越容易被感动吧。

又想起李健唱的那首似水流年,第一次听到还是在初高中时期,突然就被歌中“她手中的口琴唱的歌”、“爱是手中捧得红苹果,那年夏天她微笑着不说” 触动,当时恋爱都没有谈过,可就是有着青春时期的那种幻想,被歌中构想出来的赤脚的少年、吹口琴的女孩、红苹果般的爱情这些元素轻易打动,于是就特别喜欢。现在听感觉有时不一样了,就像刚刚分享给一个朋友听,她说的“有种告别青春的感觉,但是很美好”,现在听,确实是更多的是告别青春的感觉了,重点转移到了歌的后半段,“不经意间的回望”,“回忆慢慢淹没”、“多年以后,流年似水…”

似水流年-李键

  • 偶尔在镜子里面
  • 旧时光和我相遇
  • 那片远远的天空
  • 炉火映红的暖冬
  • 大雁飞过秋天的海面
  • 看着奔跑的童年
  • 赤着脚的快乐只不过是仓惶的一转眼
  • 她手中的口琴唱的歌
  • 唤醒贪玩的耳朵
  • 爱是手中捧的红苹果
  • 那年夏天她微笑着不说
  • 让我这一夜长醉
  • 流年似水般滋味
  • 笑中青涩的眼泪
  • 那时光渐渐沉睡
  • 记忆中曾跳动的烛光
  • 今夜又照亮脸庞
  • 这不经意之间的回望
  • 让目光走过那扇窗
  • 生命的河从身边流过
  • 将回忆慢慢淹没
  • 那年春天燃起的篝火
  • 多年以后
  • 泛着泪光闪烁
  • 我愿这一夜长醉
  • 流年似水般滋味

在线听歌地址

蜕壳

每过一段时间回头看,都会觉得之前某些时候的自己就是个傻子

记录过很多东西,初高中时期的好几本日记,之后网络的博客,还有一些老照片更加直白的拎出了以前的旧模样。

初高中也一直都算是个乖小孩,一门心思只在学习之上,其它的什么也不想,也许错过了很多美好的东西吧,可确实要感谢那个时候这位专注的同学,才让现在的自己积攒了一些东西。但是,依旧逃不过上面那句话,那个时候自己在情绪控制上很大程度上就是个傻子,情绪波动、反差尤其大,常常会因小事弄的不愉快、生气,就是折腾自己,有时开心有时郁闷的。

有一天在讨论组里hsy突然发了一张大学时在外烧烤时拍的照片,上面有我、lm、byb,把我吓一跳,简直了,我和lm俩个,就是个杀马特啊用现在的话说。当时是弄了个爆炸头的发型,穿着也很low吧,晒的还有点黑,那时觉得很酷,现在看啊,就是个傻叉啊,好好的小青年,生生的给造型给毁了。

刚才又看了之前写在一个私人博客的东西,很多文字,时间跨度也很大,现在看来分明是已经很显而易见的道理和现实,当时就是不肯承认,就是要硬撑要坚持,费尽心思勉强维持,搞到最后还是没有个结果,还弄得一身伤,也是个傻子了。也好歹跳出来了,看清了,不自欺欺人就好,就那么回事,也就过去了。

现在自己潇潇洒洒的痛斥之前的傻,等过个时候,也是会被后来的我斥为傻吧。听说真正的高人会把故事烂在心里,着实没有到这个境界,要加油,坐等自己再蜕壳,等着看以后会变成什么样。