记录一个指针问题(内存空间的初始化)

本文详细解析了C语言中指针的基本概念、动态内存分配与释放、数组与指针的关系,以及如何安全地进行内存操作。通过实例代码演示了指针的应用,包括初始化、赋值、数组元素访问、指针运算等关键操作,旨在帮助开发者理解和掌握C语言中复杂且强大的指针使用技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先正确理解一下例子。 

  void test(){
        char *str[]={"welcome","to","fortemedia","Nanjing"};  
        char * * p=str+1;     //p指向 "to"字符串地址  

        printf("%s\n",str[0]);  
        printf("%s\n",str[1]);  
        printf("%s\n",str[2]);  
        printf("%s\n",str[3]);  

        printf("p==%x\n", p);
        printf("str[0]==%x\n", str[0]);
        printf("str[1]==%x\n", str[1]);
        printf("str[2]==%x\n", str[2]);
        printf("str[3]==%x\n", str[3]);
        printf("**str==%x\n", &str);
        printf("*p=%s\n", *p);
        str[0]=(*p++) +2;          //str[0]指向'\0';           然后p后移 p=str[2]  
        str[1]=*(p+1);            //p+1后 p+1 = str[3];   然后 str[1] = str[3]           
        str[2]=p[1]+3;             //str[2]指向str[3]的从0开始数的第三个 str[2]指向"jing"地址  
        str[3]=p[0]+(str[2]-str[1]);  //str[3]指向从p[0]开始(也就是*p,也就是str[2])的 偏移量为(str[2]-str[1])的地址~ str[2]指向"jing",str[1]指向str[3],也就是"Namjing",所以str[3]指向"jing"的"g"地址  
        printf("%s\n",str[0]);  
        printf("%s\n",str[1]);  
        printf("%s\n",str[2]);  
        printf("%s\n",str[3]);  
    }


main函数中首先定义

 char *p[DATA_LENGTH] ; // 定义一个指针数组。

 char name[23] = "tian";// 定义一个字符数组。

    init_data(p);   // 初始化指针数组
    printf("init done\n");
    show(p);// 初始化指针数组的数组
    printf("show done0==%s\n",p[0]);// p[0] 可以看作是数组的第一个数据。
    CountStrLen(p[0]);// 计算初始化数组的第一个数据的字符串长度


void init_data(char **p){  // 参数传入二维指针

    int i;
    for(i =0;i< DATA_LENGTH;i++)
    {
        printf("init data==%d\n",i);
        if(NULL==(p[i]=(char*)malloc(sizeof(char)*10)))  // 循环遍历动态申请空间
        {
            perror("error...");
            exit(1);
        }else
        {
            memset(p[i],0,sizeof(char)*10); // 初始化数组的值为0
            printf("init data==%s\n",p[i]);
        }
    }
}


正确的做法(这个函数有冗余代码)

void show(char **p){  // 参数传入二维指针

    int i;
    for(i = 0;i<DATA_LENGTH;i++){

        char *tmp;

        printf("show i==%d\n",i);
        if(NULL != (tmp=(char*)malloc(sizeof(char)*10)))
        {
            memcpy(tmp,"tian",5);  // 将tmp指向 tian 字符串的首地址
            memcpy(p[0],tmp,5); // 给p[0] 所指的数据空间赋值为 “tian”
            free(tmp); // 释放临时空间,切记,malloc 和free 一定同时出现
            printf("the data%d==,%s\n",i,p[i]);
        }
    }

错误的做法

void show(char **p){  // 参数传入二维指针

    int i;
    for(i = 0;i<DATA_LENGTH;i++){

        char *tmp;

        printf("show i==%d\n",i);
        if(NULL != (tmp=(char*)malloc(sizeof(char)*10)))
        {

            *tmp =“tian”;// 本身定义常量字符串他就是一段地址,也就是后期不能做修改。

            p[i] =tmp; // 将p[i]  又指向了一段常量地址。

            free(tmp); // 释放临时空间,切记,malloc 和free 一定同时出现
            printf("the data%d==,%s\n",i,p[i]);  // 打印没有问题,p[i]  一直是 tian
        }
    }

// 计算字符串的长度

    size_t CountStrLen(char *p){
        int i;
        size_t lenth=0;
        printf("p==%s\n",p);
        for(i =0 ; *p != '\0' ;i++){
            p++;
            lenth++;

            printf("length==%d,p[i]==%s\n",lenth,p);
        }
        printf("length==%d\n",lenth);
        return lenth;
    }


总结:

1. 指针所指向的内存地址空间,需要memcpy ,strcpy 去初始化。

2. 在错误的方法中在main 打印p[0] 没有错,他打印的是“tian”字符串,只能是巧合,凑巧申请的。

3. 全局的指针指向了一个局部的地址空间,当函数运行结束,局部的地址空间被释放,所以全局的指针指向了一段被释放的地址空间。

4. 以后若使用赋值,采用memcpy ,memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中。



智能指针本身并不直接分配内存空间;它们的主要功能是指向由普通指针通过运算符 `new` 分配得到的对象,并在适当的时候自动释放这些对象所占用的资源,以此避免内存泄漏。 然而,在我们使用智能指针之前,确实需要先利用 `new` 或者其他机制为动态数据结构创建必要的存储区域。下面我将详细解释这一过程以及常见的几种 C++ 智能指针类型: ### 动态内存分配 首先得理解如何手动地去管理一块堆上分配的空间。例如,如果你想要创建一个整数类型的动态变量,可以这样做: ```cpp int* p = new int(10); // 分配并初始化一个新的整型 // 使用完之后记得删除以防止泄露 delete p; ``` 但是这种方式容易引发各种错误如忘记释放、重复释放等,所以引入了智能指针的概念来简化这个问题。 ### 标准库提供的三种智能指针 #### unique_ptr - 表示唯一所有权的关系。 - 当 `unique_ptr` 被销毁时它会自动清理其指向的内容。 - **注意**:不可以复制给另一个 `unique_ptr` ,只能转移拥有权。 例子: ```cpp std::unique_ptr<int> up(new int(20)); // 等价于 std::make_unique<int>(20) (C++14及以上版本) ``` 这里并没有显式调用 delete 因为当离开作用域时会被自动处理掉。 #### shared_ptr - 支持多个所有者共享同一个对象。 - 内部有一个引用计数器记录有多少个 `shared_ptr` 正持有该对象;一旦最后一个也消失了,则同时回收关联的数据区段。 实例化方式有两种常见模式之一: ```cpp auto sp1 = std::make_shared<int>(30); { auto sp2 = sp1; // 引用计数加一 } // 出块后sp2失效,但由于还有sp1存在因此不会立即释放内容 ``` #### weak_ptr - 主要是作为辅助工具配合 `shared_ptr` 来打破循环引用的问题。 - 它观察而非控制生命周期——即不会影响原生计数的变化趋势。 简单示意一下应用场景吧(假设有个图节点场景可能会形成环形依赖): ```cpp struct Node { int value; std::weak_ptr<Node> next; void connect(std::shared_ptr<Node>& target){ this->next = target; } }; ... auto n1 = std::make_shared<Node>(); auto n2 = std::make_shared<Node>(); n1->connect(n2); n2->connect(n1); // 这样就形成了弱连接而不是强链接导致死锁的情况啦~ ``` 综上所述,“智能指针分配内存”实际上应该说是程序员负责申请合适的地址池而后的维护工作交给智能代理去做即可!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值