一、C语言中的几个存储区
一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域;未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4、常量区:常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区
例如:
//main.cpp
int a=0; //全局初始化区
char *p1; //全局未初始化区
main()
{
int b; //栈
char s[]="abc"; //栈
char *p2; //栈
char *p3="123456"; //123456\0在常量区,p3在栈上。
static int c=0; //全局(静态)初始化区
p1 = (char*)malloc(10);
p2 = (char*)malloc(20); //分配得来得10和20字节的区域就在堆区。
strcpy(p1,"123456"); //123456\0放在常量区,编译器可能会将它与p3所向"123456"优化成一个地方。
}
二、问题:
使用两个字符串指针,使用memcpy为什么会报段错误?
1、问题
void test(void)
{
char * a = "hello world1";
char * b = "hello world2";
memcpy(a, b, strlen(b) + 1); // 此时会直接段错误
return 0;
}
void main(void){
test();
}
解释:
char *str = "hello world"
//一份拷贝,"hello world"
是常量字符串存在静态数据区,把该字符串常量存在的静态数据区的首地址赋给指针str,所以test()
函数退出时,该字符串常量所在内存不会被回收,故能通过指针访问; 而memcpy
的第一个参数是变量,不能是常量,因此在此时会报错。
2、问题
void test(void)
{
char a[] = "hello world1";
char b[] = "hello world2";
memcpy(a, b, strlen(b) + 1); // 此时程序正常
return 0;
}
void main(void){
test();
}
解释:
str[] = "hello world"//"hello world"
常量字符串在内存中有两份拷贝,一份在动态分配的栈中,一份在静态存储区,str[]
数组为函数内部局部变量,存储在栈上,在test()
函数退出时,栈要清空,局部变量的内存也被清空
3、问题
void c(int n,char *pName)
{
char *a[4] = {"aaa","bbb","ccc","ddd"};
pName = a[n];
}
void main()
{
int n=0;
char *pName = "DB";
c(2,pName);
printf("%s\n",pName); //输出为DB
}
解释:
//输出DB,因为char *pName = “DB”;已经使得pName指向了DB,但c(2,pName);并不能改变pName指向的地址。形象点说就是:我有一个箱子给你用,你可以在里面装东西,但是你不能把我的箱子换成另外一个给我。在这里指的是不能函数调用不能使pName变成函数中的二维数组a。
三、找到个新的博客园作者的分享,比较经典,分享一下:
#include <stdio.h>
//此函数中d也是个局部变量,函数执行完自动销毁,但是指针分配的空间不会被自动回收,除非程序员delete掉。
//所以这个可以正常输出。
char *a()
{
char *d = "ZET";//这个初始化的一种形式,相当于分配了四个空间
return d;
}
//但是第二个数组空间是系统维护的,函数执行完自动销毁
char *b()
{
char p[10] = {"3G平台"};
return p;
}
//参数是值传递方式,改变形参的地址,传递的实参的地址确不会因此改变
void c(int n,char *pName)
{
char *a[4] = {"aaa","bbb","ccc","ddd"};
pName = a[n];
}
void main()
{
int n=0;
char *pName = "DB";
printf("%s\n",a());//输出ZET
printf("%s\n",b());//随机输出乱码
c(2,pName);
printf("%s\n",pName); //输出DB,因为char *pName = "DB";已经使得pName指向了DB,但c(2,pName);并不能改变pName指向的地址。
//形象点说就是:我有一个箱子给你用,你可以在里面装东西,但是你不能把我的箱子换成另外一个给我。
//在这里指的是不能函数调用不能使pName变成函数中的二维数组a。
scanf("%d",&n);
}
问题四
1 #include<stdio.h>
2 #include<string.h>
3
4 int main(void)
5 {
6 //char* buf="1-test"; //换成该行会直接段错误
7 char buf[10]="1-test"; //换成此时是正常的
8 char *temp =NULL;
9 | temp = strtok(buf,"-");
10 | printf("%s\n",temp);
11 return 0;
12 }
1、char* buf="1-test";
//换成该行会直接段错误
2、char buf[10]="1-test";
//换成此时是正常的
第一行的1-test
字符串是在常量区,也就是说该buf的内容是不能进行改变的,而strtok函数的意思是按照截断的字符进行替换,因为字符串是不能进行替换的,也就是说此时的内容必然会报错。
第二行的"1-test"是在堆区,是一个变量,即可以进行替换,因此,此时对字符串的内容进行替换是没有问题的。