跳至主要内容

【转】使用::std::vector<>作为管理动态数组的优先选择

使用::std::vector<>作为管理动态数组的优先选择
作者:wangtianxing

提交者:eastvc 发布日期:2003-9-19 17:34:41
原文出处:http://www.cpphelp.net/issue/vector.html

摘要: 本文介绍了C++标准库中的容器类vector,分析了它的优点,并且建议在应用程序中使用它作为动态数组的优先选择,而不是MFC的CArray<>等其他类模板。最后介绍了vector的接口和使用时的注意事项。

在一些使用 MFC 的程序中,经常看到许多程序使用 CArray<>,由于
CArray<>的设计问题,造成使用它的代码的复杂化,增加了维护难度。因此建议使用 ::std::vector<> 代替 CArray<>。

另外,也看到一些程序在用 malloc/realloc/free/new[]/delete[]
等手工管理内存。在应用程序中,手工管理内存是容易导致错误的,应该用 ::std::vector<> 之类的对象来管理动态数组。

由于 MSDN 中关于 ::std::vector 的内容较少,我们在这里做一些介绍,供参考。

不熟悉 CArray<>/WIN32 也没关系,这里提到它们的地方并不太多。

1. CArray<> VS ::std::vector<> ?
CArray<> 和 ::std::vector<>
一样,都是模板类,用于管理任意类型的对象的动态数组。都在解构时释放所管理的动态内存。因此都可以用于代替手工动态数组管理。

但是,CArray<> 是在 C++ 标准化之前很多年(VC++2.0时代)设计的,当时对
C++程序设计,面向对象程序设计,模板程序设计等技术认识严重不足,尤其是当时对面向对象技术的错误信仰与宣传,造成 CArray<>
的设计有重大错误。

在 C++ 语言标准化以后(1998),以及 VC++ 6.0 出世以后,提供了标准的::std::vector<>
模板,基本上在任何方面都要优于 CArray<>。Microsoft 由于要支持老的程序,因此一直保留了
CArray<>,但显然并没有打算按照新的思想去发展它(至少应该提供operator=(CArray const&)吧)。

概括起来,CArray<> 与 ::std::vector<> 有以下不同:

1) CArray<> 是 MFC 中的,::std::vector<> 存在于任何标准的 C++ 实现中。因此,你用熟了 CArray<>
也只能在 MFC 中用,若用熟了 ::std::vector<>,你可以在任何平台的任何 C++
编译器下使用。使用标准的部件也有利于别人理解你的程序。 . CArray<> 继承了 CObject,仅仅为了实现
serialization,这是不恰当的, 违反了 "You don't pay for what you don't use." 的
C++ 设计原则。::std::vector<> 没有继承任何东西,只是实现了管理一个动态数组该做的事。

2) CArray<> 不是一个恰当的值类型,例如下列操作都是不合法的:

CArray<int,int> a;
CArray<int,int> b(a); // error, must use Copy().
b = a; // error, must use Copy().
b == a; // error, you must write your own.
b < a; // error, you must write your own.与 CArray<>
相反,::std::vector<> 是一个认真设计的值类型,天生是可以拷贝构造和可赋值的。如果 T 是可比较的,那么
::std::vector<T> 将自动地是可以比较的。

此外,由于涉及到四个特殊成员函数;

T(); // 缺省构造函数(default constructor)
~T(); // 解构函数(destructor)
T( T const& ); // 拷贝构造函数
T& operator=( T const& ); // 拷贝赋值函数的自动生成,如果使用 CArray() 作为 T
的成员变量,那么上述的四个特殊函数中的后两个将无法自动生成,需要手工写:

struct T
{
T() {}
T( T const& t )
{
a_.Copy( t.a_ );
i_ = t.i_;
d_ = t.d_;
s_ = t.s_;
}
T& operator = ( T const& t )
{
if( this != &t )
{
a_.Copy( t.a_ );
i_ = t.i_;
d_ = t.d_;
s_ = t.s_;
}
return *this;
}
private:
CArray<int,int> a_;
int i_;
double d_;
::std::string s_;
};如果使用 ::std::vector<>:

struct T
{
private:
::std::vector<int> a_;
int i_;
double d_;
::std::string s_;
};上面列出的三个特殊成员函数都不需要写。好处是明显的:当你增减 T 的成员变量时,你不必到
T(T const&) 和 operator=() 中去相应地增减。

3) 没有现成的算法可以对 CArray<> 进行操作,而标准 C++ 里的标准算法大多都可以直接在
::std::vector<> 上运行。例如:

static int const init_vals[] = { 3, 1, 4, 1, 6, 9 };
vector<int> a( init_vals, init_vals + 6 );
*find( a.begin(), a.end(), 6 ) = 5; // 把6改成5
sort( a.begin(), a.end() ); // 排序。可以说,CArray<>
的主要设计错误是把一个本来应该是一个简单的"值"类型的东西设计成一个难用的"对象"类型了。所有的"值"的好特性都丧失了,但那些从CArray<>继承的派生类呢?

CByteArray等的问题与 CArray<> 的问题一样,甚至更多(例如,CPtrArray,永远不要用)。

同样,其他的 MFC container 模板,象 CMap<>, CList<> 等,都有类似问题,都应该用
::std::map<>,::std::list<> 等设计更好的东西代替。

2. ::std::vector<> 在哪里?
::std::vector<> 在头文件 <vector> 中定义:

(注意,标准的 C++ 头文件都没有 .h 后缀,有 .h 的文件是与 C 兼容的,或支持老的不标准的东西,象 <iostream.h>。)

namespace std
{
template<typename T, typename A = allocator<T> >
struct vector
{
// 具体内容稍后讨论
};


template<typename T, typename A>
bool operator == ( vector<T,A> const& a, vector<T,A> const& b );
template<typename T, typename A>
bool operator != ( vector<T,A> const& a, vector<T,A> const& b );
template<typename T, typename A>
bool operator < ( vector<T,A> const& a, vector<T,A> const& b );
template<typename T, typename A>
bool operator >= ( vector<T,A> const& a, vector<T,A> const& b );
template<typename T, typename A>
bool operator > ( vector<T,A> const& a, vector<T,A> const& b );
template<typename T, typename A>
bool operator >= ( vector<T,A> const& a, vector<T,A> const& b );
}vector<> 定义在 namespace std 中,使用时为了减少击键次数,通常使用一个类型定义缩短类型名称:

#include <vector>
typedef ::std::vector<int> IntVector;
IntVector a;
IntVector b( a );
IntVector c;
c = b;
assert( a == c );请注意 <vector> 中定义了六个 vector<T,A>
的比较函数。这些函数只在真的用到时才会被实例化,才会要求 T 也提供 operator==() 和 operator<()。

另外,A = alloctor<T>:用于提供一个用户定义的存储管理类。由于这个参数很少用到,而且在 VC++6
的实现中有问题,不能用,因此以下的讨论忽略这一部分的内容。

3. ::std::vector<> 中的类型定义
vector<> 中定义了一些类型,下面只列出常用的:

typedef T value_type;
typedef T0 iterator;
typedef T1 const_iterator;
typedef T2 reverse_iterator;
typedef T3 const_reverse_iterator;
value_type 就是 vector<T> 的元素类型,也就是 T。当写通用的算法处理任意类型的 vector<> 或其他容器类型时是很有用的。

iterator/const_iterator 是两个 vector<> 的实现定义的未知类型,用于访问vector<> 中的元素,类似于
T*/T const* 指针,他们的区别是一个指向的元素可被修改,另一个只可以读:

typedef ::std::vector<int> IntVector;
IntVector::iterator iter;
IntVector::const_iterator c_iter;
// ...
++iter; iter++; // ok: increment, post-increment.
--iter; iter--; // ok: decrement, post-decrement.
++c_iter; c_iter++; // ok: increment, post-increment.
--c_iter; c_iter--; // ok: decrement, post-decrement.
*iter = 123; // ok.
int k = *iter; // ok.
k = *--c_iter; // ok.
*c_iter = k; // error.
c_iter = iter; // ok: iterator is convertible to const_iterator.
iter = c_iter; // error: can't convert const_iterator to iterator.在使用上
iterator/const_iterator 和 T*/T const* 基本相同,事实上有些vector<> 的实现里就是用 T*/T
const* 实现 iterator/const_iterator 的,但又不可以把 iterator/const_iterator
当作真正的 T*/T const*:

T* p = iter; // may fail to compile.
T const* q = c_iter; // may fail to
compile.reverse_iterator/const_reverse_iterator 与
iterator/const_iterator 类似,但以相反的次序(从尾至头)访问 vector 中的元素。

各种各样的 iterator 在 STL 中有特别重要的意义,但这里我们不做具体介绍。只要理解通过 iterator 可以访问 vector
中的元素,大概相当于一个指示位置的指针就行了。

4. ::std::vector<> 的构造
vector<> 提供了以下构造函数:(忽略 allocator 参数)

vector();
vector( size_t n, T const t=T() );
vector( vector const & );
vector( const_iterator first, const_iterator last );1) vector();

构造一个空的 vector,不包含任何元素。

IntVector v1; // 空的整数向量。2) vector( size_t n, T const t=T() );

构造一个 n 个相同元素 t 组成的 vector。如果不给出 t,那么将用 T() 做缺省值:

IntVector v2( 100, 1234 ); // 100 个 1234.
IntVector v3( 100 ); // 100 个 0。3) vector( vector const& other );

复制构造函数,复制 other 中的内容:

IntVector v4( v2 ); // 100 个 1234。4) vector( const_iterator first,
const_iterator last );

事实上,这个构造函数应该为

template<typename Iter>
vector( Iter first, Iter last );即拷贝任意的序列 [first,last) 到 vector
中。由于 VC++6sp0 编译程序的限制, Iter 被换为 const_iterator 了。不过,碰巧
const_iterator就是 T const*,所以可以如下使用:

int a[] = { 1, 2, 3, 4, 5 };
IntVector v5( a, a + 5 ); // {1,2,3,4,5}
IntVector v6( v5.begin() + 2, v5.end() ); // {3,4,5}5. 访问 vector<> 中的元素
以下成员函数/运算符用于访问 vector 中的一个元素:

T& at( size_t n );
T const& at( size_t n ) const;
T& operator [] ( size_t n );
T const& operator [] ( size_t n ) const;
T& front();
T const& front() const;
T& back();
T const& back() const;请注意,由于 vector 是一个"值"语义的对象,所有的操作函数都必须严格保证 const
的正确性。所以,所有的元素访问方法都有 const 和非 const两个版本。

at(n) 和 operator [] (n) 都返回下标为 n 的元素的引用,他们的区别是,at() 进行下标越界检查,若发现越界,抛出
range_error 异常,operator[]不进行下标检查。

front() 返回下标为 0 的元素的引用,back() 返回最后一个元素的引用。

int a[] = { 4, 1, 4, 1, 5, 8 };
IntVector v( a, a + 6 );
// 使用 front(), back():
v.front() = 3;
v.back() = 9;
// 使用 operator [] ():
for( size_t i = 0; i < v.size(); ++i )
::std::cout << v[i] << '\n';6. ::std::vector<> 的存储管理
以下成员函数用于存储管理:

void reserve( size_t n );
size_t capacity() const;
void resize( size_t n, T t=T() );
void clear();
size_t size() const;
bool empty() const { return size() == 0; }
size_t max_size() const;
另外,push_back(), insert() 等也涉及到存储管理,后面另行介绍。

1) max_size()

返回 vector<T> 理论上可以装的最多 T 的个数。这只是一个理论上的数字, 大概是 4GB/sizeof(T),没有多大实用价值。在程序中不要用。

2) size()

返回 vector<T> 中实际装的 T 的个数。相当于 CArray<>::GetSize()。

3) empty()

如果 vector<T> 中没有任何 T 对象,返回 true。也就是返回 size() == 0。

4) clear();

清除 vector<T> 中的所有 T 对象。执行后 empty() 返回 true。大致相当于 resize(0),但不要求 T
可被缺省构造。相当于 CArray<>::RemoveAll()。

5) resize( size_t n, T t = T() );

将 vector 中的元素个数设置为 n,n 可以大于 size() 也可以小于 size。如果 n 小于 size(),那么 vector
中下标为 n..size()-1 的元素都将被解构。如果 n > size(),那么将在 vector 的后面新增加
n - size() 个相同的元素 t。在增大 vector 时,可能发生存储再次分配。总之,调用resize( n, t )
后,(size() == n) 成立。

请注意,如果调用 resize( n ) 不带参数 t ,那么 T 必须可以缺省构造。

6) reserve( size_t n );

事先分配至少可以保存 n 个 T 对象的空间。调用后 (capacity() >= n)成立。

7) capacity();

返回已经分配的存储空间够容纳的 T 类型对象的个数。后续的增加元素操作(如 push_back(), insert())如果增加元素后
vector 中的总元素个数不超过 capacity(),那么 vector 的实现保证不重新分配存储空间。

vector 管理的动态存储空间是连续的。执行操作

IntVector v(7, 1); // seven ones.
v.reserve( 12 );后,v 的状态可以用下图表示:

/--size()---\
|1|1|1|1|1|1|1|-|-|-|-|-|
\--capacity()---------/其中,1 是已经构造的 int 类型的对象,- 是可以构造一个 int
类型的对象,但还没有构造的原始空间。再执行

v.push_back( 2 );
v.push_back( 3 );后,v 的状态可用下图表示:

/----size()-----\
|1|1|1|1|1|1|1|2|3|-|-|-|
\----capacity()-------/执行 resize( 11, 4 ); 后:

/----size()---------\
|1|1|1|1|1|1|1|2|3|4|4|-|
\----capacity()-------/capacity() >= size() 总是成立的。对于下标为
[size()..capacity()-1]的未构造对象的存储空间,是不可以访问的:

v[11] = 5; // undefined behavior - anything can happen.7. 添加元素到 vector 中
下列操作添加元素到 vector 中,并可能引起存储分配:

void push_back( T const& t );
void insert( iterator pos, T const& t=T() );
void insert( iterator pos, size_t n, T const& t );
template<typename Iter>
void insert( iterator pos, Iter first, Iter last );push_back()
是把一个元素添加到 vector 的末尾。insert() 是把一个 t,或 n 个 t,或从 first 开始到 last
结束的一个序列插入到 pos 指示的位置之前。

当插入元素后 size() 将会大于 capacity() 时,将引起自动存储分配。vector
将会分配一个比需要的存储区大若干倍(通常是1.5到2)的新的存储区,把老的元素拷贝过去,同时完成添加或插入,然后释放老的存储区。

这就是说,vector 自动存储分配的空间大小是指数式增长的,这可以保证多次添加元素到 vector 中时,平均用时是接近于常数的。

IntVector v;

// add 0, 1, ..., 99 to v:
for( int i = 0; i < 100; ++i )
v.push_back( i );

// append 9, 8, 7,..., 0 to the end:
int a[] = { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
v.insert( v.end(), a, a + 10 );8. 删除元素
下列成员函数完成元素删除:

void erase( iterator );
void erase( iterator first, iterator last );
void pop_back();
void clear();这些函数分别删除一个,一串,最后一个,或全部元素。

IntVector v;
for( int i = 0; i < 100; ++i )
v.push_back( i );

// 删除 50, 51, ..., 89:
v.erase( v.begin() + 50, v.end() - 10 );

// 删除 49, 48:
v.pop_back();
v.pop_back();

// 全部删除:
v.clear();注意,删除操作不会引起存储分配,因此 capacity() 不变。

9. 作为序列访问 vector 中的元素
序列(sequence)在 STL 中是一个非常重要的概念,所有的容器类型和算法都涉及到,而且所有的算法都是建立在"序列"这个概念之上的。

"序列"是一个线性结构,由一个指示其起始和一个指示结束的叠代子(iterator)来决定。如果 first 和 last
是某种类型的叠代子,那么经常用[first, last) 来表示一个序列。注意,first 指向的元素是这个序列的一个元素,而 last
指示的是这个序列最后一个元素之后的位置,可能根本没有元素可以访问。这种半闭半开的区间表示是整个 C++ 标准中的约定,而且确实可以简化程序。

叠代子是传统的 C/C++ 中指针的抽象和进一步分类。在 C++ 中把 iterator划分为 input iterator, output
iterator, forward iterator,bidirectional iterator, random access
iterator 五类。其中的 randomaccess iterator 是最强的一类,即允许的操作最多。C++
中的指针类型以及vector<>/deque<> 的
iterator/const_iterator/reverse_iterator/const_reverse_iterator 都满足
random access iterator 的要求。

vector<> 中定义了以下函数用于获取被控制(管理的)序列(动态数组)的各种叠代子:

iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
reverse_iterator rbegin();
reverse_iterator rend();
const_reverse_iterator rbegin() const;
const_reverse_iterator rend() const;这里我们不讨论叠代子的一般概念,只举几个 random access
iterator 的例子:

int a[] = { 1, 2, 3, 4, 5, 6 };[a, a + 6) 是一个随机访问序列,指示了 a[]
中的所有元素。这里叠代子的类型为 int*。

[a + 2, a + 4) 也是一个序列,指示了 a[] 中的 3, 4 两个元素。叠代子的类型仍然是 int*。

IntVector v( 100, 1 ); // 100 个 1。[v.begin(), v.end()) 是一个随机访问序列,指示了 v
中的所有元素,叠代子的类型是 IntVector::iterator。

[v.begin() + 10, v.end() - 20 ) 也是一个随机访问序列,指的是 v 中除了头 10 个和尾 20 个元素外的其它元素。

[v.rbegin(), v.rend() ) 是一个随机访问序列,指的是 v 中的所有元素,但与 [v.begin(), v.end()
) 不同,这个序列是从尾到头遍历所有元素。

[v.rbegin() + 20, v.rend() - 10) 与 [v.begin() + 10, v.end() - 20
)指示的元素相同,但遍历顺序相反。

下图是有十个元素的 vector 的 begin()/end()/rbegin()/end() 的示意:

begin() ----------> end()
| |
v v
|0|1|2|3|4|5|6|7|8|9|
^ ^
| |
rend() <---------- rbegin()

IntVector v;
for( int i = 0; i < 10; ++i )
v.push_back( i );

// print 0, 1, 2, ..., 9:
for( IntVector::iterator i = v.begin(); i != v.end(); ++i )
::std::cout << *i << '\n';

// print 9, 8, ..., 0:
for( IntVector::reverse_iterator i = v.rbegin(); i != v.rend(); ++i)
::std::cout << *i << '\n';
除了使用 begin()/end()/rbegin()/rend() 来遍历 vector 中的元素外,由于 vector
管理的空间是连续的,因此可以直接取地址进行处理:

::std::vector<HANDLE> handles;
handles.push_back( handle1 );
handles.push_back( handle2 );


::WaitForMultipleObjects(handles.size(), &handles[0],TRUE,
INFINITE);这在与 C 库函数接口时尤其有用。

10. 赋值和交换
vector<> 是可以赋值的,这也是一般的"值"类型必须提供的操作:

IntVector v( 100, 123 );
IntVector v1;
v1 = v;vector 另外还提供了

template<typename Iter>
void assign( Iter first, Iter last );
void assign( size_t n, T const& t = T() );用于赋值:

int a[] = { 1, 3, 5, 7 };
v.assign( a, a + 4 ); // v 将包含 1, 3, 5, 7.
v.assign( 100 ); // 100 个 0。还有一个很重要的操作:

void swap( vector& v ) throw();用于交换两个同类型的 vector
的值。它的特点是快速(只需要交换内部的三个指针),不产生异常。这在写一些保证异常安全的程序时非常有用。

事实上,swap() 基本上已经被当作类似于 operator=() 的一个"值"类型应该提供的基本操作,::std::swap()
也应该为用户定义的类型进行特例化,调用相应的类的成员 swap() 函数:

struct MyVal
{
// blah blah.
void swap( MyVal& ) throw();
};

namespace std {
template<>
void swap( MyVal& a, MyVal& b )
{ a.swap( b ); }
}关于 swap(),值得专文讨论。这里我们只指出,vector<T>::swap() 是快速的,不抛出异常的,很有价值。

11. 使用 vector 时的存储管理策略
从前面的介绍中可以看到,vector 的自动存储分配是指数式的增加存储空间,而且永不缩小已经分配的空间。这在大多数情况下是合适的。
如果应用程序事先知道要用到的元素个数,可以先调用 reserve()
来保留(分配)空间,这样可以避免以后增加元素时不必要的重新分配和元素拷贝:

IntVector v;
v.reserve( 100 );
for( int i = 0; i < 100; ++i )
v.push_back( i );请注意,reserve() 和 resize() 是本质上完全不同的。reserve(n)
保留的是未使用而能够使用的原始空间,而 resize(n) 是真的创建了 n 个对象:

IntVector v;
v.resize( 100 ); // v 已经包含 100 个 0.
for( int i = 0; i < 100; ++i )
v[i] = i; // 可以赋值有时候,一个 vector 可能增长到较多个元素,然后又减少到较少的元素个数,这时,可能希望缩小
vector 分配的空间以节约内存。CArray<> 中提供了 FreeExtra(),但 vector<>
并没有提供相应的函数。这时必须进行复制:

IntVector(v).swap( v );有一种看法认为拷贝构造函数同时也复制了capacity(),而标准中并没有很明确地指出这一点,因此更安全的方法是

IntVector(v.begin(),v.end()).swap(v);如果一个 vector
中可能要存储的元素个数较多(例如,超过100个),而且事先无法确定其个数(因此无法调用 reserve()),那么通常 vector
不是一个恰当的数据结构,应该考虑用 ::std::deque<>。与 vector<>
相比,deque<>不保证背后的存储空间是连续的(因此象上面的WaitForMultipleObjects()中的应用不能用
deque<HANDLE> 代替),但有较好的伸缩性,还可以在数组的前端用 push_front()/pop_front()
增减元素(hence its name, doubly endedqueue)。

评论

此博客中的热门博文

【转】VxWorks中的地址映射

在运用嵌入式系统VxWorks和MPC860进行通信系统设计开发时,会遇到一个映射地址不能访问的问题。 缺省情况下,VxWorks系统已经进行了如下地址的映射:   memory地址、bcsr(Board Control and Status)地址、PC_BASE_ADRS(PCMCIA)地址、Internal Memory地址、rom(Flach memory)地址等,但是当你的硬件开发中要加上别的外设时,如(falsh、dsp、FPGA等),对这些外设的访问也是通过地址形式进行读写,如果你没有加相应的地址映射,那么是无法访问这些外设的。   和VxWorks缺省地址映射类似,你也可以进行相应的地址映射。   如下是地址映射原理及实现:   1、 地址映射结构 在Tornado\target\h\vmLib.h文件中 typedef struct phys_mem_desc { void *virtualAddr; void *physicalAddr; UINT len; UINT initialStateMask; /* mask parameter to vmStateSet */ UINT initialState; /* state parameter to vmStateSet */ } PHYS_MEM_DESC; virtualAddr:你要映射的虚拟地址 physicalAddr:硬件设计时定义的实际物理地址 len;要进行映射的地址长度 initialStateMask:可以初始化的地址状态: 有如下状态: #define VM_STATE_MASK_VALID 0x03 #define VM_STATE_MASK_WRITABLE 0x0c #define VM_STATE_MASK_CACHEABLE 0x30 #define VM_STATE_MASK_MEM_COHERENCY 0x40 #define VM_STATE_MASK_GUARDED 0x80 不同的CPU芯片类型还有其特殊状态 initialState:实际初始化的地址状态: 有如下状态: #define VM_STATE_VALID 0x01 #define VM_STATE_VALID_NOT 0x00 #define VM_STATE_WRITA

【转】cs8900网卡的移植至基于linux2.6内核的s3c2410平台

cs8900网卡的移植至基于linux2.6内核的s3c2410平台(转) 2008-03-11 20:58 硬件环境:SBC-2410X开发板(CPU:S3C2410X) 内核版本:2.6.11.1 运行环境:Debian2.6.8 交叉编译环境:gcc-3.3.4-glibc-2.3.3 第一部分 网卡CS8900A驱动程序的移植 一、从网上将Linux内核源代码下载到本机上,并将其解压: #tar jxf linux-2.6.11.1.tar.bz2 二、打开内核顶层目录中的Makefile文件,这个文件中需要修改的内容包括以下两个方面。 (1)指定目标平台。 移植前:         ARCH?= $(SUBARCH) 移植后: ARCH            :=arm (2)指定交叉编译器。 移植前: CROSS_COMPILE ?= 移植后: CROSS_COMPILE   :=/opt/crosstool/arm-s3c2410-linux-gnu/gcc-3.3.4-glibc-2.3.3/bin/arm-s3c2410-linux-gnu- 注:这里假设编译器就放在本机的那个目录下。 三、添加驱动程序源代码,这涉及到以下几个方面。(1)、从网上下载了cs8900.c和cs8900.h两个针对2.6.7的内核的驱动程序源代码,将其放在drivers/net/arm/目录下面。 #cp cs8900.c ./drivers/net/arm/ #cp cs8900.h ./drivers/net/arm/ 并在cs8900_probe()函数中,memset (&priv,0,sizeof (cs8900_t));函数之后添加如下两条语句: __raw_writel(0x2211d110,S3C2410_BWSCON); __raw_writel(0x1f7c,S3C2410_BANKCON3); 注:其原因在"第二部分"解释。 (2)、修改drivers/net/arm/目录下的Kconfig文件,在最后添加如下内容: Config ARM_CS8900    tristate "CS8900 support" depends on NET_ETHERNET && A

【转】多迷人Gtkmm啊

前边已经说过用glade设计界面然后动态装载,接下来再来看看怎么改变程序的皮肤(主题)     首先从 http://art.gnome.org/themes/gtk2 下载喜欢的主题,从压缩包里提取gtk-2.0文件夹让它和我们下边代码生成的可执行文件放在同一个目录下,这里我下载的的 http://art.gnome.org/download/themes/gtk2/1317/GTK2-CillopMidnite.tar.gz     然后用glade设计界面,命名为main.glade,一会让它和我们下边代码生成的可执行程序放在同一个目录下边     然后开始写代码如下: //main.cc #include <gtkmm.h> #include <libglademm/xml.h> int main(int argc, char *argv[]) {     Gtk::Main kit(argc,argv);         Gtk::Window *pWnd;        gtk_rc_parse("E:\\theme-viewer\\themes\\gtk-2.0\\gtkrc");       Glib::RefPtr<Gnome::Glade::Xml> refXml;     try     {         refXml = Gnome::Glade::Xml::create("main.glade");     }     catch(const Gnome::Glade::XmlError& ex)     {         Gtk::MessageDialog dialog("Load glade file failed!", false,       \                                   Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);         dialog.run();               return 1;     }         refXml->get_widget("main", pWnd);     if(pW