首页 > 代码库 > 【学习ios之路:C语言】存储区内存划分

【学习ios之路:C语言】存储区内存划分

     一. 内存存储区的划分:

      1.栈区:栈区主要存放函数内部定义的变量,数组.函数调用时,开辟空间,函数执行完毕,回收空间,空间的开辟与回收有系统管理.

      2.堆区:堆区最大的特点:空间的开辟与释放有开发人员手动管理.

      3.全局区静态区:主要存放函数外部定义的全局变量以及静态变量,空间一旦开辟,就不会回收.直到应用程序执行结束.

      4.常量区:存储常量.1.整形常量.2.浮点型常量.3,字符串常量.4.字符串常量.

      5.代码区:存放程序编译之后生成的cpu指令.

  

  二.malloc,在堆区开辟空间.

    
    //在堆区开辟空间.
    //void * 泛型:可以代表所有的指针类型.
    //如果要存储两个整数,用int * ,存储8个字符,用char * ,存储4个人整数,用short *.
    //malloc 在堆区开辟n个字节大小的空间,返回空间的首地址.
    char *p = malloc(8);
    //存储iphone
    strcpy(p, "iphome");
    printf("%s\n",p);//iphome
    //释放空间
    //删除只是标记删除,不会清楚空间上的内容.
    free(p); p = NULL;


三.堆区空间问题.

     1.野指针问题:访问没有所有权的空间.

     2.过度释放:一块空间释放多次,程序会立即crash.

     3.内存泄露:空间没有释放,造成内存堆积.不会造成立即crash,安全隐患.

处理方法 : p = NULL;

使用指针必备条件:       1.指针要由明确的指向.

                                     2.指向一个可以控制的区域


四.malloc应用

1.一个有5个元素的数组空间.存储5个整数.

    int  *p = malloc(sizeof(int)*5);
    for (int i = 0; i < 5; i++) {
        p[i] = arc4random() % (40 - 20 + 1) + 20;
        printf("%d ",p[i]);
    }
    printf("\n");
    
   //排序
    for (int i = 0; i < 5 - 1; i++) {
        for (int j = 0; j < 5 - 1 - i; j++) {
            if (p[j] > p[j + 1]) {
                int temp = p[j];
                p[j] = p[j + 1];
                p[j + 1] = temp;
            }
        }
    }
    for (int i = 0; i < 5; i++) {
        printf("%d ",p[i]);
    }
    
    free(p);
    p = NULL;


2.有一个字符串,其中包含数字,提取其中的数字,要求动态分配内存保存.
    printf("请输入一段字符串:\n");
    char str[] = {0};
    scanf("%s",str);
    int i = 0;
    int num = 0;
    while (str[i] != '\0') {
        if (str[i] >= '0' && str[i] <= '9') {
            num++;
        }
        i++;
    }
    char *q = malloc(sizeof(char)*(num+1));//根据长度生成空间 num + 1 字符串隐藏\0
    int m = 0,n = 0;
    for (int i = 0; i < num; i++) {
        while (str[m] != '\0') {
            if (str[m] >= '0' && str[m] <= '9') {
                q[n] = str[m];
                n++;
            }
            m++;
        }
    }
    q[n] = '\0';//多出来的存储\0
    printf("\n");
    for (int i = 0; i < num; i++) {
        printf("%c", q[i] );
    }
    free(q);
    q = NULL;


3.输入3个学员的姓名,动态分配内存保存学员姓名,并在最后输出.

   

    char *names[3] = {0}; //存储在堆区开辟的空间的首地址.
    char temp[255] = {0};//存储从控制台输入的字符串
    for (int i = 0; i < 3; i++) {
        printf("请输入3个学生的姓名");
        scanf("%s", temp);//输入到temp临时数组中.
        //动态计算输入的字符串的长度
        unsigned long length = strlen(temp);
        //在堆区开辟空间.
        names[i] = malloc(length + 1);//将堆区空间的地址存储指针数组中,
         //将内容拷贝到堆区.
        strcpy(names[i], temp);
        
    }
    
    for (int i = 0; i < 3; i++) {
        printf("%s \n",names[i]);
        free(names[i]);<span style="font-family: Arial, Helvetica, sans-serif;">//空间的释放</span>
        names[i] = NULL;
     
    }



(2)calloc用法

   

    1.void * calloc(unsigned n, unsigned size);
    分配n个size大小的空间.并且把该内存上的所有字节清零.
    int *p = calloc(5, 4);//分配5块大小为4字节的空间,并且做清零操作.
    
    free(p);
    p = NULL;

(3)realloc 用法

 

    void *realloc(void *p,unsigned newSize);
    int *p = malloc(10);
    printf("%p\n",p);
    int *q = realloc(p, 40);//重新分配一个大小为100 的空间.
    //realloc 返回当前空间的首地址.如果当前空间不够,则在另一块申请20个字节的空间,此时返回新的空间的地址.
    printf("%p\n",q);
    free(q);
    q = NULL;
    p = NULL;//不需要释放.


(4)memcpy用法

  

  void *memcpy (void *dest, const void *source, size_t n);
      //从source指向内存开始拷贝到dest中n个字节.
     
     char *p1 = malloc(8);
     char *p2 = malloc(8);
     
     strcpy(p2, "frank");
     printf("%s\n", p2);
     //memcpy(p1, p2, 6);
     
     memcpy(p1, p2, 4);
     memcpy(p1, p2 + 2, 4);//ank
     p1[4] = '\0';
     printf("%s\n", p1);//fran

(5)memset用法

   
<pre name="code" class="cpp">4. void * memset(void *s, int c, size_t n);
    //从s指向的内存开始初始化n个字节的内容为c
    char *p1 = malloc(8);
    memset(p1,0,8);

五.练习

      定义两个整型指针,分别用malloccalloc对其分配空间保存3个元素,malloc分配的空间用memset清零,随机对数组进行赋值随机范围1-3,赋值后用memcmp比较两个数组。如果相同打印Good!否则打印Failed..

    int *p = malloc(sizeof(int) * 3);
    int *q = calloc(3, sizeof(int));
    memset(p, 0, sizeof(int) * 3);
    for (int i = 0;i < 3;i++) {
        p[i] = arc4random() % (3 - 1 + 1) + 1;
        q[i] = arc4random() % (3 - 1 + 1) + 1;
        printf("p:%d q:%d\n",p[i],q[i]);
    }

   //比较buf1和buf2指向的内存是否相同,比较count个字节
        if(memcmp(p , q , sizeof(int) * 3) == 0){ //地址 .3即3个字节.
            printf("profect\n");
        } else {
            printf("failed\n");
        }
    
    free(p);
    free(q);
    p = NULL;
    q = NULL;



六.宏

    定义宏: 三部分:1.#define 2.宏名 3. 替换的内容

    宏的作用: 只做替换.

    宏名的命名规范: 1.全部大写 2. k+驼峰

    无参宏  #define kArrayNumber5     #define N10

    有参宏  #define MUL(A,B) ((A) * (B))

    宏应该注意的几个问题.

     1.宏名大写

     2.参数一定要加小括号.

     3.宏替换的内容不要加分号.

     4.对于有参宏,宏名一与参数之间不要加空格.


七.枚举

 定义枚举

 枚举的作用 :罗列出所有的可能性

 枚举是将人能够识别的标示符和计算机能够识别的数字结合在一起.

例子:

//定义枚举
typedef enum button {
    //通过1的每一位来表示一个状态.
    Close = 1 << 0,//关闭 001    第一位1表示关闭
    Max = 1 << 1, // 最大化 010   第二位为1表示最大化
    Min = 1 << 2//最小化 100      第三位为1表示最小化
    
}Button;
   如果想宝贝多个枚举值,按位或即可,前提是枚举值通过左移符号对应值.
   BOOL b = Close | Max | Min; //001 010 100 = 111
   printf("%d\n", b);


八.条件编译

#开头的叫做预编译指令

预编译指令做一些文本以及代码的替换工作.

形式1:

#define A
#ifdef A
    int a = 10;//条件编译(根据条件编译不同的代码)
#else
    int a = 20;
#endif
    printf("%d ", a);//10

形式2:

#define A
#ifndef A //如果没有定义
    int a = 10;//条件编译(根据条件编译不同的代码)
#else
    int a = 20;
#endif
    printf("%d\n",a);//20


形式3:

# if  10 //非0即为真,执行if,否则为假,执行else
    int a = 10;
#else
    int b = 20;
#endif
    printf("%d\n", a); //10


九.面试题 #include #import的区别

#import 相比 #include 能够防止重复导入,引起交叉编译.

#import " " 导入自定义的头文件

#impoer <> 导入系统的头文件


















【学习ios之路:C语言】存储区内存划分