实习期间关于VC/C++的一点学习笔记

实习期间,用到了VC++,一点总结如下,以作备份。只是自己的理解,不一定权威正确。请读者慎重,请谅解。

一、p->a       p为指针,调用p指向对象的a方法   

   char *s   字符串

二、..\表示项目文件所在目录向上一级目录下的目录。即当前目录   (亲兄弟)

       ..\...\表示项目文件所在目录向上二级目录之下的目录。(亲堂弟)

三、

CString转为int:intnport=_ttoi(port)

 int转为CString:str1.Format(_T("%d"),intnum1);

TCHAR字符数组转为CStringTCHAR str2[10];  Cstring str ;    str.Format(L"%s",str2);//是这个!!

CString转为TCHAR字符数组:_tcscpy(目的str2,str);

string转为int:  inttmp; string str;  tmp = atoi(str.c_str());

Char数组转为string  stringstr;    str = ch;

AfxMessageBox不能输出int变量?

Tchar数组转长整形long_wtolTchar*

 

TCHAR数组清空

 TCHAR preStr[20]   memset(preStr,0,20*sizeof(TCHAR));

两个Tchar字符串是一样:

_tcscmp(a,b)==0

Unicode字符串的实际长度,即结尾符'\0'前内容的长度,不包括结尾符'\0',Tchar*字符串也能用。

_tcslen(preStr) 《《《》》》非Unicode的strlen一样


Memset

memset(void *buffer, int c, int count);

功能:用来对一段内存空间全部设置为某个字符。把buffer所指内存区域的前count个字节设置成字符,为cASCII码值的字符。 


针对string的两个字符串复制函数:

strcpymemcpy都是从一块内存复制一段连续的数据到另一块内存

memcpy(dest,src,count);

由src所指内存区域复制count个字节到dest所指内存区域。     

strcpy(dest,src);

 strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy是根据其第3个参数决定复制的长度。 





CStringstring虽然是类,但它们的复制:直接等号复制,值拷贝,各有不同的内存。

Cstringa=_T("hello"),b;

b=a;

改变a的值,b不变。

另附:

关于对象的赋值

对象2 = 对象1;

C++中:将对象1的成员变量值一一复制给对象2的对应成员变量(只针对成员变量,不针对成员函数!)。但两个对象具有各自的内存。如果再改变对象1的成员变量值,对象2不变。

而在C#和Java中,当将一个对象赋值给另一个对象时,他们指向同一块内存的地址,即两个对象是相同的,只是不同的引用。

Java中的引用类型变量:它的值是所引用对象的地址。如 Person p1,p2;  两个引用。  若p1=p2;则p1也指向了p2的对象。

 

四、

char *s 和 char s[]的区别

char *s1 ="hello";

char s2[] = "hello";      【区别所在】

char *s1的s1,而指针是指向一块内存区域,它指向的内存区域的大小可以随时改变,它是指向字符串常量的首地址的指针,编译器将字符串常量放在只读数据段.里面的数据是不可更改的。该内存里的值不可以改变了,但是可以改变s1指向其他字符串。

char s2[]的s2是数组对应着一块内存区域,其地址和容量在生命期里不会改变,只有数组的内容可以改变

在只读取不修改时,两个一样。

 

【内存模型】

       +-----+     +---+---+---+---+---+---+

   s1: | *======> | h |e | l | l | o |\0 |

       +-----+     +---+---+---+---+---+---+

       +---+---+---+---+---+---+

   s2: | h | e | l | l | o |\0 |

       +---+---+---+---+---+---+

 

 

场景一)

char *s1 ="hello";

char s2[] = "well";

s2=s1;  //编译ERROR    相当于改变s2指向别的字符串,错误的,

s1=s2;  //OK     改变s1指向s2指向的字符串  现在s1指向"well"

分析:s2其地址和容量在生命期里不能改变

场景二)

char s2[] ="hello";

char *s1 = s2;  //编译器做了隐式的转换 实际为&s2

char *s1 = &s2;

分析:以上两个指针复值完全等价,由于编译器会做这个隐式转换也容易导致初学者误认为char *s 与char s[]是一回事。

      另用第二种在一些编译器甚至会报警告信息。

场景三)

char *s1 ="hello";

char s2[] ="hello";

s1[0]='a';  //×运行ERROR(不可以改变s1指向的内部的值,但不改变,输出某个字符是可以的

s2[0]='a';  //OK

分析:运行时会报错,原因在于企图改变s1的内容,由于s1指向的是常量字符串,其内容是不可修改的,因此在运行时不会通过。而s2指向的是变量区字符串,可以修改。

 

下面是一些char *s1 和 chars2[]相同的地方(同样编译器对char[]做了隐式变化):

1)作为形参完全相同

如:

   void function(char *s1);

   void function(char s1[]);

2)只读取不修改的时候

如:

    char *s1="hello";

    char s2[]="hello";

   printf("s1[1]=[%c]\n",s1[1]);  //s1[1]=[e]

   printf("s2[1]=[%c]\n",s2[1]);  //s2[1]=[e]

    printf("s1=[%s]\n",s1);         //s1=[hello]

    printf("s2=[%s]\n",s2);         //s2=[hello]

2

#include<stdio.h>

intmain(int argc, char *argv[])

{

 char day[15] = "abcdefghijklmn";

 char* strTmp = "opqrstuvwxyz";

 printf("&day is %x\n",&day);

 printf("&day[0] is %x\n",&day[0]);

 printf("day is %x\n",day);


printf("\n&strTmpis %x\n",&strTmp);

 printf("&strTmp[0] is %x\n",&strTmp[0]);

 printf("strTmp is %x\n",strTmp);


getchar(); 

 return 0;

}

运行后屏幕上得到如下结果:



其实看到结果估计很多东西就好明白了,

   先看看前三个输出也就是关于变量day的,在 char day[15] = "abcdefghijklmn";这个语句执行的时候,系统就分配了一段长15的内存,并把这段内存起名为day,里面的值为"abcdefghijklmn",如下图所示:



       再看程序,第一个输出,&day,&号是地址运算符,也就是day这个变量的内存地址,很明显,在最前面,也就是a字符所在字节的地址;

        对于第二个输出也就好理解了,&day[0],就是day数组中第一个变量(也就是a)的地址,因此他们两个是一样的;

       第三个输出是day,对于数组变量,可以使用变量名来索引变量中的内容,其实这里的day可以理解成数组变量退化的指针,并且指向数组的开头,既然把它理解成指针,那么它的值肯定是地址了,所以他的值和上面两个也一样。

   再看看后面三个输出,关于字符串指针strTmp,在执行char* strTmp = "opqrstuvwxyz";后,内存的图示如下:



如图所示,内存分配了两段内存,一个名为strTmp,类型是一个字符指针,另外一段是一个字符串常量,且strTmp里面存放着字符常量的首地址,注意这里无法通过strTmp修改这段字符串,因为是常量;于是程序中的后面三个输出就好理解了;

&strTmp:strTmp这个字符指针的地址

&strTmp[0]:strTmp所指字符常量第一个字符的地址

strTmp:strTmp这个字符指针的值,即字符常量的首地址

因此,最后两个的值是一样的。

     指针可以这样理解,指针这种类型,和int,char,double等等是一样的,只是它用来保存地址值的,而int变量保存整数,char变量保存字符,仅此而已,就char型指针或者int指针,本质是一样的,都是存放的地址,只不过那个地址所里面的变量类型不同而已,还有一种void型指针,就是可以放任何类型变量的地址。

 

五、

指向字符的指针和指向字符串的指针:都是连续的,它自己找是存的字符还是字符串。

Vc6.0头文件名不区分大小写,但函数和变量名称区分大小写

windows文件路径不区分字母大小写,Linux区分字母大小写

 

六、匈牙利命名

命名:

函数:各首字母大写

变量:小驼峰:类型前缀+      第一个字母小写

      全局变量必须要以 g_ 开头:

指针以p开头,intn开头 

类:以C开头,各首字母大写

 

六、

CList中查找某项,或者在某索引处插入要先找POSITION

POSITION position = contact.m_userList.FindIndex(i);

然后

m_userList.GetAt(position)

 

七、

类的成员函数的定义实现时,要加 类名:: 找了好久,囧!!

 

八、

当某类的成员函数的形参有该种类型的对象时,要定义类的默认拷贝构造函数

CContact(constCContact& );

 

九、

调试:

VC6.0

F9——插入删除断点,断点位置为光标所在行

F5——调试go,开始运行,到断点会自动停止,没有断点,会运行完

Ctrl F5——直接运行,到断点不会停止

F10——向前走一步,不进入函数内部

F11——向前走一步,如果由函数就进入内部

未开始调试的情况下,按F10或者F11,会自动开始调试,鼠标自动定位到头部

Shift+F11——如果进入了函数,执行到中间,想跳出来,就按这个

Ctrl+F10——运行到光标,个人觉得这个很好用,觉得哪一行可能会出问题,在这一行前点一下鼠标,然后按下这俩键,就运行到这里了,不用设置断点。

F5——运行到下一个断点

至于查值,说的是在上述调试运行到某一行的过程中,查看某个变量的值,这个可以通过下方出现的Variable和Watch窗口查看,Variable不能输入,自动出现相关变量的值,watch窗口,可以自己输入变量名称,查看运行到某一步时的变量值。

如果没有这两个窗口,可以在菜单栏右侧右键,勾选variable和watch,就会出现。在VS中按下CTRL+D+W,也会出现watch窗口。

 

十、

truct结构体中定义构造函数和析构函数

 

structCTest

{

CTest();

~CTest();

int num;

};

CTest::CTest()

{

}

CTest::~CTest()

{

}

[...]

struct CTest * pTest = new struct CTest();

[...]

和class几乎没有区别。

 

十一、

结构体可以new,也可以不new。new与不new的区别是,new为会每一个成员字段赋一个默认初值(还记得default关键字吗),而不new则不会这么做。

 

十二、

汉字、字符、字节、位之间的关系

 

1字节8   天经地义永不变     O(_)O哈哈~

 

1个汉字 = 1个字 = 1个字符

 

1个字符 = 1个字节 =8bit(ACSII码下)

1个字符 = 2个字节 =16bit(Unicode码下)

处理汉字时,会默认将 编码方式调整为Unicode码,所以一个汉字始终是2字节,英文字符还分ACSII和Unicode

 

十四、

引用

对一个数据可以使用“引用 ”(reference) ”,这是 C++ 对 C的一个重要扩充 的一个重要扩充 ,引用是一种新的变量类型,它的作用是为一个变量 起别名如有a,想给它起一个别名给它起一个别名 b,可以这样写 :

int a;

int &b=a; // 声明b是a的引用

以上语句声明了以上语句声明了 b是 a的引用 ,即 b是 a的别名。

b和a占内存中的同一个储单元 ,它们具有同一地址。

 

十五、

C++多态性:

在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时就会根据对象的实际类型来确定调用相对应的函数。

重写分两种,直接重写成员函数和重写虚函数。只有重写了虚函数的才能算作是体现了C++多态性。重载也没有体现多态性,重载是允许有多个同名的函数,而这些函数的参数列表不同,允许参数个数不同,参数类型不同,或者两者都不同。编译器会根据这些函数的不同列表,将同名的函数的名称做修饰,从而生成一些不同名称的预处理函数,来实现同名函数调用时的重载问题。

 

十六、

构造函数和析构函数:

构造函数:不需要用户来调用它,而是在建立对象时自动执行。如果要执行初始化赋值等操作时,必须要自定义构造函数;系统默认生成的构造函数是空的,什么都不干。

析构函数:当对象的生命期结束时,会自动执行析构函数。析构函数的作用并不是删除对象,完成清理工作,使这部分内存可以被程序分配给新对象使用。

         A类构造函数和析构函数中不用显式写父类和对象成员的构造、析构函数,具有好的品质,尊老爱幼,会先自动调用父类的构造函数,再对象成员的构造函数(对象成员中按声明的先后顺序,析构函数反过来),最后自己的构造函数,先别人最后自己。析构函数顺序完全相反。

 

十六、

C++类有对象成员,其构造函数与析构函数:

构造函数:

例如,一个矩形数据可用两个点数据成员,分别表示矩形的左上角点和右下角点:

 

  class Rectangle {

 

  public:

 

  Rectangle (intleft, int top, int right, int bottom);

 

  //...

 

  private:

 

  Point topLeft;

 

  Point botRight;

 

  };

 

  矩形的构造函数也应该初始化两个类对象成员,假定Point类有构造函数,矩形两个数据成员topLeft和botRight可放在Rectangle构造函数的初始化表中初始化:

 

  Rectangle::Rectangle(int left, int top, int right, int bottom)

 

  :topLeft(left,top), botRight(right,bottom)

 

  {

 

  }

 

  如果Point构造函数没有参数,或所有参数都有缺省的参数,上面的成员初始化表可以省略,但是,构造函数仍然被隐含地调用。在创建类Rectangle 的对象(调用类Rectangle的构造函数)时,会自动调用类Point 的构造函数(topLeft的和botRight的)。在类Point的构造函数为无参时,先自动调用Point的构造函数,再调用Rectangle自己的,不用在Rectangle的构造函数中加上:Point() Point构造函数。会自动调用!!

        如果类Point的构造函数为有参函数时,通常采用初始化表的方式来调用构造函数。初始化化的顺序如下:首先topLeft的构造函数被调用,接着调用botRight的构造函数,最后调用Rectangle的构造函数。TopLeft初始化在botRight之前的原因不是因为:在成员初始化表中,TopLeft位于botRight之前,而是因为在Rectangle类的定义中,TopLeft位于botRight之前。所以,如果构造函数的定义改成下面的形式,并不会影响到构造函数调用的顺序。

  Rectangle::Rectangle(int left, int top, int right, int bottom)

  :botRight(right,bottom), topLeft(left,top)

  {

  }

在与构造函数赋值相比,成员初始化列表占用较少的开销,因此具有较高的效率。更重要的是成员初始化列表是用来初始化类的const或引用类型数据成员的唯一方法,它们不能采用赋值操作。

 

析构函数:析构函数也是,不用在Rectangle 析构函数中,写~Point()。会自动调用!!

在类定义中含有对象成员,则在创建类对象时先调用对象成员的构造函数,再调用类本身的构造函数;析构时

先调用自己的析构函数,再对象成员的。与构造函数顺序完全相反。

 

十七、

CList、list的构造函数和析构函数

CListlist都有自己的构造函数,会构造一个空的有序列表。不用显式构造;

都有默认析构函数,不用重写析构函数。

 

十八、

判断Tchar数组为空Tchartext[10]

If(text[0]==_T('\0'))

法二、

CStringstr=_T("");

str.Format(L"%s",preStr);

if (str.IsEmpty())

{

TRACE(_T("空"));

}

 

十九、

句柄:要对某个窗口进行操作,首先就要得到这个窗口的句柄。其它各种资源(窗口,图标,光标等),系统在创建这些资源时会为它们分配内存,并返回标识这些资源的标识号,即句柄。是窗口,图标,光标等的标识号,不是内存的地址或标识。

 

二十、

vc++结构体的定义

typedefstruct Student

{

int a;

int b;

}Stu;

于是在声明变量的时候就可:Stustu1; 这里的Stu实际上就是struct Student的别名。这里也可以不写Student

如果没有typedef就必须用structStudent stu1;来声明

 

二十一、

windows是事件驱动方式的程序设计

WinMain函数是Windows程序入口点函数,与dos的main相同,当winmain函数结束或返回时,windows应用程序结束。


二十二、

内存泄露:

动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。

内存溢出:

内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。此时软件或游戏就运行不了,系统会提示内存溢出,有时候会自动关闭软件,重启电脑或者软件后释放掉一部分内存又可以正常运行该软件或游戏一段时间。


二十三、

C++在类中定义成员变量时不可以赋值,静态成员变量可以,而Java行,语法决定。呵呵


二十四、

TRACE("DDDDDDDDDDD");

TRACE("wewe%d",333);

同样还存在TRACE0,TRACE1,TRACE2。。。分别对应0,1,2。。个参数

TRACE0  ,就是不带动态参数输出字符串,  类似C的printf("输出字符串");

TRACE1  中的字符串可以带一个参数输出   ,类似C的printf("...%d",变量);

TRACE2  可以带两个参数输出,类似C的printf("...%d...%f",变量1,变量2);

TRACE3   可以带三个参数输出,类似C的printf("...%d,%d,%d",变量1,变量2,变量3);




CListControl控件的使用:

一、

int nLine = m_list.GetItemCount();列表多少行?

int nCol = m_list.GetHeaderCtrl()->GetItemCount();列表多少列

 

二、插入行,插入在首行

int nRow = m_ctrlList.InsertItem(i,“第一行第一列值”);//i表示在数据在CListCtrl中的索引为行,返回插入的行

m_ctrlList.SetItemText(nRow,1, “第一行第二列值”);

m_ctrlList.SetItemText(nRow,2, “第一行第三列值”);

 

三、GetNextSelectedItem这个函数

看msdn的用法,其实是返回第一个的index,然后走到下一个选中的行去。

删除第0行以后,下面的行会往上移,那么原来的第1行就变成了第0行。递归的



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值