网站建设哪家公司便宜,广东深圳快递能发货吗,邢台网上房地产,广西网站建设开发团队并非从0开始的c之旅 day2一、变量1、 变量名的本质二、程序的内存分区模型1、内存分区运行之前运行之后三、栈区注意事项四、堆区1、堆区使用2、堆区注意事项五、全局变量静态变量1、静态变量2、全局变量六、常量1、全局const常量2、局部const常量七、字符串常量一、变量
既能…
并非从0开始的c之旅 day2一、变量1、 变量名的本质二、程序的内存分区模型1、内存分区运行之前运行之后三、栈区注意事项四、堆区1、堆区使用2、堆区注意事项五、全局变量静态变量1、静态变量2、全局变量六、常量1、全局const常量2、局部const常量七、字符串常量一、变量
既能读又能写的内存对象成为变量 若一旦初始化后不能修改的对象则称为常量。
1、 变量名的本质 变量名的本质一段连续内存空间的别名 程序通过变量来申请和命名内存空间 int a 0; 通过变量名访问内存空间 不是向变量名读写数据而是向变量所代表的内存空间中读写数据 变量修改方式直接修改、间接修改
void test01() {int a 10;///1、直接修改a 20;//2、间接修改int* p a;//*p 解引用*p 200;printf(a %d\n, a);
}自定义数据类型练习c
struct Person {char a;int b;char c;int d;
};void test02()
{struct Person p1 { a,10,b,20 };//直接修改d属性p1.d 100;printf(%d\n, p1.d);//间接修改d属性struct Person* p p1;p-d 200;printf(%d\n, p1.d);
}这种间接修改太简单了我们还要学一种 若使指针p加一p指针会跳过一个结构体的字节数 其中char类型占1个字节int类型占4个字节a占用0~3b从4开始系统用内存对齐的方式让a也占用了虽然他不使用但是在b之前的字节 结果使用以下代码验证
printf(%d\n, p);
printf(%d\n, p1);可得两值差16同理我们可以用这种方式修改变量 char* p p1;printf(%d\n, *(int*)(p 12));printf(%d\n, *(int*)((int*)p 3));将p指针类型改为char *型这时增加p指针的值会使其会一个一个往前走在输出时因为我们需要的是4个字节的int类型的内容故需要做强制类型转换将其转换为int *类型 同理如上面的第三行代码
二、程序的内存分区模型
1、内存分区
运行之前
可以简单先分为代码区和数据区 程序执行过程 1预处理宏定义展开、头文件展开、条件编译这里并不会检查语法 2编译检查语法将预处理后文件编译生成汇编文件 3汇编将汇编文件生成目标文件二进制文件 4链接将目标文件链接为可执行程序 当我们编译完成生成可执行文件之后我们可以通过linux下size命令可以查看一个可执行文件基本情况 执行size命令后系统会给出一下几个数据: text 代码区 data 静态数据/全局初始化数据区 bss 未初始化初始化数据区 dec 文件十进制总和 hex 文件十六进制总和 filename 文件名 通过以上数据可知在没有运行程序前也就是说程序没有加载到内存前可执行程序内部已经分好了3段信息分别为代码区、数据区和未初始化数据区3个部分有些人直接把data和bss合起来叫做静态区或全局区
代码区存放CPU执行的机器指令。通常代码区是可共享的即另外的执行程序可以调用它使其可共享的目的是对于频繁被执行的程序只需要在内存中有一份代码即可。代码区通常是只读的使其只读的原因是防止程序意外地修改了他的指令。另外代码区还规划了局部变量的相关信息全局初始化数据区\静态数据区该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量包括全局静态变量和t和常量数据如字符串常量未初始化数据区又叫bss区存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为0或空NULL
运行之后
相比运行之前多出了栈区和堆区
栈区stack栈是一种先进后出的内存结构由编译器自动分配释放存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放因此局部变量的生存周期为申请到释放该段栈空间堆区heap堆是一个大容器它的容量要远远大于栈但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放若程序员不释放程序结束时由操作系统回收。
栈区先进后出编译器管理数据开辟释放容量有限不要将大量数据开辟到栈区 堆区容量远远大于栈区程序员手动开辟数据手动释放数据
三、栈区注意事项
int* fun() {int a 10;//栈上创建的变量return a;
}void test01() {int* p fun();//结果依然不重要因为上面的a早已被释放再去操作这块数据属于非法操作printf(%d\n, *p);printf(%d\n, *p);
}栈上的数据在执行完方法就释放了第一次printf能打出正确结果其实是因为编译器认为你可能会处理错了数据故保留一次第二次打印时结果才是正常的已经是一个随机数了。
char* GetString() {char str[] hello world;return str;
}void test02() {char* p NULL;p GetString();printf(p %s\n, p);
}上述代码中将p打印后的结果为字符型乱码因为str是指向字符串的指针但字符串在函数运行结束后自动释放了故他指向的地址已经为乱码打印出来也为乱码
这里的hello world原本在常量区无法修改被复制了一份到栈区 两段代码告诉我们栈上的数据出了函数体后就不要再使用了 也即不要返回局部变量的地址局部变量在函数体执行完毕后会被释放再次操作就是非法操作结果未知 四、堆区
1、堆区使用
int* getSpace() {int * p malloc(sizeof(int) * 5);if (p NULL)return NULL;for (int i 0; i 5; i) {p[i] 100 i;}return p;
}void test01() {int* p getSpace();for (int i 0; i 5; i)printf(%d\n, p[i]);//释放数据free(p);p NULL;
} 使用malloc创建的空间在堆区printf多少次结果都一样创建后如果没有使用就是垃圾需要free 防止free后的指针仍指向原来那块已经被释放的空间称为野指针我们需要把它设成NULL 2、堆区注意事项
void allocateSpace(char* p) {char* temp malloc(100);memset(temp, 0, 100);strcpy(temp, hello world);p temp;}void test02() {char* p NULL;allocateSpace(p);printf(p %s\n, p);
}打印出来的结果为NULL因为test中的p和函数的参数p属于同级指针test的p为NULL时同级指针无法改变他的值若要改需要更高级的二级指针。 修改为以下代码
void allocateSpace2(char** p) {char* temp malloc(100);memset(temp, 0, 100);strcpy(temp, hello world);*p temp;}void test03() {char* p2 NULL;allocateSpace2(p2);printf(p2 %s\n, p2);
}p一开始为NULL参数p的值为传入p的地址temp为堆区开辟的地址解参数p后赋值temp也就是将temp所含的地址赋予p这样p就能直接指向temp所指向的堆 如果给主调函数中一个空指针分配内存在被调函数中利用同级指针是分配失败的 解决方式利用高级指针修饰低级指针 五、全局变量静态变量
1、静态变量 生命周期在程序运行结束时死亡 在程序运行前就分配内存 默认属于内部链接属性在当前文件中使用 static int a 10;//全局作用域void test01() {static int b 19;//局部作用域
}默认内部链接属性在文件外是访问不到的如在同一个项目的另一个类中就访问不到这里的a
2、全局变量 默认在c语言下全局变量前加了关键字 extern 属于外部链接属性 extern int g_b;g_b 100;若在其他类中写了一个全局变量想在main函数中引用需要有第一行这个代码 这个代码就是告诉编译器其他文件中有这么一个变量链接时要去其他文件中寻找 但是要是找不到这个全局变量就会报错报错为1个无法解析的外部命令该报错是在链接阶段报的
六、常量
1、全局const常量
//const 修饰的常量
const int a 10;//全局const常量void test01() {//直接修改失败//a 100;//间接修改int* p a;*p 100;printf(%d\n, a);
}如果直接修改则a会有红线显示语法错误 如果间接修改vs2019会报错显示写入访问权限冲突 全局const常量放在常量区中受到常量区的保护
2、局部const常量
void test02() {const int b 10;//直接修改失败//b 100;//间接修改int* p a;*p 100;printf(%d\n, b);
}直接修改依然语法不行 间接修改正常运行因为局部常量放在栈区我们称之为伪常量 伪常量不可以初始化数组
七、字符串常量
void test03() {char* p1 hello world;char* p2 hello world;char* p3 hello world;printf(%d\n, p1);printf(%d\n, p2);printf(%d\n, p3);printf(%d\n, hello world);
}由上述代码结果可知用字符串指针指向字符串常量得到的地址都是一样的可知字符串常量是可以共享的
void test04() {char* p1 hello world;p1[0] w;printf(%d\n, p1);
}运行后会报错字符串常量是不能修改的虽然ANSIC中规定是不定义的但vs是不能修改的 vs会把多个相同的字符串常量看成一个