zoukankan      html  css  js  c++  java
  • C语言 内存四区与函数调用模型

    C语言提高笔记

    标签(空格分隔): C++ C语言


    day1

    数组做函数参数的退回问题

    数组做函数参数会退回为一个指针, 正确做法:把数组的内存首地址和数组的有效长度传给被调用函数。
    实参的a 和 形参的a 的数据类型本质不一样, 形参中的数组,编译器会把它当成指针处理 只会分配四个字节。
    形参写在函数上,和写在函数内是一样的,只不过是具有对外的属性而已.

    数据类型本质分析

    1. 数据类型可理解为创建变量的模具(模子);是固定内存大小的别名
    2. 数据类型的作用:编译器预算对象(变量)分配的内存空间大小
    3. b &b 数组数据类型 (1定义一个数组类型 2定义一个数组指针 3数组类型和数组指针类型的关系) ====>压死初学者的三座大山

    可以用数组的名字作为指向数组第一个元素的指针。

    void main()
    {
    	int a; //告诉c编译器分配4个字节的内存
    	int b[10] ; //告诉c编译器分配40个自己内存
    
    	printf("b:%d, b+1:%d, &b:%d, &b+1:%d 
    ", b, b+1, &b, &b+1);
    
    	printf("sizeof(b):%d 
    ", sizeof(b));  //40
    	printf("sizeof(a):%d 
     ", sizeof(a)); //4
    	 	
    	// b+1  &b+1 结果不一样  //b &b所代表的数据类型不一样
    	//b 代表的数组首元素的地址
    	//&b代表的是整个数组的地址  
    
    	printf("hello....
    ");
    	system("pause");
    }
    

    void类型
    C语言规定只有相同类型的指针才可以相互赋值
    void*指针作为左值用于“接收”任意类型的指针
    void*指针作为右值赋值给其它指针时需要强制类型转换

    函数也可以看成一种数据类型

    变量的本质

    既能读又能写的内存对象,称为变量;若一旦初始化后不能修改的对象则称为常量

    本质:一段连续内存空间的别名。通过变量来申请和命名内存空间。
    三要素: 名称、大小、作用域

    指针是内存空间的别名。

    程雪内存四区模型

    1. 栈区:由编译器自动分配释放,存放函数的参数值,局部变量的值等;
    2. 堆区:一般由程序员分配释放,若程序员不释放,程序结束时可能由操作系统回收;
    3. 全局区:全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,该区域在程序结束后由操作系统释放;
    4. 常量区:字符串常亮和其他常量的存储位置,程序结束后由操作系统释放;
    5. 程序代码区:存放函数体的二进制代码。
    // 全局区的理解
    char *getStr1()
    {
    // 指针指向谁,将将谁的地址赋给地址
    // 指针变量与它指向的内存空间变量不同
    	char *p1 = "abcdefg2";  //静态变量,全局区
    	return p1;
    // 函数析构后,p1消失,常量"abcdefg2"不被析构
    }
    char *getStr2()
    {
    // 编译器做完词法分析、句法分析、语法分析,然后做优化,若两个常量相同,在全局区中只创建一个
    	char *p2 = "abcdefg2";  //静态变量,全局区
    	return p2;
    }
    void main()
    {
    	char *p1 = NULL;
    	char *p2 = NULL;
    	p1 = getStr1();
    	p2 = getStr2();
    
    	//打印p1 p2 所指向内存空间的数据
    	printf("p1:%s , p2:%s 
    ", p1, p2);
    
    	//打印p1 p2 的值
    	printf("p1:%d , p2:%d 
    ", p1, p2);
    
    	printf("hello...
    ");
    	system("pause");
    	return;
    }
    
    // 堆栈区的理解
    //堆
    char *getMem(int num)
    {
    	char *p1 = NULL;
    	p1 = (char *)malloc(sizeof(char) * num);
    	if (p1 == NULL)
    	{
    		return NULL;
    	}
    	return p1;
    // 函数结束后,p1指向的内存块不会被析构
    }
    
    //栈
    //注意 return不是把内存块 64个字节给return出来,而是把内存块的首地址(内存的标号0xaa11) ,返回给 tmp
    
    // 理解指针的关键,是内存. 没有内存哪里的指针 
    char *getMem2()
    {
    	char buf[64]; //临时变量 栈区存放
    	strcpy(buf, "123456789");
    	//printf("buf:%s
    ", buf);
    	return buf;
    }
    
    void main61()
    {
    	char *tmp = NULL;
    	tmp = getMem(10);
    	if (tmp == NULL)
    	{
    		return ;
    	}
    	strcpy(tmp, "111222"); //向tmp做指向的内存空间中copy数据
    
    	//tmp = getMem2();
    	tmp = 0xaa11;
    
    	printf("hello..tmp:%s.
    ", tmp);
    	system("pause");
    	return ;
    }
    
    void main()
    {
    	int a;
    	int b ;
    
    	char buf[128];  //静态联邦的时候 buf所代表的内存空间的标号 就已经定义下来了
    
    	printf("&a:%d , &b: %d 
    ", &a, &b);
    	system("pause");
    	return ;
    }
    

    在release 和debug模式下,栈的开口方向不同,releas模式向上,debug向下,一般认为栈开口向下,不管栈开口向上还是向下,静态联邦的时候 buf所代表的内存空间的标号 就已经定义下来了。

  • 相关阅读:
    Windows性能调优: Perfomn.exe 和Perfmon /res
    WPF:逻辑树和视觉树
    AD FS 概述
    SQL Server : TRUSTWORTHY 数据库属性
    WCF:在开发期间应该注意的问题
    ASP.NET MVC 2中的数据验证
    SQL Server:如何在Service Broker发送消息验证失败后获取源消息
    GDI+:自定义控件时如何使用Region来输出特定区域
    LINQ to XML:如何替换XCData的内容
    javascript是否真的就不能实现跨站请求呢?
  • 原文地址:https://www.cnblogs.com/zi-wang/p/8503994.html
Copyright © 2011-2022 走看看