编程多年的一些疑问
来源:互联网 发布:怎么查找淘宝评论过的 编辑:程序博客网 时间:2024/04/26 14:16
大学研究生阶段,总共写过的代码超过十万行,但是还有些小问题怎么解决,列举如下
问题1:*d代表的是什么?(前提:T *d,*c)
*d=*c,或许大家都懂啥子意思,跟d=c的区别大家也都懂,我所疑惑的是*d代表的是d指针所指向的内容,这个内容代表的是可以赋值的地址?还是本身他是一个值,不能被赋值,还有第二个疑惑的地方是,当指针指向的是一个int的值,而指针本身的类型是char类型,*d所指的类型到底是char还是int呢
回答第二小问题,根据vc6的编译结果
#include <iostream.h>
void main()
{
char *p;
int m=0x1041;
p=(char *)&m;
cout<<(*p);
}
输入为A,即ox41的char类型值,说明申请m保持的首地址是低位的地址,p+1存放的才是0x10的地址(后面用*(p++)做过实验),这个好像叫小端模式(低在低位,高在高位,x86都是这样,但是arm和dsp都相反),并且说明低位的地址才是整体的地址。
问题2:什么情况下会因为访问没有申请的地方而发生越界错误
指针越界的意思就是指针出了数组的最大界限,这种情况是比较危险的,因为指针指向了一个未知的地方,俗称“野指针”,
如int array[n],当不小心用到array[m] ,m>n-1或者m<0时数组指针会越界,还有字符串(也以数组的方式存放),比如常量数组
char xx[5]={0};
char *x="hello";//注意此时系统的长度是6,因为系统为其加上了1
// scanf("%s",x);
// printf("%s",x);
scanf("%s",xx);
printf("%s",xx);
第一种方式,肯定报错,原本是常量字符串,结果给其赋值,一定会出错,在scanf的时候就出错了,但是第二章,在scanf的时候不会出错,走到printf的时候都不会报错,但是无论文xx数组是多少,比如xx[12],当输入的值超过或者等于21个字母的时候,但是xx[19]的情况下,输入超过或者等于31就会报错,就到输入是36的时候就会产生程序无限执行的情况,就一定会报错,这是编译器的问题,正常情况下就报错了,但是编译器有可能不报错,需要写程序的人牢牢记住,不能超越范围,不能依靠编译器来解决。同时通过我的实验发现,当最小的不报错单位是20,当xx[m],m的一倍超越20之后就可以往上增长,比如xx[19]在入不超过30的时候就不会报错,20在写入不操作30的时候不会报错,但是21即使超过30也可以,预估可以到40,嘿嘿!
指针注意事:1:一旦我们用了new或malloc申请了空间,切记要用delete或free进行释放之;一旦用delete或free将动态申请的内存释放了,一定不要忘了将指针变量赋值为Null(后面一项最重要,在释放内存时,讲其指针变量置为NULL,然后就可以用p==NULL来判断是否该地址被初始化了没有)
问题3:怎么让输入输出流不出错?
写入一个超级大的字符串数组比如ss[111024],一般情况下采用while(scanf("%d,%d",&a,&b)!=EOF),同时的如果知道输入的组数的话那就更好说了(这里面更要弄清楚的是)
顺便复习下acm的输入输出模式(有些更有效率)
1__int64才是表示64位的,long int,unsigned long int,unsigned long都是表示32位的(vs6,32位系统中)
2gets(buf),buf是字符数组,char buf[20],跟scanf没有什么区别,但是实践证明不需要gets来读掉换行符,同时,对于scanf("%s",a);scanf("%s",b);输入的时候可以用换行可以用空格都可以,而且都不会输入了之后又多余的字符
3对于scanf必须给地址,而printf给地址或者非指针变量本身(给非指针变量的地址,打印出来的就是地址的值)
问题4:常量字符串超过赋值不会造成越界呢?(在问题2中一起回答了)
问题5:指针数组和数组指针(帮记忆)
指针数组:本质是数组,不过数组元素全是指针,以int *p[5]或者 int*(p[5])的模式出现
int *p[3];
int a[3][4];
for(i=0;i<3;i++)
p[i]=a[i];
这里int *p[3]表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]所以要分别赋值。
数组指针:本质是指针,不过是指向数组的指针,也叫行指针,以int(*p)[5]的模式出现,表示指向一个五维数组的指针
int a[3][4];
int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
问题6:引用和const所造成的错误?
intival=1024;
int&re = ival;
re=11;
上面程序编译之后ival的值改成11,这就说明引用的原意是给改别名
有幸找到了const的很多很好的用法http://hi.baidu.com/goodegg/item/d2feb8af2b0e94736cd455d1
1将其联合引用可以提高程序的健壮性(防止改动),但不能被作为输出,否则会报错
2非内部类型在参数传递有构造和析构的过程,但是通过引入传递能够节省时间空间void Func(A &a),内部数据类型int char不存在构造和析构的过程,很快,不需要这么干
3用const修饰函数的返回值,赋予值必须是const的,const char *str =GetString();
4 A & operate = (const A&other);常见模式
5const int ival = 1024; //ok:,这是可以被编译器接受的
const int *const &pi_ref = &ival;//如果不是const变量,用const修饰要报错
6悟到一招比较好的函数赋值法:如下是其构造函数,其中name和score都是其私有变量,这样赋值,简单直接
Student( const string& nm, int sc = 0 )
: name( nm ),score( sc ) {}
7网页http://blog.csdn.net/whyglinux/article/details/602329
为什么get_name()前面也加 const。如果没有前后两个const的话,get_name()返回的是对私有数据成员name的引用,所以通过这个引用可以改变私有成员 name的值,如 Student stu( "Wang", 85 );
stu.get_name() = "Li";
8const string& get_name(); //这两个函数都应该设成 const型
int get_score();
void output_student( const Student& student)
{
cout << student.get_name()<< "/t"; // 如果 get_name()和get_score()是非const成员函数,这一句和下一句调用是错误的
cout << student.get_score()<< endl;
}
由于参数student表示的是一个对const Student型对象的引用,所以 student不能调用非const成员函数如 set_student()。如果get_name()和 get_score()成员函数也变成非const型,那么上面的 student.get_name()和 student.get_score()的使用就是非法的
在编译时就会报错,虽然编程起来比较麻烦,但是后面调试非常简单
问题7:构造函数和析构函数的私有化问题?(唯一的入口问题)
class OnlyHeapClass
{
public:
static OnlyHeapClass* GetInstance()
{
// 创建一个OnlyHeapClass对象并返回其指针
return (new OnlyHeapClass);
}
void Destroy(){
delete this;
}
private:
OnlyHeapClass() { }
~OnlyHeapClass() {}
};
intmain()
{
OnlyHeapClass *p =OnlyHeapClass::GetInstance();
... // 使用*p
delete p;
return 0;
}
这个例子使用了私有构造函数,GetInstance()作为OnlyHeapClass的静态成员函数来在内存中创建对象:由于要跨函数传递并且不能使用值传递方式,所以我们选择在堆上创建对象,这样即使getInstance()退出,对象也不会随之释放,可以手动释放。
构造函数私有化的类的设计保证了其他类不能从这个类派生或者创建类的实例,还有这样的用途:例如,实现这样一个class:它在内存中至多存在一个,或者指定数量个的对象(可以在class的私有域中添加一个static类型的计数器,它的初值置为0,然后在GetInstance()中作些限制:每次调用它时先检查计数器的值是否已经达到对象个数的上限值,如果是则产生错误,否则才new出新的对象,同时将计数器的值增1.最后,为了避免值复制时产生新的对象副本,除了将构造函数置为私有外,复制构造函数也要特别声明并置为私有。
总而言之:构造和析构的私有化,是为了不让系统调用,而自己静态调用,这样能够保证单例化,并且能保证系统能在你做完你的事情前不析构(另外,堆对象就是new出来的,这是问题8中的)
Const引用可以这样认为,只能用其值,不能改其值
问题8:内存分配问题(怎样有效的使用malloc,或者)
内存分配方式有三种:
(1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
指针注意事项2:为什么不能用一个内部auto变量去初始化static指针?
static不能改变,却指向一个能随时回收的地址,容易形成野指针
问题9:约数和公倍数的问题
先把问题简化,求最大公约数和最小公倍数
最大公约数用辗转相除法,用递归或者非递归方式都可以实现
最小公倍数用两数的乘积再除以最小公倍数就搞定了
问题10:程序的界面皮肤问题
问题11:类的存储空间大小问题
1对齐规则(最大放最前最后是一样的,两倍最大,放中间不同,三倍最大),并没有定义函数和变量的占1
#include <iostream>
using namespace std;
/*没有任何数据成员的对象类占用一个字节的空间-----规则4*/
class A1
{
};
/*静态数据成员不占用类对象的存储空间-----规则3*/
class A2
{
char c1;
static int count;
};
/*当只有char类型时,以1个字节为单位对齐----规则1*/
class B1
{
char c1;
char c2;
};
/*与A比较发现,当最大为short时,以2个字节为单位对齐----规则1*/
class B2
{
char c1;
short s;
};
/*与A比较发现,当最大为int时,以4个字节为单位对齐----规则1*/
class B3
{
charc1;
int i;
};
/*与A比较发现,当最大为double时,以8个字节为单位对齐-----规则1*/
class B4
{
char c1;
float d;
};
/*与A比较发现,当最大为double时,以8个字节为单位对齐----规则1*/
class B5
{
doubled;
char c1;
};
/*c s i 占4个字节,d占8个字节----规则1*/
class C1
{
char c;
short s;
int i;
double d;
};
/*d占4个字节,c s i占4个字节----规则1*/
class C2
{
double d;
char c;
short s;
int i;
};
/*c占1个字节,d从下一个4字节开始占4个字节,s i在下一个4字节中----规则1*/
class C3
{
char c;
double d;
short s;
int i;
};
/*c s 在头4个字节中,d占下四个字节,i在最后4个字节中-----规则1*/
class C4
{
char c;
short s;
double d;
int i;
};
int main()
{
cout << "size of A1 : "<< sizeof(A1) << endl; /*1字节*/
cout << "size of A2 : "<< sizeof(A2) << endl; /*1字节*/
cout << endl;
cout << "size of B1 : "<< sizeof(B1) << endl; /*2字节*/
cout << "size of B2 : "<< sizeof(B2) << endl; /*4字节*/
cout << "size of B3 : "<< sizeof(B3) << endl; /*8字节*/
cout << "size of B4 : "<< sizeof(B4) << endl; /*8字节*/
cout << "size of B5 : "<< sizeof(B5) << endl; /*16字节*/
cout << endl;
cout << "size of C1 : "<< sizeof(C1) << endl; /*16字节*/
cout << "size of C2 : "<< sizeof(C2) << endl; /*16字节*/
cout << "size of C3 : "<< sizeof(C3) << endl; /*24字节*/
cout << "size of C4 : "<< sizeof(C4) << endl; /*24字节*/
system("pause");
return 0;
}
2继承的问题:本身多少+继承类的大小(如果是虚继承的话还需要+指向表(4个字节))注意,函数和变量混在一起,这个也有对齐问题(变量不能像函数对齐,但是虚函数有向变量对齐的冲动,并且函数不可和变量组合,变量可以和变量组合,并且组合时可以当虚函数不存在,本来不在一个地方存着)
class A4//24个(double在哪个地方都一样)
{
double num;
char numA2;
virtual FunA2();
};
class A4//24个字节
{
Shout num;
char numA2;
virtual FunA2();
};
3 实际的函数(非虚函数),可以认为不占空间
class A5//占用一个字节
{
char numA1;
void fun();
void fun2();
};
4 当是实继承的时候,父类子类都有虚函数的时候,公用
5当是虚继承的时候,加个4个字节的指针,指向父类
6继承两个类都来自于一个父类时,其中有一个类的虚函数列表会消失
问题12:sort中的greator<int>问题(按照任意排序规则使用)
vc6不支持list的自定义排序,代码已经写了两套(以华为编程大赛题目为原型),未经过验证不上传
问题13:空类型指针(void * p,可以在使用时指向任意类型,必须进行强制类型转换)
问题14:多重指针问题
问题15:const指针放前放后的问题
int me;
const int * p1=&me;//p1可变,*p1不可变,此时不能用*p1来修改,但是p1可以转向
int * const p2=&me;//p2不可变,*p2可变,此时允许*p2来修改其值,但是p2不能转向。
const int *const p3=&me;//p3不可变,*p3也不可变,此时既不能用*p3来修改其值,也不能转向
用来定义函数,放参数列表的括号后
问题16:输出给的是函数内部的临时变量或者其地址为什么不会报错(理论上已经销毁了)
http://blog.csdn.net/clamreason/article/details/7457202
如果函数没有返回值,则函数内部的变量在函数执行结束之后全部释放;
如果函数有返回值,则函数内临时变量在函数所在的赋值语句执行完毕之后释放.
问题17:类型安全
见网页http://www.cnblogs.com/lihaozy/archive/2010/10/28/1863921.html
相关部分参考了网页http://wenku.baidu.com/view/6b25e6d7b9f3f90f76c61b22.html,但是经过作者本人在vc6,32位系统中测试通过,同时有部分图片有显示问题
- 编程多年的一些疑问
- unix环境编程学习笔记-----有关进程的一些疑问???
- 抓取的一些疑问
- Nosql的一些疑问
- dynamic_cast的一些疑问
- python的一些疑问
- 我的多年编程经验总结
- 角色动画的一些疑问(-)
- copy constructor的一些疑问
- 关于360的一些疑问
- 关于PopupMenuListener的一些疑问
- 关于Spring的一些疑问
- 学习RMI的一些疑问
- 关于chukwa的一些疑问
- 关于jdbc的一些疑问
- 百度地图的一些疑问
- 关于MFC的一些疑问
- 初学Java的一些疑问
- Android应用开发之单元测试之白盒巧克力
- cuda数组的拷贝
- OpenCV检测直线
- C++运算符重载
- hdoj_2586How far away ? && poj_1986Distance Queries
- 编程多年的一些疑问
- Socket编程 简易测试socket UDP
- 多线程删除注册的事件
- HDU 4325 离散化 + 线段树
- 题目1075:斐波那契数列
- ubuntu12.04建立交叉编译环境,bin/.arm-none-linux-gnueabi-gcc: not found
- 反射
- Android中消息系统模型和Handler Looper
- c#文件读写相关类介绍