[转]const的用法
1.修饰类的数据成员
const数据成员只是在某个对象的生命周期内是常量.如果创建多个对象,那么每个对象的const数据成员可以不一样.所以,不能在类声明中初始化const数据成员.如:
class A
{
const int size = 100;// 错误
int array[size];//错误,未知的数组大小
}
const类数据成员,只能在类的构造函数的初始化列表中进行.要想建立整个类都是恒定的常量的数据,可以用类中的枚举.如:
class A
{
enum {size1 = 100, size2 = 200};
int array[size1];
}
2.只有声明为const的成员函数才能被一个const对象调用
3.在另一连接文件中引用const常量.
extern const int i
4.对于const对象,c++既允许对其进行静态初始化,也允许对其进行动态初始化.const对象具有从构造函数完成到析构函数执行之前的不变性.
5.常量与数组的组合有什么特殊吗?
我们给出下面的代码:
const int size[3]={10,20,50};
int array[size[2]];
编译通不过!为什么呢?const可以用于集合,但编译器不能把一个集合存放在它的符号表里,所以必须分配内存。在这种情况下,const意味着“不能改变的一块存储”。然而,其值在编译时不能被使用,因为编译器在编译时不需要知道存储的内容。自然,作为数组的大小就不行了
你再看看下面的例子:
class A
{
public:
A(int i=0):test[2]({1,2}) {} file://你/认为行吗?
private:
const int test[2];
};
C++标准有一个规定,不允许无序对象在类内部初始化,数组显然是一个无序的,所以这样的初始化是错误的!对于他,只能在类的外部进行初始化,如果想让它通过,只需要声明为静态的,然后初始化。
这里我们看到,常量与数组的组合没有什么特殊!一切都是数组惹的祸!
(7)什么情况下为const分配内存?
以下是我想到的可能情况,当然,有的编译器进行了优化,可能不分配内存。
A、作为非静态的类成员时;
B、用于集合时;
C、被取地址时;
D、在main函数体内部通过函数来获得值时;
E、const的 class或struct有用户定义的构造函数、析构函数或基类时;。
F、当const的长度比计算机字长还长时;
G、参数中的const;
H、使用了extern时。
(8)与static搭配会不会有问题?
假设有一个类:
class A
{
public:
......
static void f() const { ......}
......
};
我们发现编译器会报错,因为在这种情况下static不能够与const共存!因为static没有this指针,但是const修饰this指针.
(9)如何修改常量?
有时候我们却不得不对类内的数据进行修改,但是我们的接口却被声明了const,那该怎么处理呢?我对这个问题的看法如下:
1)标准用法:mutable
class A
{
public:
A(int i=0):test(i) { }
void Setvalue(int i)const { test=i; }
private:
mutable int test; file://这/里处理!
};
2)强制转换:const_cast
class A
{
public:
A(int i=0):test(i) { }
void Setvalue(int i)const
{ const_cast <int>(test)=i; }//这里处理!
private:
int test;
};
(10)最后我们来讨论一下常量对象的动态创建。
既然编译器可以动态初始化常量,就自然可以动态创建,例如:
const int* pi=new const int(10);
这里要注意2点:
1)const对象必须被初始化!所以(10)是不能够少的。
2)new返回的指针必须是const类型的。
那么我们可不可以动态创建一个数组呢?
答案是否定的,因为new内置类型的数组,不能被初始化。
posted @
2006-06-15 06:59 Jerry Cat 阅读(502) |
评论 (0) |
编辑 收藏
[转贴]音频录入
一、数字音频基础知识
Fourier级数:
任何周期的波形可以分解成多个正弦波,这些正弦波的频率都是整数倍。级数中其他正线波的频率是基础频率的整数倍。基础频率称为一级谐波。
PCM:
pulse code modulation,脉冲编码调制,即对波形按照固定周期频率采样。为了保证采样后数据质量,采样频率必须是样本声音最高频率的两倍,这就是Nyquist频率。
样本大小:采样后用于存储振幅级的位数,实际就是脉冲编码的阶梯数,位数越大表明精度越高,这一点学过数字逻辑电路的应该清楚。
声音强度:
波形振幅的平方。两个声音强度上的差常以分贝(db)为单位来度量,
计算公式如下:
20*log(A1/A2)分贝。A1,A2为两个声音的振幅。如果采样大小为8位,则采样的动态范围为20*log(256)分贝=48db。如果样本大小为16位,则采样动态范围为20*log(65536)大约是96分贝,接近了人听觉极限和痛苦极限,是再线音乐的理想范围。windows同时支持8位和16位的采样大小。
二、相关API函数,结构,消息
对于录音设备来说,windows 提供了一组wave***的函数,比较重要的有以下几个:
打开录音设备函数
MMRESULT waveInOpen(
LPHWAVEIN phwi, //输入设备句柄
UINT uDeviceID, //输入设备ID
LPWAVEFORMATEX pwfx, //录音格式指针
DWORD dwCallback, //处理MM_WIM_***消息的回调函数或窗口句柄,线程ID
DWORD dwCallbackInstance,
DWORD fdwOpen //处理消息方式的符号位
);
为录音设备准备缓存函数
MMRESULT waveInPrepareHeader( HWAVEIN hwi, LPWAVEHDR pwh, UINT bwh );
给输入设备增加一个缓存
MMRESULT waveInAddBuffer( HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh );
开始录音
MMRESULT waveInStart( HWAVEIN hwi );
清除缓存
MMRESULT waveInUnprepareHeader( HWAVEIN hwi,LPWAVEHDR pwh, UINT cbwh);
停止录音
MMRESULT waveInReset( HWAVEIN hwi );
关闭录音设备
MMRESULT waveInClose( HWAVEIN hwi );
Wave_audio数据格式
typedef struct {
WORD wFormatTag; //数据格式,一般为WAVE_FORMAT_PCM即脉冲编码
WORD nChannels; //声道
DWORD nSamplesPerSec; //采样频率
DWORD nAvgBytesPerSec; //每秒数据量
WORD nBlockAlign;
WORD wBitsPerSample;//样本大小
WORD cbSize;
} WAVEFORMATEX;
waveform-audio 缓存格式
typedef struct {
LPSTR lpData; //内存指针
DWORD dwBufferLength;//长度
DWORD dwBytesRecorded; //已录音的字节长度
DWORD dwUser;
DWORD dwFlags;
DWORD dwLoops; //循环次数
struct wavehdr_tag * lpNext;
DWORD reserved;
} WAVEHDR;
相关消息
MM_WIM_OPEN:打开设备时消息,在此期间我们可以进行一些初始化工作
MM_WIM_DATA:当缓存已满或者停止录音时的消息,处理这个消息可以对缓存进行重新分配,实现不限长度录音
MM_WIM_CLOSE:关闭录音设备时的消息。
相对于录音来说,回放就简单的多了,用到的函数主要有以下几个:
打开回放设备
MMRESULT waveOutOpen(LPHWAVEOUT phwo, UINT uDeviceID, LPWAVEFORMATEX pwfx, DWORD dwCallback, DWORD dwCallbackInstance, DWORD fdwOpen );
为回放设备准备内存块
MMRESULT waveOutPrepareHeader(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh );
写数据(放音)
MMRESULT waveOutWrite(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh );
相应的也有三个消息,用法跟录音的类似:
三、程序设计
一个录音程序的简单流程: 打开录音设备waveInOpen===>准备wave数据头waveInPrepareHeader===>
准备数据块waveInAddBuffer===>开始录音waveInStart===>停止录音(waveInReset) ===>
关闭录音设备(waveInClose)
当开始录音后当buffer已满时,将收到MM_WIM_DATA消息,处理该消息可以保存已录好数据。
回放程序比这个要简单的多: 打开回放设备waveOutOpen===>准备wave数据头waveOutPrepareHeader===>写wave数据waveOutWrite===>
停止放音(waveOutRest) ===>关闭回放设备(waveOutClose)
如何处理MM消息: MSDN告诉我们主要有 CALLBACK_FUNCTION、CALL_BACKTHREAD、CALLBACK_WINDOW 三种方式,常用的是
Thread,window方式。
线程模式
waveInOpen(&hWaveIn,WAVE_MAPPER,&waveform,m_ThreadID,NULL,CALLBACK_THREAD),我们可以继承MFC的CwinThread类,只要相应的处理线程消息即可。
MFC线程消息的宏为:
ON_THREAD_MESSAGE,
可以这样添加消息映射: ON_THREAD_MESSAGE(MM_WIM_CLOSE, OnMM_WIM_CLOSE)
窗口模式
类似于线程模式,参见源程序即可
posted @
2006-06-15 06:57 Jerry Cat 阅读(1102) |
评论 (0) |
编辑 收藏
[转]某些公司的笔试题
1.想想结果是如何出来的:
int main()
{
unsigned char i=1;
i-=3;
printf("the value of i is:%d",i);
}
结果:the value of i is:254
答案:
0000 0001
- 0000 0011
------------------
1111 1110 而 1111 1110 化为十制进正好是254!
2.看看下列的程序有什么问题
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
请问运行Test函数会有什么样的结果?为什么?
答案:1.内存泄漏 2.str仍然是空指针
3.
int n = 10;
为什么sizeof ( n ) = 4?
答案:sizeof(x)返回x的类型占用的字节数,该机器是在32位下
4.请问C++的类和C里面的struct有什么区别?
5.请讲一讲析构函数和虚函数的用法和作用?
6.全局变量和局部变量有什么区别?是怎么实现的?
7.设计函数 int atoi(char *s)。
8.int i=(j=4,k=8,l=16,m=32); printf(“%d”, i); 输出是多少?
9.解释局部变量、全局变量和静态变量的含义
10.解释堆和栈的区别。
11.论述含参数的宏与函数的优缺点。
12.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。
13.写一个函数,将其中的 都转换成4个空格。
14.Windows程序的入口是哪里?写出Windows消息机制的流程。
15.如何定义和实现一个类的成员函数为回调函数?
16.C++里面是不是所有的动作都是main()引起的?如果不是,请举例。
17.C++里面如何声明const void f(void)函数为C程序中的库函数?
18.下列哪两个是等同的
int b;
A const int* a = &b;
B const* int a = &b;
C const int* const a = &b;
D int const* const a = &b;
19.内联函数在编译时是否做参数类型检查?
void g(base & b)
{
b.play;
}
void main()
{
son s;
g(s);
return;
}
20.请你分别划划OSI的七层网络结构图,和TCP/IP的五层结构图?
21.请你详细的解释一下IP协议的定义,在哪个层上面,主要有什么作用? TCP与UDP呢?
22.请问交换机和路由器分别的实现原理是什么?分别在哪个层次上面实现的?
头文件中<> 和“”的区别?
4、定义一个宏,输入两个参数,输出积。
1. 以下三条输出语句分别输出什么?[C易] (视编译器而定)
char str1[] = "abc";
char str2[] = "abc";
const char str3[] = "abc";
const char str4[] = "abc";
const char* str5 = "abc";
const char* str6 = "abc";
cout << boolalpha << ( str1==str2 ) << endl; // 输出什么?
cout << boolalpha << ( str3==str4 ) << endl; // 输出什么?
cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?
答:分别输出false,false,true。str1和str2都是字符数组,每个都有其自己的存储区,它们的值则是各存储区首地址,不等;str3 和str4同上,只是按const语义,它们所指向的数据区不能修改。str5和str6并非数组而是字符指针,并不分配存储区,其后的“abc”以常量形式存于静态数据区,而它们自己仅是指向该区首地址的指针,相等。
12. 以下代码中的两个sizeof用法有问题吗?[C易]
void UpperCase( char str[] ) // 将 str 中的小写字母转换成大写字母
{
for( size_t i=0; i
if( 'a'<=str[i] && str[i]<='z' )
str[i] -= ('a'-'A' );
}
char str[] = "aBcDe";
cout << "str字符长度为: " << sizeof(str)/sizeof(str[0]) << endl;
UpperCase( str );
cout << str << endl;
答:函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。
[color=#DC143C]13. 非C++内建型别 A 和 B,在哪几种情况下B能隐式转化为A?[C++中等]
答:
a. class B : public A { ……} // B公有继承自A,可以是间接继承的
b. class B { operator A( ); } // B实现了隐式转化为A的转化
c. class A { A( const B& ); } // A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数
d. A& operator= ( const A& ); // 赋值操作,虽不是正宗的隐式类型转换,但也可以勉强算一个[/color]
4. 以下代码有什么问题?[C++易]
struct Test
{
Test( int ) {}
Test() {}
void fun() {}
};
void main( void )
{
Test a(1);
a.fun();
Test b();
b.fun();
}
答:变量b定义出错。按默认构造函数定义对象,不需要加括号。
5. 以下代码有什么问题?[C++易]
cout << (true?1:"1") << endl;
答:三元表达式“?:”问号后面的两个操作数必须为同一类型。
8. 以下代码能够编译通过吗,为什么?[C++易]
unsigned int const size1 = 2;
char str1[ size1 ];
unsigned int temp = 0;
cin >> temp;
unsigned int const size2 = temp;
char str2[ size2 ];
答:str2定义出错,size2非编译器期间常量,而数组定义要求长度必须为编译期常量。
2. 以下反向遍历array数组的方法有什么错误?[STL易]
vector array;
array.push_back( 1 );
array.push_back( 2 );
array.push_back( 3 );
for( vector::size_type i=array.size()-1; i>=0; --i ) // 反向遍历array数组
{
cout << array[i] << endl;
}
答:首先数组定义有误,应加上类型参数:vector array。其次vector::size_type被定义为unsigned int,即无符号数,这样做为循环变量的i为0时再减1就会变成最大的整数,导致循环失去控制。
[color=#DC143C]9. 以下代码中的输出语句输出0吗,为什么?[C++易]
struct CLS
{
int m_i;
CLS( int i ) : m_i(i) {}
CLS()
{
CLS(0);
}
};
CLS obj;
cout << obj.m_i << endl;
答:不能。在默认构造函数内部再调用带参的构造函数属用户行为而非编译器行为,亦即仅执行函数调用,而不会执行其后的初始化表达式。只有在生成对象时,初始化表达式才会随相应的构造函数一起调用。[/color]
[color=#DC143C]10. C++中的空类,默认产生哪些类成员函数?[C++易]
答:
class Empty
{
public:
Empty(); // 缺省构造函数
Empty( const Empty& ); // 拷贝构造函数
~Empty(); // 析构函数
Empty& operator=( const Empty& ); // 赋值运算符
Empty* operator&(); // 取址运算符
const Empty* operator&() const; // 取址运算符 const
};[/color]
[color=#DC143C]3. 以下两条输出语句分别输出什么?[C++难]
float a = 1.0f;
cout << (int)a << endl;
cout << (int&)a << endl;
cout << boolalpha << ( (int)a == (int&)a ) << endl; // 输出什么?
float b = 0.0f;
cout << (int)b << endl;
cout << (int&)b << endl;
cout << boolalpha << ( (int)b == (int&)b ) << endl; // 输出什么?
答:分别输出false和true。注意转换的应用。(int)a实际上是以浮点数a为参数构造了一个整型数,该整数的值是1,(int&)a则是告诉编译器将a当作整数看(并没有做任何实质上的转换)。因为1以整数形式存放和以浮点形式存放其内存数据是不一样的,因此两者不等。对b的两种转换意义同上,但是0的整数形式和浮点形式其内存数据是一样的,因此在这种特殊情形下,两者相等(仅仅在数值意义上)。
注意,程序的输出会显示(int&)a=1065353216,这个值是怎么来的呢?前面已经说了,1以浮点数形式存放在内存中,按 ieee754规定,其内容为0x0000803F(已考虑字节反序)。这也就是a这个变量所占据的内存单元的值。当(int&)a出现时,它相当于告诉它的上下文:“把这块地址当做整数看待!不要管它原来是什么。”这样,内容0x0000803F按整数解释,其值正好就是1065353216 (十进制数)。
通过查看汇编代码可以证实“(int)a相当于重新构造了一个值等于a的整型数”之说,而(int&)的作用则仅仅是表达了一个类型信息,意义在于为cout<<及==选择正确的重载版本。
[/color]
6. 以下代码有什么问题?[STL易]
typedef vector IntArray;
IntArray array;
array.push_back( 1 );
array.push_back( 2 );
array.push_back( 2 );
array.push_back( 3 );
// 删除array数组中所有的2
for( IntArray::iterator itor=array.begin(); itor!=array.end(); ++itor )
{
if( 2 == *itor ) array.erase( itor );
}
答:同样有缺少类型参数的问题。另外,每次调用“array.erase( itor );”,被删除元素之后的内容会自动往前移,导致迭代漏项,应在删除一项后使itor--,使之从已经前移的下一个元素起继续遍历。
[color=#DC143C]11. 写一个函数,完成内存之间的拷贝。[考虑问题是否全面]
答:
void* mymemcpy( void *dest, const void *src, size_t count )
{
char* pdest = static_cast( dest );
const char* psrc = static_cast( src );
if( pdest>psrc && pdest
{
for( size_t i=count-1; i!=-1; --i )
pdest[i] = psrc[i];
}
else
{
for( size_t i=0; i
pdest[i] = psrc[i];
}
return dest;
}[/color]
原题如下:
#i nclude
struct bit
{
int a:3;
int b:2;
int c:3;
};
int main(int argc, char* argv[])
{
bit s;
char *c = (char*)&s;
*c = 0x99;
cout <<<<<<
return 0;
}
Output:?
答案:
此题在在X86 little-endian 下是 1 -1 -4
在powerPC等big endian机器下,还要重新考虑
因为0x99在内存中表示为 100 11 001 , a = 001, b = 11, c = 100
当c为有符号数时, c = 100, 最高位1表示c为负数,负数在计算机用补码表示,所以c = -4;同理
b = -1;当c为有符合数时, c = 100,即 c = 4,同理 b = 3
位域的概念
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:
struct 位域结构名
{ 位域列表 };
其中位域列表的形式为: 类型说明符 位域名:位域长度
例如:
struct bs
{
int a:8;
int b:2;
int c:6;
};
位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:
struct bs
{
int a:8;
int b:2;
int c:6;
}data;
说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明:
1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs
{
unsigned a:4
unsigned :0 /*空域*/
unsigned b:4 /*从下一单元开始存放*/
unsigned c:4
}
在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。
2. 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。
3. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
struct k
{
int a:1
int :2 /*该2位不能使用*/
int b:3
int c:2
};
从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。
二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名·位域名 位域允许用各种格式输出。
main(){
struct bs
{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
pbit = new bs;
bit.a=1;
bit.b=7;
bit.c=15;
pbit->a = 1;
pbit->b = 2;
pbit->c = 3;
printf("%d, %d, %d",bit.a, bit.b, bit.c);
printf("%d, %d, %d",pbit->a, pbit->b, pbit->c);
}
一、 单项选择题(从四个备选答案中选择一个正确答案,每小题1分,共20分)
1. C++中,关键字struct和class的区别仅仅在于( C )。
(A)struct 用来定义结构体, 而class用来定义类;
(B)struct 用来定义类, 而class用来定义类结构体;
(C)struct定义的类的缺省成员为公有的,而class定义的类的缺省成员为私有的;
(D)struct定义的类的成员须全部为公有的,而class定义的类的成员可以为私有的;
2. 以下程序执行后,输出结果为( C ).
#i nclude
int Var=3 ;
void main(void)
{ int Var=10;
::Var++;
cout<<”Var=”<<<”, ::Var=”<<::Var<
}
(A)Var=11, ::Var=11 (B)Var=11, ::Var=3
(C)Var=10, ::Var=4 (D)Var=4, ::Var=10
3. 抽象基类是指( C )。
(A)嵌套类 (B)派生类
(C)含有纯虚函数 (D)多继承类
4.如果有#define AREA(a,b)a+b 则语句int s=AREA(3,4)*AREA(3,4)执行后变量s值为( D )。
(A) 24 (B)49 (C)144 (D)19
5. C++中条件表达式的值为( C )。
(A)–1或者+1 (B)–231~231 –1 (C)0或者1 (D) 0~231–1
6. 现在有以下语句:
struct MyBitType
{ int a:3;
unsigned b:3;
unsigned c:20;
int d;
};
int sz=sizeof(MyBitType);
则执行后,变量sz的值将得到( B )。
(A)2 (B)8 (C)28 (D)58
7. 假设有一个C++类名为Country, 则此类的析构函数为( C ).
(A)::Country() (B)void ~Country(void)
(C)~Country() (D)void ~Country()
8. 如果定义一个C++类CDate, 语句“CDate *p = new CDate;”的含义是( A )。
(A)调用CDate类的缺省构造函数从内存中分配一个CDate类型的对象,并将该对象地址赋值给指针p;
(B)调用CDate类的拷贝构造函数从内存中分配一个CDate类型的对象,并将该对象地址赋值给指针p;
(C)调用CDate类的析构函数从内存中分配一个CDate类型的对象,并将该对象地址赋值给指针p;
(D)从内存中释放指针p所指向的CDate类的对象;
9.如果有一个类CRect及语句“CRect x1, x2;” 要使语句 “x1=x2;”合法,可在类中定义成员函数( C )。
(A) int operator(x2) (B)int operator=(x2)
(C) void operator=(CRect &); (D) void operator=()
10. 结构体变量S实际所占内存的大小为( A )字节。
(A)sizeof(S) (B)strlen(S)
(C)结构体中最长成员的长度 (D)结构体中最短成员的长度
11.在C++中,下列算符( D )不能重载。
(A)<< (B)>> (C)delete (D)::
12.下列正确的是( D )
(A)结构不能有构造函数、析构函数; (B)缺省时类的成员是公有的;
(C)类中如果定义了析构函数,则必须定义构造函数;
(D)缺省时结构的成员是公有的;
13. 下列关于静态数据成员正确的是( B )
(A)可以通过this指针访问静态数据; (B)可以用类名和作用域访问静态数据;
(C)静态数据在类内声明并初始化; (D)只能由该类的静态成员函数访问;
14. 下列关于友元正确的说法是( D )
(A)友元只能在类的public区声明;(B)友元具有this指针;
(C)类的成员函数不能声明为另一个类的友元;
(D)一个函数如果被声明为一个类的友元,则该函数具有访问该类私有成员的权利。
15. 基类的( A )在派生类内不能被访问。
(A)私有成员 (B)保护成员
(C)公有数据成员 (D)公有静态数据成员
16. 下列关于运算符重载的描述中正确的是( D )
(A)运算符重载可以改变该运算符的优先级;
(B)运算符重载可以改变该运算符目数,即该算符运算的操作数个数;
(C)运算符重载函数只能在类中定义;
(D)new和delete允许重载;
17.左值是指( A )
(A)赋值算符左边的变量; (B)赋值算符左边的表达式的值;
(D)出现在赋值算符右边的表达式的值;
(E)二元算符左边表达式的值;
18. 下列为纯虚函数的正确声明的是( B )
(A)void virtual print()=0; (B)virtual void print()=0;
(C)virtual void print(){ }; (D)virtual void print();
19. 如果在类对象a的类中重载运算符“+”,则a+5的显示调用方式为( C )
(A)a.operator(5) (B)a->operator+(5);
(C)a.operator+(5) (D)5.operator+(a)
20.一个类如果有一个以上的基类就叫做( B )。
(A)循环继承 (B)单继承
(C)非法继承 (D)多继承
二、 多项选择题(从五个备选答案中选择2~5个正确答案,每小题2分,共10分)
1. 如果已定义了一个C++类CMyList并有以下语句:
CMyList list(3);
以下说法正确的是( AC )。
(A)该语句会创建一个CMyList类的一个对象;
(B)该语句会创建一个CMyList类的3个对象;
(C)必须为类CMyList定义一个构造函数;
(D)必须为类CMyList定义一个析构函数;
(E) 必须定义函数CMyList list(int);
2. 以下说法正确的是( ABCDE )。
(A)内联(inline)函数改善了函数调用的执行效率。
(B)类A的友元(friend)函数可以访问类A的私有成员。
(C)类A的友元(friend)类B可以访问类A的私有成员。
(D)类A的静态数据成员为类A的所有对象所共享。
(E)类A的静态成员函数没有传递this 指针作为参数。
3.类B从类A派生,则类B可以访问类A中的( AC )成员。
(A)public成员 (B)private成员 (C)protected成员
(D)数据成员 (E)函数成员
4. 面向对象的程序设计语言具有( ABE )等共同特性。
(A)封装性 (B)多态性 (C)简单性 (D)复杂性
(E)继承性
5. 现有一个程序如下:
#i nclude
class A
{ public:
void f(){ cout<< "A::f()"<
};
class B
{ public:
void f(){ cout<< "B:f()"<
void g(){ cout<< "B:g()"<
};
class C:public A, public B
{ public:
void g(){ cout<<"C::g()"<
void h()
{ cout<<"C::h()"<
f(); //语句1
}
};
void main()
{ C obj;
obj.f(); //语句2
obj.A::f(); //语句3
obj.B::f(); //语句4
obj.g(); //语句5
}
则编译时会产生错误的语句有( AB )。
(A)语句1 (B)语句2 (C)语句3
(D)语句4 (E)语句5
三、 判断分析题(正确的画上√,错误的画上×,每小题1分,共10分)
1.如果一个类的所有对象都共享某一个变量,则应当将该变量定义为该类的static成员。 ( √ )
2.语句“ typedef struct _MYSTRUC { int x; int y; double z; } MYSTRUC; ”是非法语句。 ( × )
3.语句“ int (*p)(int x, int y);”说明了p是一个指向整数的指针。 ( × )
4.Visual C++集成开发环境中,一个Workspace中只能创建一个Project。 ( × )
5.能访问一个类CMyClass中的private成员的可以是类CMyClass的成员函数,友元函数和友元类中的函数。 ( √ )
6. 所有的MFC应用程序都必须有一个且只有一个CWinApp对象。 ( √ )
7.C++中的多态性就是指在编译时,编译器对同一个函数调用,根据情况调用不同的实现代码。 ( × )
8.在for循环中,是先执行循环体后再判断循环条件。 ( × )
9.C++中,如果条件表达式值为-1, 则表示逻辑为假。 ( × )
10. 在C++中用new分配的内存空间,在不需要时一般用free将该空间释放。 ( × )
四、 填空题(每空2分,共20分)
1.以下函数完成求表达式 的值,请填空使之完成此功能。
float sum( float x )
{ float s=0.0;
int sign=1;
float t=1.0;
for(int i=1; i<=100; i++)
{
t=t*x;
s=s+-sign*i/(t+sign*i);
sign=-sign;
}
return s;
}
2.以下程序中实现类CSort, 完成对其成员p所指向的整数数组进行从小到大排序,该数组的元素个数由num表示,请填空完善该程序。
#i nclude
class CSort
{
int *p;
int num;
public:
void Order();
CSort(int *, int);
void Disp();
};
CSort::CSort(int *arry, int n)
:p(arry), num(n)
{ }
void CSort::Order() //函数Order原型
{ int m, tmp;
for(int i=0; i
{ m=i;
for(int j=i+1; j
{ if(p[j]
m=j;
}
if(m!=i)
{ tmp=p[i];
p[i]=p[m];
p[m]=tmp;
}
}
}
void CSort::Disp()
{ for(int i=0; i
cout<<<",";
cout<<
}
void main( )
{ static int a[]={10, -15, -3, 5, -4, 7,2};
CSort obj(a,2);
obj.Disp(); //应输出一行:10,-15,-3,5,-4,7,2
obj.Order(); //对数组排序
obj.Disp(); //应输出一行:-15,-4,-3,2,5,7,10
}
3.以下函数完成求两个数n1和n2的最大公约数。
#i nclude
int fac(int n1, int n2)
{ int tmp;
if( n1 < n2 )
{ tmp=n1;
n1=n2 ;
n2=tmp ;
}
while(n1%n2!=0)
{ tmp=n1%n2; n1=n2; n2=tmp;
}
return n2;
}
2005年华为招聘--C语言笔试试题
一、判断题(对的写T,错的写F并说明原因,每小题4分,共20分)
1、有数组定义int a[2][2]={{1},{2,3}};则a[0][1]的值为0。( )
2、int (*ptr) (),则ptr是一维数组的名字。( )
3、指针在任何情况下都可进行>,<,>=,<=,==运算。( )
4、switch(c) 语句中c可以是int ,long,char ,float ,unsigned int 类型。( )
5、#define print(x) printf("the no, "#x",is ")
二、填空题(共30分)
1、在windows下,写出运行结果,每空2分,共10分。
char str[ ]= "Hello";
char *p=str;
int n=10;
sizeof(str)=( )
sizeof(p)=( )
sizeof(n)=( )
void func(char str[100])
{ }
sizeof(str)=( )
2、void setmemory(char **p, int num)
{ *p=(char *) malloc(num);}
void test(void)
{ char *str=NULL;
getmemory(&str,100);
strcpy(str,"hello");
printf(str);
}
运行test函数有什么结果?( )10分
3、设int arr[]={6,7,8,9,10};
int *ptr=arr;
*(ptr++)+=123;
printf("%d,%d",*ptr,*(++ptr));
( ) 10分
二、编程题(第一小题20,第二小题30分)
1、 不使用库函数,编写函数int strcmp(char *source, char *dest)
相等返回0,不等返回-1;
2、 写一函数int fun(char *p)判断一字符串是否为回文,是返回1,不是返回0,出错返回-1
五、 阅读程序题(每个小题5分,共20分)
1.阅读以下程序,概括地写出程序的功能。
#i nclude
double Exp(double x)
{ double sum=1.0;
double term=x;
double i=1 ;
while (term>=1.0E-8)
{ sum+=term ;
i++;
term=term*x/i ;
}
return sum ;
}
void main()
{ double s;
s=Exp(1.0)+Exp(2.0);
cout.precision(8);
cout<<"s="<<
}
zz
2. 阅读程序,写出程序执行时输出结果。
#i nclude
const int SIZE=10;
class stack
{ char stck[SIZE];
int top;
public:
void init();
void push(char ch);
char pop();
};
void stack::init()
{ top=0; }
void stack::push(char ch)
{ if(top==SIZE)
{ cout<<"Stack is full.\n";
return ;
}
stck[top++]=ch;
}
char stack::pop()
{ if(top==0)
{ cout<<"Stack is empty.\n";
return 0;
}
return stck[--top];
}
void main()
{ stack s1, s2;
s1.init();
s2.init();
s1.push('a');
s1.push('b');
s1.push('c');
s2.push('x');
s2.push('y');
s2.push('z');
for(int i=0; i<3; i++)
cout<<"Pop s1:"<<
for(i=0; i<3; i++)
cout<<"Pop s2:"<<
}
程序结果:
Pop s1: c
Pop s1: b
Pop s1: a
Pop s2: z
Pop s2: y
Pop s2: z
3.阅读程序,写出程序运行时输出结果。
#i nclude
class Tdate
{ public:
Tdate();
Tdate(int d);
Tdate(int m, int d);
Tdate(int m, int d, int y);
protected:
int month;
int day;
int year;
};
Tdate::Tdate()
{ month=4;
day=15;
year=1995;
cout<<<"/" <<<"/" <<
}
Tdate::Tdate(int d)
{ month=4;
day=d;
year=1996;
cout<<<"/" <<<"/" <<
}
Tdate::Tdate(int m, int d)
{ month=m;
day=d;
year=1997;
cout<<<"/" <<<"/" <<
}
Tdate::Tdate(int m, int d, int y)
{ month=m;
day=d;
year=y;
cout<<<"/" <<<"/" <<
}
void main()
{ Tdate aday;
Tdate bday(10);
Tdate cday(2,12);
Tdate dday(1,2,1998);
}
运行结果:
4/15/1995
4/10/1996
2/12/1997
1/2/1998
4.阅读程序,写出程序运行时输出结果。
#i nclude
#i nclude
class shape
{ public:
shape(double x, double y):xCoord(x), yCoord(y){}
virtual double Area()const {return 0.0; }
protected:
double xCoord, yCoord;
};
class AA :public shape
{ public:
AA(double x, double y, double r): shape(x,y), rad(r){}
virtual double Area()const { return 3.0 * rad * rad; }
protected:
double rad;
};
class BB :public shape
{ public:
BB(double x1, double y1, double x2, double y2)
:shape(x1, y1), x2Coord(x2), y2Coord(y2){ }
virtual double Area()const;
protected:
double x2Coord, y2Coord;
};
double BB:Area()const
{ return fabs((xCoord-x2Coord)* (yCoord - y2Coord));
//库函数fabs(double t)求得t的绝对值
}
void fun(const shape& sp)
{ cout<<
}
void main()
{ AA aa(2.0, 5.0, 4.0);
fun(aa);
BB bb(2.0, 8.0, 12.0, 17.0);
fun(bb);
}
运行结果:
48
30
六、 编写程序题(每小题10分,共20分)
1.编写一个函数int Judge(int *pArray, int n),判断一个n×n二维整数数组pArray 是否为“魔方阵”,若是返回1,否则返回0。所谓魔方阵就是将1到n2的各个数字组成的方阵,它的每一行、每一列以及两个对角线上数字之和均相等。例如,3×3的中,A是魔方阵,而B不是魔方阵。然后在主程序中调用Judge函数判断数组A是否为魔方阵。
参考程序
#i nclude
int Judge(int *pArray, int n)
{ int s1, s2, s3,s4,sum=0;
int *p=pArray;
for(int i=1; i<= n*n; i++)
{ int Found=0; //为0,不在方阵中;
for(int j=0; j
if(p[j]==i)
{ Found=1; //为1,在方阵中
break;
}
if(Found==0) return 0; // 值为 i 的元素不在数组中,显然不是魔方阵
}
for( i=1; i<=n*n; i++)
sum=sum+i;
sum=sum / n; // 各行、各列、对角线元素应当得到的和
s3=0;
s4=0;
for( i=0; i
{ s1=0, s2=0;
p=pArray;
for(int j=0; j
{ s1=s1+p[i*n+j]; //第i行的元素和
s2=s2+p[j*n+i]; //第i列的元素和
}
if ( s1!=sum)
return 0;
if ( s2!=sum)
return 0;
s3=s3+pArray[i*n+i]; // 对角线一元素和
s4=s4+pArray[i*n+(n-1-i)]; // 对角线二元素和
}
if(s3!=sum)
return 0;
if(s4 != sum)
return 0;
return 1;
}
void main()
{ int Array[3][3]={{ 8, 1, 6},{ 3, 5, 7},{ 4, 9, 2}};
当 x 输入值为9999时,函数返回值为多少?
int fun ( unsigned int x )
{
int count = 0;
while(x)
{
x = x & (x-1);
count++;
}
return count;
}
答案:此函数是在计算 x 中含有1的个数,所以返回值为8。
if(Judge((int*)Array, 3))
cout<<"Yes, it's a magic array"<
else
cout<<"No, it isn't a magic array"<
}
/*********************************
* 两个超大数相乘算法
*********************************/
#i nclude
void main()
{
int a[30],b[30],c[60];
int i,j;
/* 给乘数和被乘数赋值,并把结果赋零 */
for (i=0;i<30;i++)
{
a[i]=i%10;
b[i]=i%10;
c[2*i]=0;
c[2*i+1]=0;
}
/* 给每位结果赋值,这里应该考虑清楚为什么这么写
还有这里的位的值的最大限度应该是-128 -- +127
所以就算是10*10也可以满足存进去一个char类型里 */
for(i=0;i<30;i++)
for(j=0;j<30;j++)
c[i+j]+=a[i]*b[j];
/* 这里把每个位>10的数进位和把余数重新赋值给这一位 */
for(i=0;i<59;i++)
{
c[i+1]+=c[i]/10;
c[i]=c[i]%10;
}
/* 打印出来 */
for(i=0;i<30;i++)
printf("%d",a[30-i-1]);
printf("\n");
for(i=0;i<30;i++)
printf("%d",b[30-i-1]);
printf("\n");
for(i=0;i<60;i++)
printf("%d",c[60-i-1]);
printf("\n");
}
posted @
2006-06-15 06:54 Jerry Cat 阅读(1007) |
评论 (0) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
读VC++内幕之体悟 - 03
作者: Jerry Cat
时间: 2006/06/14
链接: http://www.cppblog.com/jerysun0818/archive/2006/06/14/8526.html
3. 包含控件的窗口称为包容器:)
posted @
2006-06-14 02:41 Jerry Cat 阅读(606) |
评论 (1) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
读VC++内幕之体悟 - 02
作者: Jerry Cat
时间: 2006/06/10
链接:
http://www.cppblog.com/jerysun0818/archive/2006/06/10/8391.html
2. 普通控件给对话框发送通知命令消息。如果希望控件执行一个动作,可以调用C++控件类的成员函数,实际上该函数也是给控件发送一个Windows消息。普通控件的另一种情况为定制控件,它由程序员创建,其行为与普通控件一样,也给父窗口发送WM_COMMAND消息,并接受用户定义的消息。
posted @
2006-06-10 19:09 Jerry Cat 阅读(1162) |
评论 (4) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
读VC++内幕之体悟 - 01
作者: Jerry Cat
时间: 2006/06/10
链接:
http://www.cppblog.com/jerysun0818/archive/2006/06/10/8365.html
第八章 使用ActiveX控件
1. VBX即VB控件,大部分用C语言实现,其标准建立在16位段式结构基础上,不适合32位环境。ActiveX控件之前常称为OLE控件,即OCX,它基于Microsoft的COM技术,可以代替以前的VBX。ActiveX控件可以用C++语言实现,还可以有MFC库的支持或者ActiveX模板库ATL的支持。
posted @
2006-06-10 02:47 Jerry Cat 阅读(528) |
评论 (0) |
编辑 收藏
/********************************************\
| 欢迎转载, 但请保留作者姓名和原文链接, 祝您进步并共勉! |
\********************************************/
常用C函的汇编实现(注: AT&T汇编)
作者: Jerry Cat
时间: 2006/06/07
链接: http://www.cppblog.com/jerysun0818/archive/2006/06/07/8253.html
//字符串拷贝
void strcpy(const char *src, char *dest)
{
char ch;
__asm
{
loop:
ldrb ch, [src], #1
strb ch, [dest], #1
cmp ch, #0
bne loop
}
}
//int转字符串
void I2S(int isource, char* desstr)
{
char ch, i=sizeof(isource)*2, temp = 0, is =0;
if(isource==0)
{
*desstr = '0';
*(desstr+1)=0;
return;
}
if(isource<0)
{
*desstr='-';
desstr++;
isource=-isource;
}
desstr--;
__asm
{
loop:
mov ch, #'0'
add ch, ch, isource, lsr#28
mov isource, isource, lsl#4
sub temp,ch, #'0'
orr is, temp, is
cmp is, #0
beq loop1
mov is, #0xFF
strb ch, [desstr,#1]!
loop1:
sub i, i, #1
cmp i, #0
bne loop
}
*(++desstr) = 0;
}
posted @
2006-06-07 18:11 Jerry Cat 阅读(551) |
评论 (0) |
编辑 收藏
Linux下的mount命令
‘fstab’的用处
‘/etc/fstab’是‘mount’命令重要的配置文件。您可以‘root’身份通过‘diskdrake’(Mandrake Linux 控制中心 - 载入点),或用编辑器修改这个文件。
‘/etc/fstab’有几个用处:
*
决定开机时自动挂载哪些介质;
*
指定每个介质挂载时的可选项、载入点;
*
系统用来挂载几个虚拟文件系统。
开机时挂载介质
默认下,开机时‘fstab’中列出的所有介质都将被挂载。如果其中某个介质出了问题,‘mount’会显示出错信息,然后继续下一条目。请注意,这对于网络介质,比如 NFS 或 SMB 共享,也有效。如果想取消开机时的自动挂载,您得在‘/etc/fstab’给相应条目提供‘noauto’可选项。
指定挂载的可选项和载入点
如果阅读了上一篇,您就已经知道‘mount’命令有两种可选项:常规可选项、文件系统的特殊可选项。
特殊可选项针对特定的文件系统。如果您在‘fstab’中给某个条目提供了特殊可选项(比如用于 Windows FAT 文件系统的 umask=0),只有相应文件系统的介质才可以通过该条目被挂载。如果您想挂载其他文件系统的介质,要么再设置那个可选项,要么创建单独的条目。
指定载入点有个好处,以后可以省略挂载/卸载时的设备文件名。
mount mount_point
就将在‘/etc/fstab’寻找相应载入点的条目,并按照预先设定完成挂载。
虚拟文件系统
根据系统配置,‘fstab’含有一些虚拟文件系统的条目,这些条目没有列出相应的设备文件。不管如何,请不要动这些条目!
*
‘proc’挂载的是“进程树”(process tree) ,提供一些系统的信息;
*
‘pts’启用“伪终端支持”(pseudo terminal support),这是 Unix 的兼容功能;
*
‘shm’开启“POSIX shared memory”,仅有一小部分程序会用到,不会有负面影响。
* section index * top
* ‘fstab’中条目的介绍
本地的固定系统分区
您看到,‘/etc/fstab’中已有几个系统分区(如‘/’)的条目。 就拿这个为例:
/dev/hda3 / ext2 defaults 1 1
语法为:
/dev/hda3 / ext2 defaults 1 1
device_name mount_point file_system options 'dumpe2fs' 'fsck'
这儿有介质(硬盘的分区)的设备名、载入点(/)、文件系统(ext2)。
defaults 包括若干‘mount’的可选项:rw 可读写、 suid 允许设置用户标识符(set-user-identifier)、exec 允许运行程序、auto 开机时自动挂载、nouser 只有‘root’有权卸载、async 允许磁盘延迟存取(caching) 。
最后两个可选项是文件系统工具‘dumpe2fs’、‘fsck’用到的标志。
‘dump’域可设成‘1’(启用)和‘0’(禁用)。由于备份 ext2/ext3 文件系统的程序要用到‘dump’,因此文件系统为 ext2/ext3 的条目要设成‘1’,所有其他条目都为‘0’。
‘fsck’域可设成‘0’(不检查文件系统)、‘1’(先检查)、‘2’(检查)。所有非 Linux 文件系统、可移动的和网络介质、虚拟文件系统都要设成‘0’;由于根分区要先被挂载(接着检查),‘/’条目要设成‘1’;而所有其他不可移动的及 Linux 文件系统的本地介质都要设成‘2’。
可移动介质
可移动的数据存储体,比如光盘,需要不同的可选项(如果您没有用‘supermount’):
/dev/cdrom /mnt/cdrom auto user,noauto,exec,ro 0 0
user 允许用户进行挂载、卸载,否则只有‘root’有此权利。请注意,这个可选项会关闭 exec ,您得在 user 可选项后指定 exec ,以使在用户可在挂载的介质中运行程序,这些和上例是一致的。noauto 指开机时不挂载该介质,一些可移动介质就需要这个可选项。 ro 挂载的介质“只读”(read-only) 。‘dump’、‘fsck’都被关闭了。
虽然光盘没有设成自动挂载,但由于‘/etc/fstab’中的这一条目,使得接下来的挂载变得很简单:放入光盘,然后输入:
mount /mnt/cdrom
‘mount’将在‘/etc/fstab’查找和这个载入点联系的那个条目,并采用列出的可选项,因此,这样您就可以省些输入。在 KDE 中,放入光盘后,您只要用鼠标点击那个光盘图标,就能自动运行‘mount’命令。
挂载后的光盘将锁住光驱,要弹出光盘的话,您得先卸载:umount /mnt/cdrom 。
要配置其他可移动介质,您要知道他们的设备文件名,可以看一下 devices.txt 或阅读 有关可移动存储设备的文章 。
本地的固定非系统分区
MS-Windows 分区的条目看起来是什么样呢?假设是在第一块 IDE 硬盘的第二个分区,而且您想要自动挂载:
/dev/hda2 /mnt/win_d vfat umask=0,quiet 0 0
如果是 Windows NT 的 NTFS 分区,就要把‘vfat’换成‘ntfs’。请注意,目前在 Linux 下,只能对 NTFS 的文件系统读,而不能写。
‘umask’可选项将关闭权限监查,没有这项的话,就只有‘root’能存取该设备上的文件。当向这个介质移动文件时,由于无法设置权限,会显示恼人的出错信息,用‘quiet’就可以不显示这些无关紧要的信息。
如果 Windows 分区中的文件名看起来很奇怪,那您就得再添两个可选项:‘iocharset’和‘codepage’。默认下,‘mount’用‘iso8859-1’字符集和 codepage 437 。修改后,可以实现字符转换。man charsets 列出了可用的字符集,将 codepage 设成 850 (codepage=850)就可以解决一些问题。
(注:要显示简体中文的话,得添上 charsets=gb2312,codepage=936)
一个分区挂载在一个已存在的目录上,这个目录可以不为空,但挂载后这个目录下以前的内容将不可用。
对于其他操作系统建立的文件系统的挂载也是这样。但是需要理解的是:光盘、软盘、其他操作系统使用的文件系统的格式与linux使用的文件系统格式是不一样的。光盘是ISO9660;软盘是fat16或ext2;windows NT是fat16、NTFS;windows98是fat16、fat32;windows2000和windowsXP是fat16、fat32、 NTFS。挂载前要了解linux是否支持所要挂载的文件系统格式。
挂载时使用mount命令:
格式:mount [-参数] [设备名称] [挂载点]
其中常用的参数有
-t<文件系统类型> 指定设备的文件系统类型,常见的有:
minix linux最早使用的文件系统
ext2 linux目前常用的文件系统
msdos MS-DOS的fat,就是fat16
vfat windows98常用的fat32
nfs 网络文件系统
iso9660 CD-ROM光盘标准文件系统
ntfs windows NT 2000的文件系统
hpfs OS/2文件系统
auto 自动检测文件系统
-o<选项> 指定挂载文件系统时的选项。有些也可用在/etc/fstab中。常用的有
codepage=XXX 代码页
iocharset=XXX 字符集
ro 以只读方式挂载
rw 以读写方式挂载
nouser 使一般用户无法挂载
user 可以让一般用户挂载设备
提醒一下,mount命令没有建立挂载点的功能,因此你应该确保执行mount命令时,挂载点已经存在。(不懂?说白了点就是你要把文件系统挂载到哪,首先要先建上个目录。这样OK?)
例子:windows98装在hda1分区,同时计算机上还有软盘和光盘需要挂载。
# mk /mnt/winc
# mk /mnt/floppy
# mk /mnt/cdrom
# mount -t vfat /dev/hda1 /mnt/winc
# mount -t msdos /dev/fd0 /mnt/floppy
# mount -t iso9660 /dev/cdrom /mnt/cdrom
现在就可以进入/mnt/winc等目录读写这些文件系统了。
要保证最后两行的命令不出错,要确保软驱和光驱里有盘。(要是硬盘的磁盘片也可以经常随时更换的话,我想就不会犯这样的错误了 :-> )
如果你的windows98目录里有中文文件名,使用上面的命令挂载后,显示的是一堆乱码。这就要用到 -o 参数里的codepage iocharset选项。codepage指定文件系统的代码页,简体中文中文代码是936;iocharset指定字符集,简体中文一般用cp936或 gb2312。
当挂载的文件系统linux不支持时,mount一定报错,如windows2000的ntfs文件系统。可以重新编译linux内核以获得对该文件系统的支持。关于重新编译linux内核,就不在这里说了。
自动挂载
每次开机访问windows分区都要运行mount命令显然太烦琐,为什么访问其他的linux分区不用使用mount命令呢?
其实,每次开机时,linux自动将需要挂载的linux分区挂载上了。那么我们是不是可以设定让linux在启动的时候也挂载我们希望挂载的分区,如windows分区,以实现文件系统的自动挂载呢?
这是完全可以的。在/etc目录下有个fstab文件,它里面列出了linux开机时自动挂载的文件系统的列表。我的/etc/fstab文件如下:
/dev/hda2 / ext3 defaults 1 1
/dev/hda1 /boot ext3 defaults 1 2
none /dev/pts devpts gid=5,mode=620 0 0
none /proc proc defaults 0 0
none /dev/shm tmpfs defaults 0 0
/dev/hda3 swap swap defaults 0 0
/dev/cdrom /mnt/cdrom iso9660 noauto,codepage=936,iocharset=gb2312 0 0
/dev/fd0 /mnt/floppy auto noauto,owner,kudzu 0 0
/dev/hdb1 /mnt/winc vfat defaults,codepage=936,iocharset=cp936 0 0
/dev/hda5 /mnt/wind vfat defaults,codepage=936,iocharset=cp936 0 0
在/etc/fstab文件里,第一列是挂载的文件系统的设备名,第二列是挂载点,第三列是挂载的文件系统类型,第四列是挂载的选项,选项间用逗号分隔。第五六列不知道是什么意思,还望高手指点。
在最后两行是我手工添加的windows下的C;D盘,加了codepage=936和iocharset=cp936参数以支持中文文件名。参数defaults实际上包含了一组默认参数:
rw 以可读写模式挂载
suid 开启用户ID和群组ID设置位
dev 可解读文件系统上的字符或区块设备
exec 可执行二进制文件
auto 自动挂载
nouser 使一般用户无法挂载
async 以非同步方式执行文件系统的输入输出操作
大家可以看到在这个列表里,光驱和软驱是不自动挂载的,参数设置为noauto。(如果你非要设成自动挂载,你要确保每次开机时你的光驱和软驱里都要有盘,呵呵。)
posted @
2006-06-06 00:36 Jerry Cat 阅读(542) |
评论 (0) |
编辑 收藏
Windows调试命令 NTSD
http://www.cppblog.com/jerysun0818/archive/2006/06/06/8193.html
问
:
怎么才能关掉一个用任务管理器关不了的进程?我前段时间发现我的机子里多了一个进程,只要开机就在,我用任务管理器却怎么关也关不了
答
1:
杀进程很容易,随便找个工具都行。比如
IceSword
。关键是找到这个进程的启动方式,不然下次重启它又出来了。顺便教大家一招狠的。其实用
Windows
自带的工具就能杀大部分进程:
c:\>ntsd -c q -p PID
只有
System
、
SMSS.EXE
和
CSRSS.EXE
不能杀。前两个是纯内核态的,最后那个是
Win32
子系统,
ntsd
本身需要它。
ntsd
从
2000
开始就是系统自带的用户态调试工具。被调试器附着
(attach)
的进程会随调试器一起退出,所以可以用来在命令行下终止进程。使用
ntsd
自动就获得了
debug
权限,从而能杀掉大部分的进程。
ntsd
会新开一个调试窗口,本来在纯命令行下无法控制,但如果只是简单的命令,比如退出
(q)
,用
-c
参数从命令行传递就行了。
NtsdNtsd
按照惯例也向软件开发人员提供。只有系统开发人员使用此命令。有关详细信息,请参阅
NTSD
中所附的帮助文件。用法
:
开个
cmd.exe
窗口,输入:
ntsd -c q -p PID
把最后那个
PID
,改成你要终止的进程的
ID
。如果你不知道进程的
ID
,任务管理器-进程选项卡-
>
查看-
>
选择列-
>
勾上
"PID
(进程标识符)
"
,然后就能看见了。
答
2
:
xp
下还有两个好东东
tasklist
和
tskill
。
tasklist
能列出所有的进程,和相应的信息。
tskill
能查杀进程,语法很简单:
tskill
程序名!
posted @
2006-06-06 00:11 Jerry Cat 阅读(261) |
评论 (0) |
编辑 收藏
[转]Track'em Down...
http://www.cppblog.com/jerysun0818/archive/2006/06/04/8153.html
P.S. 很多朋友都抱怨说STL出问题的时候debug很难,编译期错误算是轻的,大不了一串串令人头晕的出错信息,至少还能双击定位到错误行。而神秘的运行期崩溃才是真正令人头大的问题。下面就是一个比较典型的、五脏俱全的运行期崩溃事件,从几行简简单单的代码,似乎根本不可能崩溃,一直到最后揪出隐藏在背后的机制。其中的思维分析过程是怎样的呢?希望对一些朋友有点帮助。标题起作”Track’em Down”一方面是暗指整个分析跟踪的过程,而是最后所揭露出的机制的确是个tracking机制:-)
很久没写blog了,一来是诸事缠身,二来也是实在不像以前那么有热情坐下来好好写篇技术文章了。很多时候只是做个旁观者,四处潜水而已。昨天又跟老婆闹了矛盾,心里郁闷,于是给自己一个理由四处乱逛,跑到许久未去的cpper(
http://www.cpper.com/c/
)上,cpper还是一如既往的冷清,看来一个论坛要想火起来光靠技术是不行的,cpper(前身为allaboutprogram)圈子里有一批技术很牛的朋友,也许是太牛了,这两年都开始一个个牛得没影儿了。二来cpper不像CSDN这样有一个门户,主页上有乱七八糟的好玩信息,新闻,八卦,等等不一而足。国内C++社群的大部分朋友想必是不知道cpper的,尤其是初学者。实在是个不小的遗憾…等等…似乎扯远了,刚才说到逛到cpper上,看到tomato的一个帖子,提到一段行为怪异的代码,行为是崩溃,而且是在l析构的时候崩溃:
#include <list>
int main(int argc, char* argv[])
{
std::list<int> l;
std::list<int>::iterator* it1 = new std::list<int>::iterator(l.begin());
memset(it1, 1, sizeof(std::list<int>::iterator));
l.push_back(1);
l.begin();
return0;
以上是原贴里的代码,我就不简化了。大伙看着办吧,呵呵。
乍看上去这段代码似乎不应该有什么问题(至少不会崩溃)。最可疑的当然是那行memset,但memset只是把it1指向的一个new出来的iterator给corrupt掉了,况且这个it1也没被delete,所以也就不会因析构而崩溃了(虽然有内存泄漏,但tomato说的是崩溃),另外it1跟l看上去是井水不犯河水。所以怎么看不像有问题的样子。
以上是眼睁睁瞪着代码看出来的结果,然而当我把代码塞到IDE里面,编译运行之后却发现果然如tomato所说,在list析构时程序崩溃了(很凑巧我跟tomato用的IDE都是VC8,这是一个关键条件)。对于这类问题,一般我是先黑箱再白箱。这么短的程序,要出问题肯定出在memset那行,逻辑上可以大致这么说:“由于memset把it1的内存corrupt掉了,结果导致l在析构的时候崩溃。”听到自己下这么个结论我都觉得有点哭笑不得,这压根儿风马牛不相及嘛,it1被corrupt与l析构崩溃有屁关系?!it1只不过是个指向l的迭代器,说穿了就是裹着个指向l中的某个node的指针而已,它的死活怎么会影响到l的析构呢?
但福尔摩斯大致说过,去掉那些绝对不可能的,剩下的就是可能。这里这是唯一的可能,只不过问题是,这个逻辑还不够细致,需要丰满起来。进一步,要想使得l在析构的时候崩溃,这个memset肯定以某种方式影响到了l。又因为memset直接影响的是it1,那么必然是it1和l的某种联系导致memset间接对l产生了影响。于是再来看,it1和l有什么联系呢?得!这下看出来了,it1在构造的时候是通过l.begin()拷贝构造的,哈!问题似乎有点眉目了,但到此我还是想不通,根据常识,iterator的拷贝构造会产生出一个与源iterator不相干的副本,对这个副本干什么事怎么会影响到源呢?更别提影响到源iterator所指向的容器了。开玩笑!甭管他,既然已经肯定下来这是唯一的可能,那么推论只能是:背后还隐藏着什么不为人之的东西。于是进一步注释掉“(l.begin())”,使it1变成缺省构造的迭代器,果然,不再崩溃了。于是,现在可以肯定作出的结论是:“… it1 = new std::list<int>::iterator(l.begin());这行代码使得it1所指向的迭代器与l发生了某些关系(当然,不是it1指向l这样的白痴关系)。很显然,这个关系的建立点不可能是在l.begin(),因为这里还没有涉及到it1,所以唯一的可能就是,这个关系是在std::list<int>::iterator的拷贝构造函数阶段发生的。这次拷贝构造以l.begin()为参数,而l.begin()推测起来应当带了l的信息,因而it1就利用这个机会与l建立起了“某种关系”。OK,到目前为止这个“某种关系”只是一个模糊的概念,我们并不明确知道究竟是什么关系,竟然导致list析构会失败。看来是时候进入白箱了,F5,break,看断在什么地方了,说实话P.J Plauger写的STL代码至少在外观上不像SGI的那么亲近人,nevermind,我看到了我想看到的信息——一个名为_HAS_ITERATOR_DEBUGGING的宏,哈,想起来了,这一版的STL是带有iterator debugging功能的,所以当然能够发现iterator被corrupt掉的情况。疑问来了,怎么发现的呢?直接的答案是,肯定有某种追踪机制,而且根据前面的推理,由于是l在析构的时候发生的崩溃(it1并不析构,因为并没有对它进行delete),所以l在析构的时候必然能以某种方式访问到it1并发现它被corrupt掉的事实,再推广之,l肯定能够访问到所有指向它的迭代器(这才叫iterator debugging嘛),而it1在构建的时候的确是拷贝构造l.begin()来的,也就是说指向l,所以l必然应当知道(跟踪)它(it1)的存在,又因为接下来的memset是个相当low level/native的操作,所以l对此肯定不知情,还以为it1一直好端端的指着它的begin()处呢,最后当它一个个察访指向它内部的迭代器,当查访到it1时就发现it1被corrupt掉了,于是崩溃。呼~一切到这里似乎都串起来了。剩下的就是去发现list<int>::iterator的拷贝构造函数究竟在背后捣了什么鬼才使得l最终能够访问到it1的,在开始之前我就设想一个场景:肯定是l里面有一个链表,把所有指向它内部的迭代器串起来了,这样它就能够逐一检查这些迭代器,看看比如说它们是否越界之类的。而it1在拷贝构造的时候由于是以l.begin()为参数的,l.begin()肯定带有l的信息,说白了,指向l的指针,或l的引用,于是it1的拷贝构造函数就可以把自身链入l内部的链表中去。想到这里似乎情况十分明朗了,剩下的就是跟踪进去验证一下了…但是等一下…刚才说到,程序的现象是…崩溃!如果是在l对它的迭代器check的时候发现错误,大脑没毛病的库设计者肯定会抛出一个异常吧,不会是崩溃的症状…what the hell。反正我已经知道问题的核心在list<int>::iterator的拷贝构造函数那里,那是唯一同时拥有it1与l信息的地方。于是F11到list<int>::iterator的拷贝构造处,即“… it1 = new std::list<int>::iterator(l.begin());”这行代码处,郁闷的是,调试器在这里一跳而过,似乎它拥有的是一个trivial的拷贝构造函数(从后来的结果来看这似乎是VC2005的一个小问题),而且我”go to disassembly”居然也就看到聊聊几行代码,除了一个对list<int>::begin()的调用之外就没有其它调用了,傻眼了,似乎真的是个trivial的copy ctor?如果真是这样的话,就不可能在这里建立起it1跟l之间的联系了,因为trivial的copy constructing只会是把成员按位复制一下,没有其它代码,不涉及函数调用,怎么可能会有机会干其它事情呢?而后面又没有任何地方同时涉及it1跟l的,那又怎么会最终导致l析构崩溃呢?简单的推理,反推上去,既然l最终析构崩溃了,那么这个逻辑的某一环肯定错了,最可能的就是,这并非trivial copy ctor,调试器欺骗了我的眼睛,背后肯定调用了某个拷贝构造函数,然后干了些勾当。于是静态跟踪派上了用场,幸好VS2005的intelli-sense非常强大,两次“go to definition”就置身于了list<int>::iterator的定义里面,刚才不是说关键就在list<int>::iterator的拷贝构造函数里面吗?于是浏览一下list<int>::iterator的定义,VC带的STL的代码真难看啊,好在这个类比较短,很遗憾,没有拷贝构造函数,只有构造函数,不过碰巧,在其中一个由_HAS_ITERATOR_DEBUGGING条件控制的构造函数里面发现了一行宝贵的代码:this->_Adopt(_Plist);,如果没猜错,这肯定是把这个迭代器自身链到它所指向的容器内的跟踪链表中去的。_Plist(该构造函数的第二个参数)是什么?跟踪到l.begin()调用里面就会发现,喂给list<int>::iterator的构造函数的第二个参数(也就是_Plist)是this,呵呵,看来this->_Adopt(_Plist)实际上就是把this(迭代器)“收养(adopt)”给_Plist啊。没猜错,果然是这样的跟踪机制。那么,照理说list<int>::iterator的拷贝构造函数也应该有相应的动作才对啊,不然拷贝构造出的新的iterator就会没人“收养”了(而我们的跟踪机制是应当跟踪到每个“在世”的iterator的,否则就没意义了)。刚才提到,list<int>::iterator本身没有拷贝构造函数,那么只有一种可能,要么其成员具有non-trivial的拷贝构造函数,要么在基类里面。实际上list<int>::iterator只有一个成员,一如我们意料之中的,指向list的node的ptr。所以秘密肯定在基类中,顺着基类一路找下去,_Const_iterator->_Bidit->_Iterator_base。说实话到_Bidit差点放弃,因为我以为_Bidit里面肯定就是一些typedef,就像unary_function那样。事实却不是这样,下面还隐藏了一层_Iterator_base,而这个_Iterator_base就是一切秘密所在了。它是有拷贝构造函数的,代码我就不列了,如果你跟踪到这里,真相也就大白了,简单的来说,它根据源iterator找到其所指的容器,然后取出该容器里面的用于跟踪迭代器的链表头指针,然后把自身(this)链到这个链表里面去。由于一个iterator诞生的方式一共就两种,一种是从某个容器诞生,这时是调用的它的一般构造函数,容器会把自身的指针当作参数传递给这个iterator,后者通过这个容器指针来将其自身链接到容器的跟踪链表中。第二个诞生方式就是这里说的,拷贝构造,拷贝构造时会将其自身链到源iterator所指向的容器内的跟踪链表中去。反正就是,一切现有的iterator都会恰当被它所指的容器跟踪着。
那么是时候揭穿谜底了吧,为什么memset会闯下这么大的祸?因为一个iterator要被链到链表里面,它肯定有next指针,这样才能链成一个链表嘛。而memset粗暴地将这个next指针给重置了(事实上它把it1指向的迭代器整个给memset了,当然包括里面的next指针),这里,next指针被重置为了1(如果memset成0就不会崩溃了,原因很简单,想想看),显然,这就指向了一块无意义的内存。于是,l在析构的时候,试图遍历并check它所跟踪的iterator链表,随着它通过next指针一节节跳转,当到了我们的it1的时候,由于其next指针的值是1,所以试图再跳(next)的时候就非法内存访问了!崩溃!所以,这里的问题并不在于最后的check失败了,而是在于迭代器链表被corrupt掉了,才导致的崩溃。这就解除了前面的关于未发生异常的疑惑。事情到此就大致结束了。
当然,这里还隐藏了很多的细节。例如最后l析构时并不是要去“check”每个迭代器。另外这个iterator tracking的机制还是很有代表性的,boost::signal里面就用了如出一辙的手法,这个手法充分显示出了C++的强大和灵活。原来看上去毫不起眼的构造函数在背后还能做那么多工作。这里就不方方面面总结这一技术了,一是困了,二是这不是这篇blog的初衷,写这篇blog一是为了平静一下郁闷的心情,二是为了显示一个从现象开始分析推理问题,最终接近答案的过程。程序员的大部分时间是在debug,这篇blog其实介绍的就相当于一个debug的思维过程,或许它并不是最好的,但希望你能够在其中发现一些有用的东西。
P.S. 看上去啰啰嗦嗦一大通,实际上在脑子里转来转去是一瞬间的工夫,加上跟踪(静态/动态)也就十来分钟,而写这篇blog倒花了我四十多分钟(这也是越来越不写blog的原因吧),人类自然语言的表达力某些时候是十分啰唆冗余的…
posted @
2006-06-04 19:52 Jerry Cat 阅读(311) |
评论 (0) |
编辑 收藏