5.1.0 内存四区模型 操作系统把物理硬盘代码 load 到内存。 操作系统把C代码分成四个区。 操作系统找到Main函数入口执行。
内存四区 一个由C/C++编译的程序占用的内存分成以下几个部分: 栈区(stack):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。只有汇编语言可以操控栈区。
堆区(heap):一般由程序员分配释放,若程序员不释放,程序结束时可能操作系统回收。 注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
数据区:主要包括静态全局区和常量区,如果要站在汇编的角度细分的话,还可以分为很多小区。
全局区(静态区)(static):全局变量和静态变量的存储放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。常量区也存在于全局区(他们统称)。
常量区:常量字符串就是放在这里的,程序结束后由系统释放。
代码区:存放函数体的二进制代码。由操作系统管理,函数指针最强大的地方就是可以操作代码区的数据。
全局区详解 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <Windows.h> char * pMem1 () ; char * pMem2 () ;char * pMem3 () ;void main (void ) {char * p1 = NULL ; char * p2 = NULL ;char * p3 = NULL ;p1 = pMem1(); p2 = pMem2(); p3 = pMem3(); printf ("p1 = %s p1 = %p\n" , p1, p1);printf ("p2 = %s p2 = %p\n" , p2, p2);printf ("p3 = %s p3 = %p\n" , p3, p3);system("pause" ); } char * pMem1 () {char * p1 = "Hello World" ; return p1; } char * pMem2 () {char * p2 = "Hello World" ;return p2;} char * pMem3 () {char * p3 = "Hello Arvin" ;return p3;}
总结: 全局区相同字符串常量合并。 注意三点: 栈区调用完毕就销毁了。 指针指向谁就把谁的地址赋值给指针。 %s 的意思就是打印地址所指向的内容。
栈区详解 #include <stdlib.h> #include <stdio.h> char * p_str () ;void main () {char * p = NULL ;p = p_str(); printf ("P = %s\n" , p); printf ("\n" );system("pause" ); } char * p_str () {char str[100 ] = "Hello" ; printf ("str = %s\n" ,str);return str;}
内存栈区调试
#include <stdio.h> #include <stdlib.h> char * p_str2 () ;void main (void ) {char * p = NULL ;p = p_str2(); printf ("p = %s\n" , p);printf ("p = %p\n" , p);printf ("\n" );system("pause" ); } char * p_str2 () {char str[100 ] = "Hello" ;printf ("str = %s\n" , str);printf ("str = %p\n" , str);return str;}
所以在栈上开辟一个数组是很危险的,所以我们一般在堆上开辟数组。堆是由我们控制的所以在使用完成后要手动释放。
堆区详解 strcpy 函数的作用 原型声明:charstrcpy(char dest,const char* src); 功能:把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间。 说明:stc和dest所指内存区域不可以重叠,且dest必须有足够的空间来容纳src的字符串。 返回指向dest的指针。
堆区不会自动释放需要手动释放。 Free 的意思不是清空内存,而是解除这个指针的绑定关系(解除和这块内存的绑定关系),原先只有指针自己能调用,(取消绑定后)现在谁都可以用。
#include <stdio.h> #include <stdlib.h> char * phead () ;void main (void ) {char * p = NULL ;p = phead(); printf ("p = %p\n" , p);printf ("p = %s\n" , p);if (p != NULL ) { printf ("%s\n" , p);free (p); p = NULL ; } system("pause" ); } char * phead () {char * tmp = (char *)malloc (100 ); if (tmp == NULL ) { return NULL ;} strcpy (tmp, "Hello" ); printf ("tmp = %p\n" , tmp);printf ("tmp = %s\n" , tmp);return tmp;}
5.1.2 指针运算(重点) 指针的加减法,它是处理多级指针包括数组的时候非常重要。它决定了你在指针这里能走多远。严格来说,指针很少有人走到头。至少要掌握二级指针到三级指针(否则你将很难明白C++语言中的引用)。 void *p是一个万能指针。
指针的数据类型决定了指针的解析方式,同时也决定了指针的步长。 指针+1等于指针加上所指向类型的大小。 编译器对指针+1的操作:p+sizeof(p) 1;
#include <stdio.h> #include <stdlib.h> void main () {int num = 10 ;int * p = #printf ("%p %p\n" , p, p + 1 ); printf ("%d %d\n" , p, p + 1 ); char str = 'a' ;char * p_str = &str;printf ("%p %p\n" , p_str, p_str + 1 ); printf ("%d %d\n" , p_str, p_str + 1 );printf ("\n" );system("pause" ); }
指针与数组练习 指针也是一种数据类型,指针的数据类型是指它所指向的内存空间的数据类型。 指针的空间和指针所指向的内存空间是不同概念。 下面实例比较复杂建议多看内存和多思考。
#include <stdio.h> #include <stdlib.h> void main () {char str[] = "Hello" ; for (char * p_str = str; p_str < str + 3 ; p_str++) {printf ("%c" , *p_str);} printf ("\n" );system("pause" ); }
指针的间接赋值 如果想通过形参改变实参的内存内容(值)必须地址传递。 指针间接赋值的三个条件 两个变量、建立关系、通过*操作内存
#include <stdio.h> #include <stdlib.h> int num (int a) ;void pnum (int *a) ;void main () {int a = 20 ;printf ("main a = %p\n" , &a);int b = num(a); printf ("b = %d\n" , b);int c = 100 ;pnum(&c); printf ("c = %d\n" , c);printf ("\n" );system("pause" ); } int num (int a) {a = 10 ; printf ("num a = %p\n" , &a);return a;} void pnum (int * a) {*a = 10 ; }
指针数组 指针数组实际上就是指针类型的数组。 当然还有一种叫数组指针,实际上就是指向数组的指针。 注意:指针数组是指针类型的数组,数组指针是指向数组的指针
#include <stdio.h> #include <stdlib.h> void main (void ) {int a = 1 ;int b = 2 ;int c = 3 ;int d[4 ] = { 1 ,2 ,3 ,4 };for (int i = 0 ; i < 4 ; i++) {printf ("%d\n" , d[i]);} char *pa[] = { "a" ,"b" ,"c" }; for (int i = 0 ; i < 3 ; i++) { printf ("%s\n" , pa[i]); } printf ("\n" );system("pause" ); }
5.1.3 结构体基本操作 结构体类型定义 结构体变量定义 结构体变量初始化 Typedef 改变类型名 注意:结构体也是一种数据类型,是我们自定义的类型。
#include <stdio.h> #include <stdlib.h> struct Test {char face[10 ];int EyesSize;}; struct Test test = { "Hello" ,20 }; struct Test02 {char face[10 ];int EyesSize;}test02 = {"World" ,15 }; struct {char face[10 ];int EyesSize;}test03,test04; typedef struct Test05 {char face[10 ];int EyesSize;}Test05; Test05 test05 = { "Love" ,520 }; void main () {printf ("\n" );system("pause" ); }
结构体初始化 注意: Test结构体它声明的时候是不开辟内存的,你必须初始化对象才开辟内存。 箭头运算符 :用于指针来调用变量的一种方式。 test.face = { “Hello” }; 这样的化会出错,它不是指针(不能往指针的地址里拷贝字符串)。所以我们要用到Strcpy函数。
#include <stdio.h> #include <stdlib.h> typedef struct Test {char face[10 ];int EyesSize;char * str;}Test; void main (void ) {Test test; strcpy (test.face,"Hello" );test.str = "World" ; test.EyesSize = 15 ; for (char * p = test.str; p < test.str + 6 ; p++) {printf ("%c" , *p);} Test test02 = { "Hello" ,15 ,"Arvin" }; printf ("%s %d\n" , test.face, test.EyesSize);printf ("%s %d %s\n" , test02.face, test02.EyesSize, test02.str);Test test01; Test* p_test = NULL ; p_test = &test01; strcpy (p_test->face, "Hello" );p_test->EyesSize = 100 ; p_test->str = "World" ; printf ("%s %d %s\n" , p_test->face, p_test->EyesSize, p_test->str);printf ("\n" );system("pause" ); }
如果你在p_test->EyesSize = 100;非要用*的方式也不是不可以: (*p_test).EyesSize = 100;
结构体变量之间赋值 #include <stdio.h> #include <stdlib.h> typedef struct ModelFace {char face[10 ];int EyesSize;}ModelFace; void main56 (void ) {ModelFace modelFace = { "Hello" ,50 }; ModelFace modeF = modelFace; printf ("%s %d\n" , modeF.face, modeF.EyesSize);printf ("\n" );system("pause" ); }
结构体静态数组-栈区 #include <stdio.h> #include <stdlib.h> typedef struct ModelFace {char face[10 ];int EyesSize;}ModelFace; void main () {int a[3 ] = { 1 ,2 ,3 };ModelFace modelFace[3 ] = { "Gun" ,100 ,"Liyun" ,50 ,"Note" ,10 }; ModelFace modelF[3 ] = { {"Fun" ,100 },{"Miun" ,50 },{"Mote" ,10 } }; for (int i = 0 ; i < 3 ; i++) {printf ("ModeFace:%d %s %d\n" , i, modelFace[i].face, modelFace[i].EyesSize);printf ("ModeF :%d %s %d\n" , i, modelF[i].face, modelF[i].EyesSize);} printf ("\n" );system("pause" ); }
结构体动态数组-堆区 #include <stdio.h> #include <stdlib.h> struct ModelFace {char face[10 ];int EyesSize;}; void main () {int a[3 ] = { 0 };int * pa = (int *)malloc (3 * sizeof (int )); free (pa);pa = NULL ; struct ModelFace modelFace [3] = { 0 };struct ModelFace * p_str = (struct ModelFace*)malloc (3 * sizeof (struct ModelFace));if (p_str == NULL ) {return NULL ;} for (int i = 0 ; i < 3 ; i++) {strcpy (p_str[i].face, "Arvin" );p_str[i].EyesSize = 10 + i; } for (int i = 0 ; i < 3 ; i++) {printf ("modelFace %d :%s %d\n" , i, p_str[i].face, p_str[i].EyesSize);} if (p_str != NULL ) {free (p_str);p_str = NULL ; } printf ("\n" );system("pause" ); }
结构体嵌套数组 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> typedef struct ModelFace {char * face;int EyesSize;}ModelFace; void main59 (void ) {printf ("请输入您的姓名:" );char Mname[10 ] = { 0 };int * age = 0 ;scanf ("%s" ,Mname);char * name = NULL ;name = (char *)malloc (10 ); strcpy (name, Mname);printf ("name:%s\n" , name);if (name != NULL ) {free (name);name = NULL ; } ModelFace modelFace; modelFace.face = (char *)malloc (10 ); strcpy (modelFace.face, Mname);modelFace.EyesSize = 18 ; printf ("ModeFace:name:%s age:%d" , modelFace.face, modelFace.EyesSize);if (modelFace.face != NULL ) {free (modelFace.face);modelFace.face = NULL ; } printf ("\n" );system("pause" ); }
结构体赋值细节 在程序流程时如果你不知道大小可以采用strlen函数
for (int i = 0 ; i < strlen (Name); i++) { pMyStruct->hello[i] = Name[i]; }
结构体赋值第一部分
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> typedef struct MyStruct {char hello[10 ];int a; }MyStruct; void main () {int a;printf ("a:%d\n" , sizeof (a)); MyStruct myStruct; printf ("myStruct:%d\n" , sizeof (myStruct)); strcpy (myStruct.hello, "A" );printf ("myStruct.hello:%d\n" , sizeof (myStruct.hello));printf ("myStruct.a:%d\n" , sizeof (myStruct.a)); MyStruct* pMyStruct = &myStruct; printf ("*pMyStruct:%d\n" , sizeof (*pMyStruct)); printf ("pMystruct:%d\n" , sizeof (pMyStruct)); strcpy (pMyStruct->hello, "Hello" );pMyStruct->a = 50 ; printf ("pMystruct:%s %d\n" , pMyStruct->hello, pMyStruct->a);char name[10 ];printf ("输入你要的pMyStruct的值:" );scanf ("%s" , name);strcpy (pMyStruct->hello, name);printf ("pMyStruct:%s %d\n" , pMyStruct->hello, pMyStruct->a);char Name[10 ] = { 'H' ,'e' ,'l' ,'l' ,'o' };for (int i = 0 ; i < strlen (Name); i++) { pMyStruct->hello[i] = Name[i]; } printf ("pMyStruct:%s %d\n" , pMyStruct->hello, pMyStruct->a);char * pName = "ZhangSan" ;if (pMyStruct == NULL ) {return NULL ;} for (int i = 0 ; i < 9 ; i++) {pMyStruct->hello[i] = pName[i]; } printf ("pMyStruct:%s %d\n" , pMyStruct->hello, pMyStruct->a);pName = "LiSi" ; for (pMyStruct = pName; pMyStruct < pName + 5 ; pMyStruct++) {printf ("pMyStruct: %s\n" , pMyStruct->hello);} printf ("\n" );system("pause" ); }
C语言赋值细节二 #include <stdio.h> #include <stdlib.h> MyStruct* Test(); typedef struct MyStruct { char* Hello; }MyStruct; void main(void) { MyStruct myStruct; MyStruct* pMyStruct = &myStruct; /** * 字符串是不动的常量,在内存中是以下的表现: * const char* H = "Arvin" * 它在全局区是不可更改的,我们通过指针地址将它赋值给pMySruct,说白了就是地址的转移。 */ char* H = "Arvin"; pMyStruct->Hello = H; /** * I是在栈区的数组。 * pMyStruct是在Main函数中的所以下面不会出问题。 * 如果在外部函数中这样,需要讲值传递置堆区。 * 栈区是先进后出的模型运行的,Main函数先进,所以当Test进入,出去时候将值传给了Main还是可以正常运行的。 * 此处涉及主调函数、被调函数、函数调用模型有关系。 */ char I[10] = { '1','2','3','4','5'}; pMyStruct->Hello = I; printf("%s\n", pMyStruct->Hello); printf("\n"); system("pause"); }
C语言实现界面 #include <stdio.h> #include <stdlib.h> void main (void ) {puts ("\n" );puts ("\n" );puts ("\t\t ---------------------------" );puts ("\t\t| Leve1:1 |" );puts ("\t\t| Leve1:2 |" );puts ("\t\t| Leve1:3 |" );puts ("\t\t| Leve1:4 |" );puts ("\t\t| Leve1:5 |" );puts ("\t\t ---------------------------" );printf ("\n" );system("pause" ); }
Dos窗口的大小为:宽80字符高25字符 一个\t的是8个字符。
puts和printf的区别和putchar的区别 printf、pytchar和puts函数均为输出函数。printf函数可以输出各种不同类型的数据,putchar函数只能输出字符数据,而puts函数可输出字符串数据。 puts(s)的作用与语句printf(“%s”,s)的作用基本相同,puts函数只能输出字符串,不能输出1数值或进行格式变换,puts函数在输出字符它自带换行。
结构体保存数据 C语言没有类的概念,我们做一些属性的时候,只能是往结构体里保存。
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> typedef struct Info {char Name[10 ];char Gender[10 ];double Height;unsigned short Age;}Info; void main (void ) {Info info = { 0 ,0 ,0 ,0 }; Info* pInfo = &info; printf ("请输入您的姓名:" );scanf ("%s" , pInfo->Name);printf ("您的姓名是:%s\n" , pInfo->Name);printf ("请输入你的性别:" );scanf ("%s" , pInfo->Gender);printf ("您的性别为:%s\n" , pInfo->Gender);if ((!strcmp (pInfo->Gender,"男" )) || (!strcmp (pInfo->Gender,"男生" ))) { printf ("请输入您的身高:" );scanf ("%lf" , &pInfo->Height); printf ("您的身高为:%g\n" , pInfo->Height);printf ("请输入您的年龄:" );scanf ("%u" , &pInfo->Age);printf ("你的年龄为:%u\n" , pInfo->Age);} printf ("\n" );system("pause" ); }
strcmp 的作用 比较字符串,0代表两个相同,非0代表两个字符串不相同。