c++基础知识点六

 

例题:Char szNum[]=”123456789”, int n=*(short*)(szNum+4)- *(short*)(szNum); 则n= ___1028_____ ;

 

这里(short*)(szNum)是指向12 为内存单元为0000 0001 0000 0010   即两个内存单元存储一个数,(short*)(szNum+4)是指向56,内存中存放为 0000 0101 0000 0110 即两个内存单元存储一个数

这里在char类型中,所以1字节看成一个数,所以12中为0000 0001 0000 0010但是非常注意的是强转成short的时候要把2个字节看成一个数,因此0000 0001 0000 0010在short里面是258,自己做的时候以为56-12=44,不对的,没弄明白强转成另一个类型的时候,不仅仅是字节变宽了,而且加减运算也变了

 

为什么c++的效率比java要快?

摘自https://www.zhihu.com/question/51284083

 

C++STL中map,set的底层实现全是用的红黑树,java,C#等语言同样如此。map,set底层都提供了排序功能,且查找速度快。

作用:红黑树实际上是AVL(平衡二叉搜索树)的一种变形,用它来存储有序的数据, 它可以在O(log n)时间内做查找,插入和删除。所以作用是用了红黑树插入删除效率非常之高。红黑树的基本操作是添加、删除。通过左旋和右旋让添加或删除节点后的红黑树仍然是一棵红黑树,也即通过旋转仍然是二叉排序树

 

例题、要增加一个类到某种类型的隐式转换可通过重载赋值操作符实现


例题、C++64位编译环境下,sizeof(int)=___4_____,sizeof[int *]=____8______

 

局部变量中的static变量和普通变量的区别:
相同点:

作用域相同,都是局部于当前所在的函数体内。
不同点:

1)前者是内部连接,后者没有连接,因为普通局部变量是位于堆栈中,连接器不知道它们;
2)存储区域不同:前者存放于静态存储区,后者存放于栈中;
3)生命期不同:前者是整个程序的生命期,后者是所在函数的生命期(即随它所在的函数调用的结束而结束;
4)初始化:前者只初始化一次;后者则是在每次调用函数时都要重新创建它的存储空间,重新初始化一次。

 

线程死锁发生的原因?要防止死锁在多线程编程中我们要注意什么?

原因:

多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

比如2个人一起吃饭但是只有一双筷子。一个拿了左筷子,一人拿了右筷子,2个人都同时占用一个资源,等待另一个资源,这个时候甲在等待乙吃完并释放它占有的筷子,同理,乙也在等待甲吃完并释放它占有的筷子,这样就陷入了一个死循环,谁也无法继续吃饭。。。

 

避免死锁的技术:

  1. 加锁顺序(线程按照一定的顺序加锁)
  2. 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
  3. 死锁检测

 

什么叫哈希算法?有什么应用?

 

先说优势:数据量非常庞大,查找大字符串的时候,我们可以将大字符串压缩成一个数字(key),在数组中进行查找这个数组,时间复杂度为O(1)

哈希表,也叫散列表,是根据key而直接进行访问的数据结构。映射函数叫做散列函数,存放记录的数组叫做散列表。

数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表,哈希表有多种不同的实现方法,我接下来解释的是最常用的一种方法——拉链法,我们可以理解为“链表的数组”,如图:

左边很明显是个数组,数组的每个成员包括一个指针,指向一个链表的头

 

元素特征转变为数组下标的方法就是散列法。散列法当然不止一种,下面列出三种比较常用的:

1,除法散列法 

最直观的一种,上图使用的就是这种散列法,公式:  index = value % 16 

 

AVL树查找、插入和删除在平均和最坏情况下都是O(log n)

 

 

例题:在几进制下等式13*16=244成立?

自己犯错:假设所求为X进制,那么13就是10进制,转成X进制,十位是13/X,  个位为13%X,

其实这里的13是X进制下的13,因此,要把它转成10进制的再说,13转成10进制为13=1*X的一次方+3*X的零次方,16,244同理

因此X进制下的13*16=244转成十进制下的(1*X的一次方+3*X的零次方)*(1*X一次方+6*X的零次方)=2*X的二次方+4成X的一次方+4*X的零次方

得到一元二次方程解得X为7,所以为7进制

 

要获得最有效率的*2,*4,*8和/2,/4,/8等运算是通过位运算<<, >>那么unsigned short表示的无符号short类型,没有负数只表示正的部分,所以是正的16位,如果

 

unsigned short k=0;  
k-=1;

那么k为65535

 

struct的sizeof计算已经讲过,共用体union的sizeof是最大变量成员的整数倍;

 

 

union foo{
  int i;
  char c;
  double k;
  };
sizeof(foo);   //double最长占用8字节,所以union foo大小为8字节
union A  
{
    int a[5];       //20
    short b;     //2
    double c;    //8
    char p2;     //1
};

 sizeof(A) ;  //24     而不是20 ,因为是最大double的整数倍

 

按位取反运算符(~)
按位取反运算是单目运算,用来求一个位串信息按位的反,即哪些为0的位,结果是1,而哪些为1的位,结果是0。例如, ~7的结果为0xfff8。

 

 

#include <filename.h> 和#include “filename.h”有什么区别?

答:对于#include <filename.h> ,编译器从标准库路径开始搜索filename.h 对于#include “filename.h”,编译器从用户的工作路径开始搜索filename.h

 

成员对象:一个类中包含了其他类的对象。

 

 

 

class Date{  
...
};  
class Time{
 Date d1 , d2;
}; 
 //Time的成员d1和d2是Date的对象,那么d1和d2就是成员对象。

 

 

一个类有基类、内部有一个其他类的成员对象,构造函数的执行顺序是怎样的。
答:先执行基类的(如果基类当中有虚基类,要先执行虚基类的,其他基类则按照声明派生类时的顺序依次执行),再执行成员对象的,最后执行自己的。

为什么先执行成员对象的然后执行自己的呢?


在C++ 程序中调用被C 编译器编译后的函数,为什么要加extern “C”? 
答:C++语言支持函数重载,C 语言不支持函数重载。函数被C++编译后在库中的名字与C 语言的不同。假设某个函数的原型为: void foo(int x, int y); 该函数被C 编译器编译后在库中的名字为_foo , 而C++ 编译器则会产生像_foo_int_int 之类的名字。 
C++提供了C 连接交换指定符号extern“C”来解决名字匹配问题

 

 

 

11.如何判断一段程序是由C 编译程序还是由C++编译程序编译的? 答案: 

#ifdef __cplusplus cout<<"c++"; 
#else 
cout<<"c"; 
#endif

 

在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的:

指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。(这里是在说实参指针本身的地址值不会变)

而在引用传递过程中,被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量,那就得使用指向指针的指针,或者指针引用。

 

如果输入参数是以值传递的话,最好使用引用传递代替,因为引用传递省去了临时对象的构造和析构 

 

 

c++的函数返回注意的问题:

1 返回局部变量没问题 

返回指向局部变量的指针才有问题, 函数退栈之后,局部变量消失, 指针将指向未知区域,所以出现问题。

 

int* test(int b)
{
    int a=0;//局部变量
    a=b; //行参复制给a
    int * p=&a ; //定义一个int* 的指针,把变量a的地址给p
    return p; //返回p。这个p是不是局部变量的指针?我觉得是的
}
void test2()
{
    int a[10];
    for (int i = 0; i < 10; i++)
    {
        a[i] = i;
    }
}
int *result=test(5);
printf("%d\n", *result); //此时是5
test2();
printf("%d\n", *result); //此时是一个错误的值

返回的是个栈上的指针。一调用其他函数。确实指向了一个未知的数

 

返回局部变量的引用也是绝对不可以的引用只是变量的一个别名,变量本体都不存在了,引用当然也没有任何意义

还有,如果是堆空间,可以返回,即在函数中用new申请的空间,是可以返回的

但是一般的情况下,好的风格是: 
尽量在同一个作用域内成对使用new   和delete,(也即不要返回堆空间),因为如果不是这样,会是你的函数的接口变的不够灵活, 试想每个调用你的函数的人还需要记得去delete掉你在函数中new的堆空间, 否则就会造成内存泄露。

永远不要从函数中返回局部自动变量的地址。如果你真的需要这样操作。你可以在函数的参数表中传入一个指针变量然后将需要写入的数据写入到该指针变量指向的地址。由于该指针指向的变量,作用域在函数体 之外。因此不会在函数结束结束时被回收。

 

 

 

#include <stdio.h>    
char *returnStr()   
{   
    char *p="hello world!";   
    return p;   
}   
int main()   
{   
    char *str;   
    str=returnStr();   
    printf("%s\n", str);   
    return 0;   
}  

这个没有任何问题,因为"hello world!"是一个字符串常量,存放在只读数据段,把该字符串常量存放的只读数据段的首地址赋值给了指针,所以returnStr函数退出时,该该字符串常量所在内存不会被回收,故能够通过指针顺利无误的访问。

 

char *returnStr()   
{   
    char p[]="hello world!";   
    return p;   
}  

"hello world!"是局部变量存放在栈中。当returnStr函数退出时,栈要清空,局部变量的内存也被清空了,所以这时的函数返回的是一个已被释放的内存地址,所以有可能打印出来的是乱码。 
如果函数的返回值非要是一个局部变量的地址,那么该局部变量一定要申明为static类型。如下:

 

 

 

 

char *returnStr()   
{   
    static char p[]="hello world!";   
    return p;   
}   

摘自http://blog.csdn.net/u012317833/article/details/40779297

 

 

 

 

 

字符串指针与字符串数组的区别

 

    char *s1 = "abc";
    char *s2 = "abc";
    char ss[] = "abc";
    printf("%p\n", s1);
    printf("%p\n", s2);
    printf("%p\n", ss);

 

运行结果是

0x80485d0

0x80485d0

0xbfc2df64

s1,s2实际指向了同一个地址
用char *ss = "abc"定义了一字符串指针,如果在程序中试图更新ss,程序就会出错,

因为字符串指针保存的数放在常量区,是不能被更改的。

但指针是放在栈上的

如果要更改字符串,使用字符串数组的定义方式:char ss[] = "abc";

 

char arr[] ="123"; //存在栈区数组arr内,可以改变

char *p ="123";  //"123"存放在常量区,不可改变

 

用new或malloc分配内存时,必须要对此指针赋初值。   
用delete 或free释放内存后,必须要将指针指向NULL   
不能修改指向常量的指针数据

 

释放内存时,如果是数组指针,必须要释放掉所有的内存,如   
char *p=new char[100];   
strcpy(p,”Hello World”);   
delete []p; //注意前面的[]号   
p=NULL;

 

const总结:

const修饰函数参数:传递过来的参数在函数内不可以改变

void testModifyConst(const int _x) {
     _x=5;   ///编译出错
}

const修饰成员函数:在函数的后面加上const意思是

1不能对其数据成员进行修改操作

2const成员函数不能调用非const成员函数,因为非const成员函数可以会修改成员变量

void out3() const{
        num+=10; //出错,const函数不能修改其数据成员
        cout<<num<<endl;
    }

const修饰对象:给一个对象加上const,即const的对象,不能引用非const的成员函数。

int _tmain(int argc, _TCHAR* argv[])
{
    aa a1;
    a1.out1();
    a1.out2();
    a1.out3();
    const aa a2;
    a2.out1(); // 错误,const的成员 不能访问非const的函数
    a2.out2();
    a2.out3();
    return 0;
}

const修饰引用:试图通过此引用去(间接)改变其引用的对象的值时,编译器会报错!

 int val = 1024;

  const int &ir = val;

  ir++;   //通过ir来改变val的值,编译时会出错。

 val++;   //可以通过val直接改变其值(第9行)
const修饰函数返回值:如果返回const data,non-const pointer,返回值也必须赋给const data,non-const pointer。因为指针指向的数据是常量不能修改。

 

const int * mallocA(){  ///const data,non-const pointer
    int *a=new int(2);
    return a;
}
int main()
{
    const int *a = mallocA();   //函数的返回值为const,那么接收函数的时候也要拿const来接
    ///int *b = mallocA();  ///编译错误
    return 0;
}

转自:http://www.cnblogs.com/xudong-bupt/p/3509567.html

 

extern“C”有什么作用?   
Extern “C”是由C++提供的一个连接交换指定符号,用于告诉C++这段代码是C函数。这是因为C++编译后库中函数名会变得很长,与C生成的不一致,造成C++不能直接调用C函数,加上extren “c”后,C++就能直接调用C函数了。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值