指针与内存, 指针数组,常量指针类笔记
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。
char *str1;
char str2[10]="dfdf";
str1=str2;
//将指针str1指向以str2为首地址的内存空间,即现在str2和str1表示内存中的同一区域.
4。char *str3="aaaaaaaaaaaaaaa";
char * str2="bbbb";
strcpy(str3,str2);
//这时的str2和str3就不指向同一内存空间,因为str3在初始化时已经分配了指向的内存空间;此时只是将str2所指向的内存的内容拷贝到str3所指向的内存空间的内容(注意:str3指向的内存空间的大小最好大于str2所指向的内存空间的大小,否则,可能将其他变量的内存覆盖。另外,c语言对数组不做越界检查,使用时候小心,否则出现不可预料的错误)。
5。其它如int,double,float,short等类型,在申明变量时内存空间就已经分配好了。例如:
int i=1;
int j;
j=i;
j=2;
printf("i=[%d];j=[%d]\n",i,j);
输出结果为i=1;j=2
7.图例:
char*str1="abcd",*str2="cde"; //"abcd"的首地址为2000,"cde"的首地址为2004,分别存放在str1和str2.
address(int类型)address(int类型)strings or object
------ ------
str1 |2000| -----------------2000| |:memory1
------ ...
str2 |2004| -----------------2004| |:memory2
------ ...
| | 2007| |
------ ------
| | | |
------ ------
(1) str1=str2; //str1的值变为2004,指向了memory2
(2) strcpy(str1,str2);//将memory2的内容"cde"拷贝到memory1,str1仍指向memory1
kdsj\0fkdjfkdj\0fkdjfkjdfk\0
8.总结:
内存的地址是固定的,而地址所指向的内存中的内容是变化的
指针是存放地址的变量;
strcpy是内存区域中的内容的拷贝,而内存的地址是固定不变的。
字符数组char str[10];与char *str;的区别:
①,字符数组在变量声明时即已分配了内存空间,也就是说他的地址已经确定了,而字
符指针没有指向内存空间(除了char *str="dfdf")。
②,字符数组中str表示数组的首地址,是一个常量,str等于一个固定的内存地址,所
以永远不能在等号左边被赋值(除了声明变量初始化时,char str[10]="sssssss"),
只能利用strcpy来改变他所指向的内存空间的内容。
③,而char *str中str是一个变量,可以指向任意内存空间;str=str2只表示将str指向
地址str2指向的内存空间;他同样必须用strcpy来改变str所指向的内存区域的内容。
注意:指针仅仅起指向的作用!!
最常见的例子是:
fgets(str,1000,fp);//预先定义str为字符数组char str[1000],
因为不确定文件的一行有多长
fetch into :str;//同上
9. C语言函数中的局部变量的空间一般都是放在堆栈里面.在进入函数前,通过"SUB SP,+XX"来为这些局部变量分配堆栈空间.然后同样通过BP来对这些局部变量进行访问.函数结束时,"MOVSP,BP"还原堆栈指针,局部变量随之而消失.最后以"POP BP"还原BP,结束该函数.
值得注意的是,C语言会自动为C函数中经常使用int类型变量设置成resigter int.这样的局部变量就不是使用堆栈空间的了,而就是直接使用SI寄存器.
10. char str[10];
struct aa bb;
........
memset(str,'\0',sizeof(str));//清空内存,赋值为10个'\0'字符
memset(&bb,0,sizeof(struct aa));
从文件中逐行读取,可以处理完一行,memset一下,再读取下一行
11.注意:char *str1;
str1=(char *)malloc(sizeof(char) * num)
给指针分配他所指向的内存空间的大小,sizeof函数是取得变量类型或者变量所占用
的内存字节数。例如:
sizeof(int)//结果为4(个字节)
sizeof(double)//结果为8(个字节)
charstr[]="qqqqqq";sizeof(str)//结果为7(个字节),不要漏掉'\0'!!!
char *str="qqqqqq";sizeof(str)//结果为4(个字节)(32为操作系统),因为str是指针变量!!!
注意:指针变量(存放地址的变量)的sizeof值一般都为4,不要将所占用内存大小和字符串长度混淆!!!
例如:
char str[10]={'a','b','c'};
strlen(str)//值为3
sizeof(str)//值为10
sizeof(char)* strlen(str)//值为3
C语言中数组定义几位,元素的最大个数就是几位。例如char str[10];元素的最大个数
为10,只不过字符数组自动在最末尾加'\0',所以定义字符数组的时候要小心,你想定
义一个有2个字符的数组,就要char str[3];,留出一位给'\0',strlen为2,sizeof为
3!!!!
12. return语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被
自动销毁。但是可以return一个在“栈内存”的整型变量,因为返回的是内存中的值。
例如
char * Func(void)
{
char str[] = “helloworld”; // str的内存位于栈上
.......
return str; //函数结束时候str为首地址的内存即刻释放,只是返回一个内存的地址
}
main( )
{ char *str1;
str1=Func;
printf("%s\n",str);//将导致错误
13。内存分配方式有三种:
(1)静态内存分配。在内存的数据区上创建。内存在程序编译的时候就已经分配好。
这块内存在程序的整个运行期间都存在。例如 已经初始化的变量,全局变量,static变量。
(2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数
执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率
很高,但是分配的内存容量有限。也属于动态内存分配
(3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的.
静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.
栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。
静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.
7.4指针参数是如何传递内存的?
如果函数的参数是一个指针,不要指望用该指针去申请动态内存。示例7-4-1中,Test函数的语句GetMemory(str,200)并没有使str获得期望的内存,str依旧是NULL,为什么?
voidGetMemory(char *p, int num)
{
p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{
char *str = NULL;
GetMemory(str, 100); // str 仍然为 NULL
strcpy(str, "hello"); // 运行错误
}
示例7-4-1 试图用指针参数申请动态内存
毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p =p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。
如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见示例7-4-2。
voidGetMemory2(char **p, int num)
{
*p = (char *)malloc(sizeof(char) * num);
}
voidTest2(void)
{
char *str = NULL;
GetMemory2(&str, 100); // 注意参数是 &str,而不是str
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
示例7-4-2用指向指针的指针申请动态内存
由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。这种方法更加简单,见示例7-4-3。
char*GetMemory3(int num)
{
char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}
示例7-4-3用函数返回值来传递动态内存
用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return语句用错了。这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡,见示例7-4-4。
char *GetString(void)
{
char p[] = "hello world";
return p; // 编译器将提出警告
}
voidTest4(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)
{
char *p = "hello world";
return p;
}
voidTest5(void)
{
char *str = NULL;
str = GetString2();
cout<< str << endl;
}
示例7-4-5 return语句返回常量字符串
函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。
14.函数参数的传递分为:值传送和引用(指针)传送。前者将变量的复本传给函数的形参,形参的改变不会引起变量原值得改变;后者将变量的地址传给形参,形参的改变将引起变量的改变。
1. int fflush(FILE *stream);
清除输入流的缓冲区,使它仍然打开,并把输出流的缓冲区的内容写入它所联系的文件中。成功时返回0,出错时返回EOF。
例如:fflush(stdout);//刷新屏幕输出
FILE *fp;
fputs(str,fp);
fflush(fp);//马上写入文件中
再次重申:如果函数的参数是指针,千万不要用该指针申请动态内存。
exit退出整个程序;return退出所在的函数
2. char *str="abc";
printf("%d\n",str);//打印地址
printf("%s\n",str);//打印字符串
3.
初始化数据段。通常将此段称为数据段,它包含了程序中需赋初值的变量。例如, C程序中任何函数之外的说明:int maxcount = 99;使此变量以初值存放在初始化数据段中。一般是:全局变量和静态变量.
非初始化数据段。通常将此段称为bss段,这一名称来源于早期汇编程序的一个操作符,意思是“block started by symbol(由符号开始的块)”,在程序开始执行之前,内核将此段初始化为0。函数外的说明:long sum[1000] ;使此变量存放在非初始化数据段中。
栈。自动变量以及每次函数调用时所需保存的信息都存放在此段中。每次函数调用时,
其返回地址、以及调用者的环境信息(例如某些机器寄存器)都存放在栈中。然后,新被调
用的函数在栈上为其自动和临时变量分配存储空间。通过以这种方式使用栈, C函数可以递归调用。
堆。通常在堆中进行动态存储分配。由于历史上形成的惯例,堆位于非初始化数据段顶
和栈底之间。
注:平常所说的堆栈即为栈(stack)
1. char *str1="abcd";
//此时分配的内存既不在栈中,也不在堆中,而是在静态存储区域。
char str2[]="dfdfdf";
//这是的内存在栈中
2. 在用delete或用free释放指针p所指的内存后,只是p所指的内存释放了。变量p还是存在的。应该马上显式地将p置为NULL,以防下次使用p时发生错误。示例程序如下:if(p==NULL);strcpy(p,"dkfjdf");// //p是个野指针,指向了一堆垃圾
“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,
因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。
“野指针”的成因主要有两种:
(1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如
char *p = NULL;
char *str = (char *) malloc(100);
(2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
3. 不要将BOOL值TRUE和FALSE对应于1和0进行编程。大多数编程语言将FALSE定义为0,任何非0值都是TRUE。
1. 传址参数,交换a,b的值必须用传址。另外还有传值,传引用。
#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;
}
1. 在运用宏__FILE__和__LINE__时,要注意,不要写在一个函数里,在其他地方调用此函数,最好写在如下:
#defineERR printf("error:[%d]file:[%s]line:[%d]\n",\
sqlca.sqlcode,__FILE__,__LINE__);exit(0);
如果写在函数里,比如:
void err()
{
printf("error:[%d]file:[%s]line:[%d]\n",sqlca.sqlcode,__FILE__,__LINE__);
}
会造成这样一种情况:
__LINE__的值永远是此函数中printf语句所在的行数
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(str2)=4 所以如果不想覆盖str1中其余的字符,
则不能用sizeof.
1.跨平台申请内存
malloc(sizeof(char)*(strlen(str)+1))
charstr[]="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,strcmp,memcmp区别
int main(intargc , char *arge[])
{
char str[]=" aaaaaaaaa\0bbbbb";
char str1[]=" aaaaaaaaa";
if(memcmp(str,str1,17) == 0)
printf("xiangdeng\n");
if(strncmp(str,str1,17) == 0)
printf("--xiangdeng\n");
}
从上例可以看出strncmp和memcmp的区别,strncmp会截断字符串str与str1判断,所以输出相等,但是memcmp会将'\0'带入比较所以不相等。
同理,strncpy与memcpy的区别也一样。
int main(intargc , char *arge[])
{
char str[18]=" aaaaaaaaa\0bbbbb";
char str2[18];
strncpy(str2,str,17);
for (int i=0; i<17; i++)
{
printf("str2[%d]=[%c]\n", i, str2[i]);
}
memcpy(str2,str,17);
for (int i=0; i<17; i++)
{
printf("str2[%d]=[%c]\n", i, str2[i]);
}
}
memcpy会输出'\0'后面的bbbbb,而strncpy不会输出。