指针变量与内存分配
1. char * str1;
//str1 只是一个指针,指针指向的空间还没有分配,所以此时用strcpy向str1所指向的内存
中拷贝内容将出错。利用malloc动态分配指向的内存(在堆中):
str1=(char *)malloc(10) or str1=(char *)malloc(sizeof(char) *num)//分配num个char
所占有的字节(一般是1个字节)数空间,用完后必须用free释放内存空间。这与在栈中自动
分配的内存不同,栈中的内存在函数结束后自动释放。
2.char str2[10];//字符数组的赋值要么在声明时初始化(="dfdf"or={'a','b','c','d'},要末一个字符的赋值,str[0]= ;str[1]= ;......或者strcpy(str2,"aaaaaa")(因为内存空间已分好);
如果char str2[10];str2="abcdefgjk";就会出错,因为str2表示数组str2[10]的首地址,在声明数组时已经分配好了地址值,不是变量,而是常量。strcpy(str2,"aaaaaa")这种数组初始化方式,是将"aaaaaa"拷贝到以str2为开始地址的内存空间当中。
3。
//将指针str1指向以str2为首地址的内存空间,即现在str2和str1表示内存中的同一区域.
4。char * str3="aaaaaaaaaaaaaaa";
//这时的str2和str3就不指向同一内存空间,因为str3在初始化时已经分配了指向的内存空间;此时只是将str2所指向的内存的内容拷贝到str3所指向的内存空间的内容(注意:str3指向的内存空间的大小最好大于str2所指向的内存空间的大小,否则,可能将其他变量的内存覆盖。另外,c语言对数组不做越界检查,使用时候小心,否则出现不可预料的错误)。
5。其它如int,double,float,short等类型,在申明变量时内存空间就已经分配好了。例如:
输出结果为i=1;j=2
6。面向对象编程例如java中的对象声明,句柄和指针的情况类似
7.图例:
char*str1="abcd",*str2="cde";//"abcd"的首地址为2000,"cde"的首地址为2004,分别存放在str1和str2.
str1
str2
(1)
(2)
kdsj\0fkdjfkdj\0fkdjfkjdfk\0
8.总结:
9. C语言函数中的局部变量的空间一般都是放在堆栈里面.在进入函数前,通过"SUBSP,+XX"来为这些局部变量分配堆栈空间.然后同样通过BP来对这些局部变量进行访问.函数结束时,"MOVSP,BP"还原堆栈指针,局部变量随之而消失.最后以"POP BP"还原BP,结束该函数.
值得注意的是,C语言会自动为C函数中经常使用int类型变量设置成resigterint.这样的局部变量就不是使用堆栈空间的了,而就是直接使用SI寄存器.
10.
长度。
11.注意:char *str1;
注意:指针变量(存放地址的变量)的sizeof值一般都为4,不要将所占用内存大小和字符串
12. return语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被
13。内存分配方式有三种:
(1)静态内存分配。在内存的数据区上创建。内存在程序编译的时候就已经分配好。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,
程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,
但问题也最多。
按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.
7.4指针参数是如何传递内存的?
void GetMemory(char *p, int num)
{
}
void Test(void)
{
}
示例7-4-1 试图用指针参数申请动态内存
毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p=p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。
如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见示例7-4-2。
void GetMemory2(char **p, int num)
{
}
void Test2(void)
{
}
示例7-4-2用指向指针的指针申请动态内存
由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。这种方法更加简单,见示例7-4-3。
char *GetMemory3(int num)
{
}
void Test3(void)
{
}
示例7-4-3 用函数返回值来传递动态内存
用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return语句用错了。这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡,见示例7-4-4。
char *GetString(void)
{
}
void Test4(void)
{
char *str = NULL;
str = GetString(); // str 的内容是垃圾
cout<< str<< endl;
}
示例7-4-4 return语句返回指向“栈内存”的指针
用调试器逐步跟踪Test4,发现执行str =GetString语句后str不再是NULL指针,但是str的内容不是“hello world”而是垃圾。
如果把示例7-4-4改写成示例7-4-5,会怎么样?
char *GetString2(void)
{
}
void Test5(void)
{
}
示例7-4-5 return语句返回常量字符串
函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“helloworld”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。
14.函数参数的传递分为:值传送和引用(指针)传送。前者将变量的复本传给函数的形参,形参的改变不会引起变量原值得改变;后者将变量的地址传给形参,形参的改变将引起变量的改变。
======================================================================================
2003-3-21 20:29
1.
清除输入流的缓冲区,使它仍然打开,并把输出流的缓冲区的内容写入它所联系的文件中。成功时返回0,出错时返回EOF。
2.
3.
2003-3-22 9:21
1.再次重申:如果函数的参数是指针,千万不要用该指针申请动态内存。
2003-3-23 17:55
1.
2.
2003-3-24 8:59
1.
2003-3-31 21:39
1.
2.
3.? 初始化数据段。通常将此段称为数据段,它包含了程序中需赋初值的变量。例如, C程
序中任何函数之外的说明:
int maxcount = 99;使此变量以初值存放在初始化数据段中。
一般是:全局变量和静态变量
? 非初始化数据段。通常将此段称为b s s段,这一名称来源于早期汇编程序的一个操作符,
意思是“block started by symbol(由符号开始的块)”,在程序开始执行之前,内核将此段初始
化为0。函数外的说明:
long sum[1000] ;
使此变量存放在非初始化数据段中。
? 栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,
其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调
用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈, C函数可以递归
调用。
? 堆。通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位于非初始化数据段顶
和栈底之间。
注:平常所说的堆栈即为栈(stack)
2003-4-2 20:58
1.
2.
2003-4-24 9:44
1.
2.
“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,
因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。
“野指针”的成因主要有两种:
(1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,
它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向
合法的内存。例如
(2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。参见7.5节。
3.
(4)小心不要将"= ="写成"=",编译器不会自动发现这种错误。
(5)不要将123写成0123,后者是八进制的数值。
(6)将自己经常犯的编程错误记录下来,制成表格贴在计算机旁边。
4.
(1)宏定义用大写字母加下划线表示,如MAX_LENGTH;
(2)函数用大写字母开头的单词组合而成,如SetName, GetName ;
(3)指针变量加前缀p,如 *pNode ;
(4)BOOL 变量加前缀b,如 bFlag ;
(5)int
(6)float 变量加前缀f,如 fWidth ;
(7)double变量加前缀d,如 dWidth ;
(8)字符串变量加前缀str,如 strName ;
(9)枚举变量加前缀e,如 eDrawMode ;
(10)类的成员变量加前缀m_,如 m_strName, m_iWidth ;
对于 int, float, double 型的变量,如果变量名的含义十分明显,则不加前缀,避免烦琐。
如用于循环的int型变量 i,j,k ;float 型的三维坐标(x,y,z)等。
2003-5-13 15:12
1.
#include <stdio.h>
main()
{
int a=10;
int b=20;
printf("a is %d b is %d\n",a,b);
swap(&a,&b);
printf("a is %d b is %d\n",a,b);
}
swap(int *a,int *b)
{
int temp;
temp=*a;
*a=*b;
*b=temp;
}
2003-5-22 17:23
1. 在运用宏__FILE__和__LINE__时,要注意,不要写在一个函数里,在其他地方调用此函数
最好写在如下:
#define ERR
sqlca.sqlcode,__FILE__,__LINE__);exit(0);
如果写在函数里,比如:
void err()
{
}
会造成这样一种情况:
2003-5-28 15:30
1.strcpy与memcpy的区别
strcpy(str1,str2)是将str2连带'\0'一同拷贝到str1,比如:str1="aaaa",str2="bbb",
则结果str1="bbb",导致str1碰到'\0',自动终止字符串
memcpy(str1,str2,strlen(str2))是将str2的bbb(不带'\0')拷贝到str1中,结果为
str1="bbba"
这里注意:strlen(str2)=3
则不能用sizeof.
strncpy与memcpy功能一样。
2005-04-12
1.跨平台申请内存
malloc(sizeof(char)*(strlen(str)+1))
char str[]="hello world";
char str1[9]
这两种情况sizeof(str)=12;sizeof(str1)=9;strlen(str)=11;strlen(str1)=0;
char *p=str;
sizeof (p)=4
如果将str[]用作参数例如fun(char str[100])
则sizeof(str)=4而不是100,因为此时字符串数组自动退化为指针。
2.char *str = "ABCD";
str[0]='M';//将导致错误,因为这种方式是在静态存储区分配内存
如果用strcpy(str,"BBBB")会出错,因为char *str没有动态分配内存,而是指向
了静态存储区如果写成:char *str = (char *)malloc(100)
strcpy (str,"ABCD");
str[0] = 'M';即可
char str[4] = "ABC";
str[0]='N';
则str = "NBC";因为是在栈上分配内存
3.new 必须与delete对应,动态内存必须要手工释放,动态内存分配的析构函数只会在使用delete的时候被调用。
4.如果文件中的记录格式为定长,长度为99,则定义char*strBuf[101]//99+'\n'+'\0';fgets(strBuf,101,fp)从fp中读取100个字符包括\r,都作为一个字符串,最后以'\r'结束(fgets中读取字符串的长度是101-1),这样输出到另一个文件的时候fprint(fp1,"%s",strBuf)自动回车,不用在%s后加\n
如果用fread(strBuf,100,1,fp)这样的写法即可。但是之前必须执行
memset(strBuf, '\0',sizeof(strBuf))否则,会出现很乱的字符,而fgets不存在这个问题,不知道为什么。总之,在使用字符串之前,最好memset一下。
无论用fgets还是fread读取的文件的记录结果strlen(strBuf)不是99,而是100。
另外:int fread(void *buf, int size, int count,FILE*stream)表示从文件中读取count(字段数)个字段,每个字段大小为size,函数返回实际读取的字段数。如果函数要求的字段数超过实际文件存放的字段数,举例说明:
文件内容如下:
AAAAAAAAAA
AAAAABBBBB
AAAAA
while(fread(strBuf,11,1,fp))
{
}
如果用fread读文件while(fread(strBuf,11,1,fp))只能读两条出来,前两条返回1,第三条返回0。因为第三条只有6个字节(连着回车符),不满足长度为11的条件,所以fread读文件的话,最实用于定长文件。但是第三条fread也将值赋给了strBuf,只是不满足条件返回了0。第三条的格式:
(1)没有memset(str,'\0',sizefo(str))
[AAAAA(此处一个回车符)
BBBB(此处一个回车符)--这部分是上一个串留下的部分
]
(2)有memset(str,'\0',sizefo(str))
[AAAAA(此处一个回车符)
]
在例如:
AAAAAAAAAA
AAAAA
AAAAAAAAAA
这种情况会跨行截取11个字节,第一条正常,第二条就变为:
[AAAAA(此处是一个回车符)
AAAAA]
如下例所示:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
int main()
{
int i =0;
FILE *fp,*fp1,*fp2;
char str[10]="370706751";
char str1[11];
memset(str1,'\0',sizeof(str1));
fp = fopen("d:\\test.txt", "w");
if (fp == NULL)
{
}
//fwrite("a\0b",3,1,fp);
fprintf(fp, "%s\n\0",str);
//fwrite("a\0b",3,1,fp);
fprintf(fp, "%s\n\0",str);
fclose(fp);
fp1 = fopen("d:\\test.txt", "r");
if (fp1 == NULL)
{
}
fp2 = fopen("d:\\test1.txt", "w");
if (fp2 == NULL)
{
}
while (fgets(str1,11,fp1) )
//while (fread(str1,10,1,fp1))
{
}
fclose(fp1);
fclose(fp2);
printf("record num = [%d]\n",i);
}
2005-06-16
c++中
Object object1 = new Object();
......
delete object1;
object1 = NULL;好的编程习惯,最后置为NULL,防止野指针,因为虽然内存释放了,但是指向一堆垃圾。
2005-06-18:
int main(int argc , char *arge[])
{
char str[]=" aaaaaaaaa\0bbbbb";
char str1[]=" aaaaaaaaa";
if (memcmp(str,str1,17) == 0)
}
从上例可以看出strncmp和memcmp的区别,strncmp会截断字符串str与str1判断,所以输出相等,但是memcmp会将'\0'带入比较所以不相等。
同理,strncpy与memcpy的区别也一样。
int main(int argc , char *arge[])
{
char str[18]=" aaaaaaaaa\0bbbbb";
char str2[18];
strncpy(str2,str,17);
for (int i=0; i<17; i++)
{
}
memcpy(str2,str,17);
for (int i=0; i<17; i++)
{
}
}
memcpy会输出'\0'后面的bbbbb,而strncpy不会输出。