1./(★)/可以通过指针来改变int a 中 a 的值;因为指针指是对其地址的操作... ...(很重要!!!)
2.(★)函数名和数组名一样都是 常量指针 ,只能指向唯一的内存
3.c语言中 static uint8ucState=0类型为占8个bit的无符号整型的静态变量ucState,赋初值为0。
(附加:误区:const 是常量 而stastic是静态变量 是不一样的;前者修饰的量不能改变;后者是累加)
(附加:在给数组初始化时应当要注意的:例如: int num[100] = { 0 }; 也就是赋值所有的元素为 0;但是
(★)(★) 如果是这个样子: int num[100] = { 1 }; 结果是num[0] =1; 其余元素为 0 )(★)(★)(★)
4.(★)
也就是说只要涉及到要用指针来储存值时先必须分配内存!!
!!!
如果仅仅是声明一个指针*p,然后让他指向一个struct x;即p=&x;此时无需分配内存
5.数组之间的赋值需要用到strcpy()函数,但是一旦转化为指针,就可以直接是p1=p2;
6.char **和const char **事不一样的,前者指向char *,后者指向const char *
原因:例如:const float * 其实是只想一个具有const限定的float的指针
7./####################################################################/
/####################################################################/
/####################################################################/
/@@@@@@@/其实我们平时的声明就是!!!申请内存!!! 例如int i;即申请了一个内存为sizeof(int)大小的内存的房间,房间名字叫 i
!!!!!!
然后赋值就是给房间住进客官!!!!!!(即赋值是对房间名进行操作,即变量名!!)
8./@@@@@@@/inti=20; int* p; p=&i;
指针的意义:名称为 i 的房间里住着20,p相当于服务员,一旦申请int* p后(相当于聘请好这个服务员)也必须申请了一处内存(即服务员
站在吧台) 我现在要找 20 ,怎么办? 去吧台找服务员,服务员指着 i 房间说他在那里,然后我就找到了,所以此处的服务员就是一个指
针!!!!!!!!!!!! 他知道 20 的地址,即储存的是 20 的地址 ,所以我才能找到20 这个人!!!
请注意:::::int* p 中 int* 是一个整体!!!! 是声明指针的!符号! p才是指针变量!!!所以 p 的值是 &20(即20 的地址(房间号))
; 所以要读取20 就必须要读 *p !!!!!!
9./@@@@@@@/ 可以通过指针改变原 房间中的值 也就相当于 服务员 送走 20后;又迎来 15 客人!!!!!
即: int i=20; int *p;p=&i;(p是指向i的指针) *p=15; printf("%d",i);
此时 输出的值是 15 而不是 20 !!!!!(root:可以通过指针改变原 房间中的值)
/####################################################################/
/####################################################################/
/####################################################################/
10.注意细节: int *p;
int a[]={1,2,3,4,5,6};
要把 a[] 赋值给p ,直接是p=a即可, 因为a是首地址!!!
/####################################################################/
/####################################################################/
11.请注意数组名与指针的区别: 数组名只不过是一个const 指针(数组名只是指向首地址!!!如果要取得后面的数就只能是相对于首地址的
偏移量!!!而不能是指针的向后移动!!! 因为他是const指针,而指针变量p可以这样),即是指针常量!!! 而指针本身的意义是指针变
量!!!!!!!!!! 所以又有一种情况是指针能做到而数组名做不到的:
int *p;
int a[]={1,2,3,4,5,6};
p=a;
int i;
for(i=0;i<6;i++)
{
printf("%d",p);
p++;
}
偏移量: 例如 *(p+3); *(a+3); a[3]; 三者都是等价的 ^_^
/####################################################################/
/####################################################################/
12.指向指针的指针(即二阶指针):
int **p;
int a=10;
我们可以知道 p 是储存地址的地址的变量;所以必有 *p=&a;而p是储存&a(也就是*p) 的地址的; 所以如果调用出 10 就必须是printf
(**p); 而printf(*p)其实是输出 &a的地址 ......
// 好好理解 二阶指针!!!
#include
void main()
{
int a=10;
int*point; //一维指针 储存 10 的地址
int**ppoint; //二维指针 储存 一维指针的地址
point=&a;
ppoint=&point; //此处千万不能是 ppoint=&(&a);之类; 因为此处的 &a已经是常量了;不能对常量取地址;而point是变量;
所以可以...
printf("%d\n",*point);
printf("%d\n",**ppoint);
}
void find2(char array[], char search, char **ppa)
{
int i;
for (i=0; *(array + i) != 0; i++)
{
if(*(array + i) == search)
{
*ppa = array + i;
break;
}
else if(*(array + i) == 0)
{
*ppa = 0;
break;
}
}
}
ppa指向指针p 的地址。
对*ppa的修改就是对p值的修改。
13.函数指针和指针函数:
函数指针: 例如:void (*fun)(int )
(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
void MyFun(int x) //声明
{}
void (*fun)(int ); //声明
void main()
{
fun=&MyFun; //把函数地址给函数指针
fun=MyFun; //这是什么?哈哈;也是把函数地址给函数指针
MyFun(10); //输出结果
(*fun)(10); //可以这样
fun(10); //还可以这样
(*MyFun)(10); //竟然还可以这样
以上说明了什么?》》》》》函数名就是 指针(★)
MyFun 的函数名与FunP函数指针都是一样的,即都是函数指针。
MyFun 函数名是一个函数指针常量,而 FunP 是一个函数数指针变量,这是它
们的关系。
}
(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
指针函数: 例如:char *fun(int x) 必须有返回值,而且类型是char * 型
14.
(1)为了防止头文件被重复引用,应当用 ifndef/define/endif 结构产生预
处理块。(★)
(2)用#include
标准库目录开始搜索)。
用 #include“filename.h” 格式来引用非标准库的头文件(编译器将
从用户的工作目录开始搜索,如果工作目录没有找到,在返回标准库开始搜索)。
15.
/..............#ifndef..#define...#endif............................................./
#ifndef GRAPHICS_H // 防止graphics.h 被重复引用 (★)(★)(★)
#define GRAPHICS_H (★)(★)(★)
#include
…
#include“myheader.h” // 引用非标准库的头文件
…
voidFunction1(…); // 全局函数声明
…
classBox // 类结构声明
{
…
};
#endif
以上是一个头文件块;是用ifndef/define/endif 结构产生 预处理块(所有声明在一起是一个
整体);
然后我要生成这些函数或者类;必须在 graphics.cpp中单独写入;
例如:
#include"graphics.h" //声明包含在此头文件中
void Function1()
{
printf("hello world");
}
class Box //类结构声明
{
public:
....
private:
....
protected:
....
};
现在我要应用此头文件:
#include "graphics.cpp"//声明包含在此头文件中
void main()
{
.....
}
/............#ifndef..#define...#endif..................................................../
16.头文件的作用
(1 )通过头文件来调用库功能。在很多场合,源代码不便(或不准)向用户公布,只要
向用户提供头文件和二进制的库即可。用户只需要按照头文件中的接口声明来调用库功
能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。
(2 )头文件能加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中
的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的
负担。
17.代码格式(注意空格的添加和 {} 的随时添加 !!!)
(a)为风格良好的代码行 (b )为风格不良的代码行。
int width; //宽度 int width, height, depth; // 宽度高度深度
int height; // 高度
int depth; // 深度
x = a +b; X = a + b; y = c +d; z = e + f;
y = c + d;
z = e + f;
{ dosomething(); } for (initialization; condition;update) for (initialization;condition; update) { dosomething(); dosomething(); other(); } // 空行 other(); if 、for 、while等关键字之后应留一个空格再跟左括号 ‘(’,以突出关键字。 、“&&”、“||”、“<<”, “^ ”等二元操作符的前后应当 加空格。 void Func1 (int x,int y,intz); // 不良的风格 if (year >=2000) // 良好的风格 if(year>=2000) // 不良的风格 if ((a>=b)&&(c<=d)) // 良好的风格 if(a>=b&&c<=d) // 不良的风格 for (i=0; i<10;i++) // 良好的风格 for(i=0;i<10;i++) // 不良的风格 for (i = 0; I < 10; i++) // 过多的空格 x = a < b ? a :b; // 良好的风格 x=a
int *x =&y; // 良好的风格 int * x = &y; // 不良的风格 array[5] =0; // 不要写成 array [ 5 ] = 0; a.Function(); // 不要写成 a . Function(); b->Function(); // 不要写成 b -> Function(); 是“在变量和函数名中加入前缀以增进人们对程序的理解”。例如所有的字符变量均以 ch为前缀,若是指针变量则追加前缀p。如果一个变量由ppch 开头,则表明它是指向字 符指针的指针。 “匈牙利”法最大的缺点是烦琐,例如 int i, j, k; float x, y, z; 倘若采用“匈牙利”命名规则,则应当写成 int iI, iJ, ik; // 前缀 i 表示 int 类型 float fX, fY, fZ; // 前缀 f 表示 float 类型 (2) 无意义的名字(因为用数字编号最省事)。 (3) (5):重点补充:有时候把 if (p == NULL) 写成 if (NULL ==p) 其实是防止把if (p == NULL)写成 (6): 不良风格: 20. for 语句 22. (3).如果输入参数以值传递的方式传递对象,则宜改用“const &“方式来传递,这样可以省去临时对 (4).尽量不要使用类型和数目不确定的参数。C标准库函数 printf 是采用不确定参数的典型代表, (5).有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。 例如字符串拷贝函数strcpy 的原型: char *Strcpy(char *strDest,const char*strSrc); //格式 strcpy 函数将strSrc 拷贝至输出参数strDest 中,同时函数的返回值又是 strDest 。这样做 char str[20]; int length = strlen( strcpy(str, “Hello World”)); //经典 3>(★) 例如: while(*to++ ==*from++); 引用与指针的比较 :( 经 典 ) 引用:n是m的一个引用,m是被引用物;int m ; int &n = m;//好好看看它的形式哦,( ⊙o⊙ ;其实也就是在叫 m ;其实 n 就是 m;m 就是n;是同一个人;所以对 n的处理也就是对 m 的处理 ! ! ! ! ! ! (真的要 注意哦对n的处理就是对 m 的处理 ) 注意点:(1).引用在被创建的同时必须被初始化(必须的哦) iK =iJ; //此时 iK 的值改变咯(知道 iK 其实就是 iI ) printf("%d\n", iI); ----> 10 printf("%d\n", iJ); ---->10 引用传递参数的小例子: printf("%d\n", x); (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★) 640Kought to be enough for everybody — Bill Gates 23-1.内存分配的方式: returnstr; // 错误 错误 错误 错误 错误 错误 错误 (3).在堆上创建:也就是所谓的动态分配(malloc 或者 new 申请内存单元);程序员 自己决定何时释放( free 或 delete ) 如果用 malloc 和 new 申请内存,必须用if (p == NULL) or if (p != NULL) (4).请注意:自己经常犯的错误:在动态分配时要时刻牢记的不仅是申请内存;更重 以是不可以将栈内存的指针或引用返回的! ! ! ! !! 【规则4】用free 或 delete 释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。 23-3. 不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变(此 O(∩_∩)O~)。 指针可以随时指向任意类型的内存块,它的特征是“可变”,所以我们 常用指针来操作动态内存。指针远比数组灵活,但也更危险。 例如: char str[] = "hello"; //在栈内存中的分配 p[0] ='X'; //由以上可知此句是错误的;const 是不能改变的额 puts( str ); 23-4.计算内存容量: 计算 sizeof( p ); 输出来的结果是 4 ( 很诧异吧,O(∩_∩)O哈哈~ ); 实际上sizeof计算的是 sizeof( char * ) == 4;C 和 Cpp都是不能知道指针所指的内存容量的 除非在分配内存的时候记住它;所以sizeof( 指针) == 4 ; 所以计算内存不能用指针计算!!! (2)一个典例: (3)注意:例如: char str[]="hello"; printf("%d\n", sizeof(str)); 编译器在编译时总是要给每个参数制作一个副本;指针参数 p 的副本是 _p ;执行函数时;副本 典例: void main() GetMemory(str, 100); assert(str != NULL); strcpy(str, "hello,world"); puts(str); free(str); //重要重要重要重要重要重要重要重要重要重要重要 GetMemory(&str, 10); assert(str != NULL); strcpy(str, "hello"); puts(str); free(str);//重要重要重要重要重要重要重要重要重要重要重要 另类形式: returnp; str = GetMemory(str, 10); assert(str != NULL); strcpy(str, "hello"); puts(str); free(str);//重要重要重要重要重要重要重要重要重要重要重要 str = NULL; //好习惯 (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★) ... return str; //错误错误错误错误错误错误错误错误错误错误错误错误错误错误 void main() str = GetChar(); //得到的是 乱码 puts(str); 23-6. 所以这个指针本身还是存在的;发现指针 p 被 free 以后其地址仍然不变(非NULL),只是 该地址对应的内存是垃圾,p 成了“野指针”。如果此时不把 p 设置为 NULL ,会让人误 以为 p 是个合法的指针。进而导致错误;而且此时如果用 if (p != NULL) or if(p == NULL) (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★) 切记:在 free 和 delete 指针之后;必须把指针赋值为 NULL ;防止指针成为 “野指针” ! (★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★) 23-7. ;必须的是我们自己 free() 或者 delete() 它们;并且要赋值指针为 NULL;防止变为野指针! ( 请不要偷懒或者对这个问题不以为然;会出大问题的!!!靠... ... ) 》》》》》》指针死了,并不代表它的内存回收咯;内存释放咯,也并不代表指针死了(可能变野 之前的 23-6. 也提到了野指针,但那时候重点讲的是 要 free (or delete) 内存,现在 所以在定义时要将指针初始化;可以是 NULL 或者 指向... char *p = NULL; { public: void Func(void){ cout << “Func ofclass A” << endl; } }; void Test(void) { A *p; { A a; p =&a; // 注意 a 的生命期 p->Func(); // p 是正常指针 } p->Func(); // p 是“野指针” } 处理 1. void main() for (i = 0; i < 5; i++) deletepi; //释放内存 挂嘴边 在 C++的类中,对于不同的对象的处理是不同的,所以会有不同的 内存的申请方式 Obj *objects = new Obj[100]; // 创建 100 个动态对象 不能写成: Obj *objects = new Obj[100](1);// 创建 100个动态对象的同时赋初值 1 在用 delete 释放对象数组时,留意不要丢了符号‘[]’。例如 delete []objects; // 正确的用法 deleteobjects; //错误的用法 后者相当于 delete objects[0],漏掉了另外99 个对象。 23-12. #endif 条件指示符#ifndef 检查BOOKSTORE_H 在前面是否已经被定义 这里 BOOKSTORE_H 是一个预编译器常量 习惯上预编译器常量往往被写成大写字母 如果BOOKSTORE_H 在前面没有被定义 则条件指示符的值为真 于是从#ifndef 到#endif 之间的所有语句都被包 含进来进行处理 相反 如果#ifndef 指示符的值为假 则它与#endif 指示符之间的行将被忽 略 为了保证头文件只被处理一次 把如下#define 指示符 #defineBOOKSTORE_H 放在#ifndef后面 这样在头文件的内容第一次被处理时 BOOKSTORE_H 将被定义 从而防止了在程序文本文件中以后#ifndef 指示符的值为真 只要不存在两个必须包含的头文件要检查一个同名的预处理器常量 这样的情形 这 个策略就能够很好地运作 #ifdef指示符常被用来判断一个预处理器常量是否已被定义 以便有条件地包含程序代
if (width
‘,’之后要留空格,如 Function(x, y, z) 。如果‘;’不是一行的结束
符号,其后要留空格,如 for(initialization; condition; update)。
赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如“=”、“+= ” “>=”、 “<=”、“+ ”、“* ”、“% ”
void Func1(int x, int y, intz); // 良好的风格
18. 命名 规则
匈牙利命名规则的主要思想:
共性:
(1)
Windows 应用程序的标识符通常采用“大小写”混排的方式,如 AddChild。
而 Unix 应用程序的标识符通常采用“小写加下划线”的方式,如add_child。别把这两混在一起使用!
尽量避免名字中出现数字编号,如 Value1,Value2等,除非逻辑上的确需要编号。这是为了防止程序员偷懒,不肯为命名动脑筋而导致产生
1>类名和函数名采用大写字母开头的字母组合而成,例如:class TreeNode; voidSetValue();
2>变量和参数采用小写字母开头,组合名的后面字母仍用大写字母 例如:inttreeNode; BOOL flag;
3>所有常量都用大写字母和下划线的组合 例如:const int MAX; const intMAX_LENGTH;
4>静态变量前都加上s_;然后的命名和变量时一样的 !!!例如:static ints_treeNode;
5>全局变量前都加上g_(global) 例如:intg_treeNode; (一般情况下不用(少用)全局变量)
6>类的数据成员前加上m_(member)以示区别
19.写if 语句与零值比较
(1):BOOL值 例如:bool flag; if(flag)//flag 为真 和 if (!flag) //flag 为假
其它的用法都属于不良风格,例如:
if (flag == TRUE) //注意空格
if (flag == 1) //注意空格
if (flag == FALSE) //注意空格
if (flag ==0) //注意空格
(2):整形与零值的比较
if (curentValue == 0) //注意空格
if (curentValue != 0) //注意空格
(3):浮点值与零值的比较
if ((curentValue > -0.000001)&& (curentValue <0.000001))
千万不能直接与0.0之类比较; if (curentValue==0.0) //错误
要转化为:if((curentValue >-EPSINON)&& (curentValue
(4):指针变量与零值的比较
定义一个指针变量 p
if (p == NULL) //不要写成 p ==0 //注意空格
if (p != 0)
不要写成:
if (p == 0 )
if (p != 0)
if (p)
if (!p)
if (p = NULL) 而导致出错;编译器编译时 if (p = NULL)是不会报错的,但是如果写成
if (NULL = p) 是会报错的...(值得学习!!!)
if (condition) //注意空格
return x;
return y;
改写后:
if (condition) //注意空格
{
return x;
}
else
{
return y;
}
或者:
return (condition ? x : y); //注意空格
(1):在多重循环中,如果有可能,将最大循环放在最里面!in fact 为了减少 CPU的跨切循环层 的次数
(2):如果循环体内存在逻辑条件判断,最好将它移到循环外面:
例如:
for (i = 0; i <= N;i++) if(condition)
{ {
if(condition) for (i = 0; i <= N;i++)
{ {
DoSomething(); DoSomething();
} }
else }
{ else
DoOtherthing(); {
} for (i = 0; i <=0; i++)
} {
DoOtherthing();
}
}
如果 N 不大 ;那么两个执行效率差不多,可以用左边的;如果 N 很大 那就用右边的
(3):最好采用半开半闭的方法
将上面语句改成:
for (i = 0; i < N+1; i++)
{
... ...
}
21. switch语句: 结尾的 default 一定要加上;以防别人误以为你忘记 default 的处理
函数方面:
(1).书写要完整:例如: voidTreeNode(int x, int y);
void TreeNode(int , int ); //不良写法
int value(void);
intvalue(); //不良写法
(2).对于指针作为参数的处理,如果只做输入用,最好在前面加上 const ;避免被无意修改!!!
例如: char *Strcpy(char *strCopyTo, const char *strCopyFrom);
象的构造和析构过程,从而提高效率。 (★)(★)(★)(不懂!!!!!!)
其原型为:int printf(const chat *format[, argument]…);这种风格的函数在编译时丧失了严
格的类型安全检查。 (★)(★)(★)(不懂!!!!!!)
并非多此一举,可以获得如下灵活性:
(6).关于函数的 return 语句(★)(★)(★)
1> return 语句不可以返回指向栈内存的指针或者引用 ,因为该内存在函数结束时自动销毁
例如: char *Fun(void)
{
char str[] = "hello word"; //str 内存位于栈上
...
returnstr; //错误
}
2> 要尽量提高函数的执行效率
例如:
return String(s1 + s2); 和 temp= String(s1 + s2); return temp;
两者的执行效率是不同的!! 前者更好!!前者是创建一个临时的对象并返回它。
对于后者:首先,temp 对象被创建,同时完成初始化;然后拷贝构造函数把temp 拷贝
到保存返回值的外部存储单元中;最后,temp 在函数结束时被销毁(调用析构函数)
。然而“创建一个临时对象并返回它”的过程是不同的,编译器直接把临时对象创建
并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了效率。
类似的 我们 要写:return (x + y); 而不是:temp = x + y; returntemp;
程序一般分为 Debug 和 Release 版本,Debug 版本用于内部测试;Release版本用于发行给用
户。 断言( assert ) 的使用时很重要的!!!一般在函数的入口处最好用断言来判断参数的可
行性。而且 assert 只在 Debug 起作用,是一种宏结构,不是一种函数,是为了避免对我们的
函数主题产生不必要的影响。assert 的作用是:只要其条件不满足,就会终止程序的运行!!
char *Copy(char *copyTo, const char *copyFrom)
{
assert((CopyTo != NULL) &&(CopyFrom != NULL)); // 使用断言
byte *to=(byte*)copyTo; //防止改变地址
byte *from=(byte *)copyFrom; //防止改变地址
return copyTo
}
4>(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
)哇,怎么会是这样啊,?这样也行?O(∩_∩)O~;例如一个人叫 m;现在他有一个绰号 叫 n ;我们叫 n
(2).一旦被初始化,就不能改变引用的关系咯
例如:int iI =5; //注意 空格 命名法则( 匈牙利 )
int iJ = 10;
int &iK = iI; //引用参数的定义以及初始化
//所以此时就是相当于 iI 改变了
printf("%d\n", iK); ----> 10
void Add(int &x) //引用传递的 处理像是指针的处理
{
x += 1;
}
void main()
{
int x;
Add(x); //引用传递的 传入 像是值传递
}
(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
23. (深入研究 内存 问题 )
欢迎进入内存这片雷区。伟大的 Bill Gates 曾经失言:
(1).从静态储存区域分配:内存在程序编译时就已经分配好了,这块内存在程序整个
运行期间都是存在的。例如:全局变量;static 变量
(2).在栈上创建:在执行结束时被自动释放;
例如之前的一段程序:
char *String(void)
{
char str[]="hello world"; // 在栈上创建的
... ...
} //内存在程序结束时被自动释放
23-2.常见的错误:
(1).内存未分配成功却使用了它,解决方法:在使用前检查 指针 是否为 NULL;
如果指针 p 是参数;那么可以 用断言 assert(p != NULL) 来判断;
来防止出现错误!
(2).分配成功;但是未初始化就使用了它
(3).操作超过内存边界
要的是释放内存!!! 规则:malloc 和 free次数必须相同;new 和delete次
数必须相同;
(5).释放了内存却还在使用它:
典例1: 栈指针或者栈引用的返回问题 : 在程序执行完后,内存被自动释放;所
典例2: 动态分配后,用free和new 释放了内存;但是指针没有设置为 NULL ;
导致指针成为野指针
【规则1】不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
【规则2】避免数组或指针的下标越界,特别要当心发生“多 1”或者“少 1 ” 操作。
【规则3】动态内存的申请与释放必须配对,防止内存泄漏。
指针与数组对比:( 宏观上 )(★)(★)(★)(★)(★)(★)(★)(★)(★)
数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。数组名对应着(而
句话说明或者照应了我以前的说法:数组名实际上就是一个 const 指针,特点是:指向
唯一内存;但是内存中的值可以改变;函数名本质上也是指针;具体的请看以前的...
char *p ="world"; //实际上是位于静态存储区 相当于指针 p 指向的是const
//即:const char *p;
str[0] = 'X';
puts( p );
(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
(1)int str[100]; 用sizeof();来计算,输出的是 100 ;现在有一个指针p指向 数组str;
int TreeNode( char str[100] ) 或者 void TreeNode( char str[100])
{ {
return(sizeof(str)); printf("%d\n",sizeof(str));
} }
以上两种情况输出的结果都是 4;哇塞,不会吧;会的;因为此处的数组已经退化
为指针咯
请问输出的结果是多少呢? 对了是 6 ;请不要把 '\0'不当人!!!我靠!!!
(4)还要注意:sizeof() 是计算 内存大小的 ;而 strlen() 是计算实际字符串大小的哦
23-5. 指针参数如何传递的?
_p 的改变就是 指针 P 的改变;
void GetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) *num)
}
{
char *str = NULL;
}
貌似这个程序非常正确;其实是从根本上错了;str 根本就没有分配到内存;不信就“断言”吧;
其实想想也是很简单的;不就是相当于要为 str 分配内存吗?刚开始str中是NULL;现在我要
改变它就是了;那么不就可以通过指向 str 的指针来改变吗? 所以不就可以定义一个指向指针的
指针来处理吗?
(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
简单一句话-:如果参数是指针;请不要指望让它去申请内存;
总之如果要用 函数 来实现给 字符串 分配内存;请不要传递实参 str 即不要写成形参是指针 *p
形式;不要指望它;要找就找 &str 和 **p 他们可以做到为你分配内存 !!!
void GetMemory(char **p, int num )
{
*p = (char *)malloc(sizeof(char) * num);
}
void main()
{
char *str = NULL;
str = NULL; //好习惯
}
char *GetMemory(int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void main()
{
char *str = NULL;
}
这里还强调 return 函数的问题;;;
例如: char*GetChar()
{
char str[] = "hello"; //此处是 栈 内存;程序结束时自动消亡
}
{
char *str;
}
探讨 free 和 delete 把指针怎么了???
O(∩_∩)O哈! 它们只不过把指针的内存给释放掉咯;但是并没有把指针本身给干掉((★))
都是判断不了的 !!!所以切记:::::free or delete 之后必须要使之指针为NULL
切记:动 态 内 存 是 不 会 被 自 动 释 放 的... ...
只要你的整个的大的 main() 函数还在执行;不管是那个局部的多么少的动态分配都不会自动释放
指针咯);
》》总结: 释放内存 和 让指针变为NULL是不可能同时达到目的的;但是我们又是必须要做的
23-8.
————》请杜绝野指针
------By pt
首先我们来看一下什么要的指针叫“ 野指针 ” ;野指针,顾名思义,是没有人需要的指针,
或者说你人们害怕的指针!请不要把野指针和和 NULL 指针混淆, NULL指针 可不是野指针
重点讲野指针:
野指针的产生:
1. 在定义指针的同时没有初始化指针,这时在使用时它就会乱指一气;
或者
char *p = (char *)malloc(100);
2.就是之前所说的 free 和 delete 之后没有赋值指针为 NULL;(注意)
3.指针操作超过了变量作用的范围
例如:
class A
23-9.
free 和 malloc 与 delete 和 new之间的差别
free 和 malloc 是库函数,但是 delete 和 new 不是库函数;对于一个外部的对象而言,它在创
建时要执行 构造函数,在消亡时要执行 析构函数;但是free 和 malloc 是库函数是库函数,它
们不在编译器的控制范围之内,不能把执行构造函数和析构函数的任务强加给 它们,所以就出现
了delete 和 new ;理论上讲:delete 和 new 对于内部数据处理时也能代替free 和 malloc,但
是由于 C 中只能是free 和 malloc,所以free 和 malloc是不能被遗忘的......
23-10.
内存耗尽怎么办???-----》也就是说我在申请动态内存时没有那么大的内存咯,返回了NULL
我该怎么办???
判断 if ( p = NULL )
{
return // 可以用return 语句返回
}
处理 2.
判断 if ( p = NULL )
{
cout << “Memory Exhausted”<< endl;
exit(1); // 马上用exit(1)杀死整个应用程序
}
附录:关于 exit(1) 和 exit(0)我在这里也要解释一下,其实在正常没有返回值的情况下他们两是完
全一样的》》》都是杀死应用程序;当时如果有返回值是,exit(0) 表示的是 非正常情况下的结
束,而 exit(1),或者 exit(2) 等非 0 的数都可以表示正常结束应用程序,一般都用exit(1);
不过现在对于 32位以上的应用程序来说,基本上是不可能内存耗尽的,因为即使内存耗尽了,”虚存
“可以帮我们忙,自动用硬盘空间来顶替... ...
------》
个人建议最好用exit(1)杀死整个应用程序
23-11.
new 的使用 比malloc要简单多了
例如: int *p = (int*)malloc(sizeof(int) * num);
int *p = new int[num];
例如:
#include
using namespace std;
{
int *pi = new int[10]; //用 new申请内存更方便
int i ;
for (i = 0; i < 5; i++)
{
cin >> pi[i]>> endl;
//scanf("%d\n",&pi[i]);
}
{
cout << *(pi + i)<< endl;
}
}
---》 class Obj
{... ...};
Obj *obja = newObj; //声明一个
Obj *objb = new Obj(1); //... 外加赋值为1 ;
(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
如果用 new 创建对象数组,那么只能使用对象的无参数构造函数。例如
(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)(★)
(★)(★)(★)(★)
(★)(★)(★)(★)
#ifndef BOOKSTORE_H //有可能一个源文件中包含了两个以上此头文件, 这时防止重复处理相同的头文件
#define BOOKSTORE_H