給字符指針分配內存

马上能想到的函数有:


GetMem
AllocMem
ReallocMem
FreeMem

GetMemory
ReallocMemory
FreeMemory

New
Dispose

NewStr
DisposeStr

StrNew
StrAlloc
StrDispose

GlobalAllocPtr
GlobalFreePtr

WideStrAlloc
AnsiStrAlloc
StrDispose

Move
MoveMemory
CopyMemory
ZeroMemory
FillMemory
FillChar

StrBufSize


给字符指针(PChar、PWideChar、PAnsiChar)分配内存, 最佳选择是: StrAlloc.

StrAlloc 虽然最终也是调用了 GetMem, 但 StrAlloc 会在指针前面添加 Delphi 需要的 4 个管理字节(记录长度).

StrAlloc 分配的内存, 用 StrDispose 释放, 用 StrBufSize 获取大小.

用 FreeMem 释放可以吗? 这样会少释放 4 个字节.

这种类型的指针一般用于 API 函数的参数, 譬如获取窗口标题:


var
  p: PChar;
begin
  p := StrAlloc(256);
  GetWindowText(Handle, p, StrBufSize(p));
  ShowMessage(p); {Form1}
  StrDispose(p);
end;


StrAlloc 根据不同的参数(PWideChar、PAnsiChar)分别重载调用了 WideStrAlloc、AnsiStrAlloc, 所以我们也可以直接使用这两个函数(这也需要用 StrDispose 释放), 不过使用它们的必要性不大; 用 StrAlloc 指定好参数类型即可.

给字符指针分配内存其他方法也挺方便, 譬如:


//获取 WINDOWS 所在目录
var
  buf: array[0..MAX_PATH] of Char;
begin
  GetWindowsDirectory(buf, Length(buf));
  ShowMessage(buf); {C:/WINDOWS}
end;


数组的内存不是我们自己申请的, 系统会自动释放; 记住: 只要是手动申请的内存一定要手动释放.

我们给字符指针申请内存主要是为了在 API 中接受数据, 如果我们要直接赋给常量值, 系统会自动分配内存的, 譬如:


var
  p: PChar;
begin
  p := '万一的 Delphi 博客';
  ShowMessage(p); {万一的 Delphi 博客}
end;


当然我们也可以用这种办法申请内存, 就是笨了点, 譬如:


//获取系统目录
var
  p: PChar;
begin
  p := PChar(StringOfChar(Char(0), 256)); {反复一个空字符 256 次成一个字符串, 然后转为 PChar}
  GetSystemDirectory(p, StrBufSize(p));
  ShowMessage(p); {C:/WINDOWS/system32}
end;


如果在 API 函数需要的字符指针是为了输入, 当然也不需要申请内存, 譬如:


//设置窗口标题
var
  p: PChar;
begin
  p := '窗口新标题';
  SetWindowText(Handle, p);
end;

//也可以直接给常量
begin
  MessageBox(Handle, '提示信息', '标题', MB_OK);
end;

//如果是给字符串的变量或常量, 则需要转换一下
var
  str: string;
begin
  str := '万一的 Delphi 博客';
  TextOut(Canvas.Handle, 10, 10, PChar(str), Length(str));
  {在窗体上输出文字, 此代码不能在 OnCreate 事件中}
end;


跑题了...到现在已用到了 StrAlloc、StrDispose、WideStrAlloc、AnsiStrAlloc、StrBufSize 几个函数.

还有 NewStr、DisposeStr、StrNew、StrDispose 也貌似有点关系.

先说 NewStr 和 DisposeStr(它们是一对);
NewStr 是根据 AnsiString 再新建一个 PAnsiString, 不过这是为兼容而存在的, Delphi 已不提倡使用了.
不再提倡使用的函数都缀以 deprecated 标识, 并在代码提示中用灰色显示.
其实用 @ 即可获取字符串指针, 当然根本用不着它们.

还有个 StrNew; StrNew 可以再制一个相同的字符指针, 譬如:


var
  p1,p2: PChar;
begin
  p1 := 'Delphi';

  p2 := StrNew(p1);
  ShowMessageFmt('%s, %s', [p1, p2]); {Delphi, Delphi}

  p1 := '2009';
  ShowMessageFmt('%s, %s', [p1, p2]); {2009, Delphi}

  StrDispose(p2); {释放自己申请的}
end;


不过 StrNew 存在的意义也不大, 我们可以更简单地完成上面的操作:


var
  p1,p2: PChar;
begin
  p1 := 'Delphi';
  p2 := p1;
  ShowMessageFmt('%s, %s', [p1, p2]); {Delphi, Delphi}
  p1 := '2009';
  ShowMessageFmt('%s, %s', [p1, p2]); {2009, Delphi}
end;


说来说去, 好像只有 StrAlloc 是我们值得我们记忆的?

还有一对非常重要的相关函数: GlobalAllocPtr、GlobalFreePtr; 它们的功能是上面这些都不可替代的!

GlobalAllocPtr 和 GlobalFreePtr 是对系统函数: GlobalAlloc、GlobalFree 的简化, 之所以说它们重要, 只是因为它们可以跨进程操作; 不过 GlobalAllocPtr 是给无类型指针(Pointer)分配内存, 当然就不仅仅用于字符指针了. 还是到后面专题再做例子吧.

在 C++ 中,给指针分配内存的核心方法是使用 `new` 或 `new[]` 操作符(对于动态数组),或者使用 C 风格的 `malloc`/`calloc`(较少用于 C++ 类对象)。下面我以你前面的学生通讯录为例,详细讲解 **如何给 char* 指针分配内存**,并结合实际代码说明每一步。 --- ### 一、为什么要给指针分配内存? 当你声明一个 `char* name;` 时,这只是创建了一个**指针变量**,它本身不存储字符串内容,只保存地址。 要真正存储字符串(比如 `"张三"`),你需要在堆(heap)上申请一块足够大的内存空间,并让指针指向它。 --- ### 二、基本语法:使用 `new[]` 分配字符数组内存 ```cpp name = new char[size]; ``` - `size` 是所需字节数,通常为 `strlen(原始字符串) + 1` - `+1` 是为了容纳字符串结尾的空字符 `\0` #### 示例: ```cpp char source[] = "张三"; name = new char[strlen(source) + 1]; // 计算需要多少字节 strcpy(name, source); // 把内容复制进去 ``` ✅ 这样就完成了“动态分配 + 内容拷贝”的过程。 --- ### 三、完整步骤详解(逐行) 假设我们要初始化姓名字段: ```cpp char* name; // 声明一个字符指针 ``` #### 步骤 1:计算所需空间 ```cpp int len = strlen("张三"); // 返回字符个数(中文一般占3字节/字符,这里"张三"可能是6) ``` > 注意:中文字符在 UTF-8 编码下通常是 3 字节,所以 `"张三"` 实际长度是 6,加上 `\0` 就要 7 字节。 #### 步骤 2:分配内存 ```cpp name = new char[len + 1]; // 分配 len+1 个字节的空间 ``` > 在堆上开辟了一块连续内存区域,可以存放这个字符串。 #### 步骤 3:复制数据 ```cpp strcpy(name, "张三"); ``` > 使用 `strcpy` 将字符串内容从源复制到新分配的内存中。 此时内存布局如下: ``` 堆(Heap): [name] ---> ['张']['三']['\0'] ← 动态分配的空间 栈(Stack): name 指针变量保存了这块内存的首地址 ``` --- ### 四、错误做法 vs 正确做法 ❌ 错误写法(浅拷贝,危险!): ```cpp char temp[] = "张三"; name = temp; // ❌ 危险!temp 是局部变量,函数结束后失效 ``` > 结果:`name` 指向一个已经被销毁的内存区域 → 成为“悬空指针”,后续访问出错! ❌ 更危险的写法: ```cpp name = "张三"; // 虽然语法合法,但不能 delete!因为字符串常量不可修改 delete[] name; // ❌ 运行时报错:尝试释放常量区内存 ``` ✅ 正确写法(深拷贝): ```cpp name = new char[strlen("张三") + 1]; strcpy(name, "张三"); // 安全复制到自己申请的空间 // 后续可以用 delete[] name; 安全释放 ``` --- ### 五、释放内存(必须匹配) 分配用了 `new[]`,释放就必须用 `delete[]`: ```cpp delete[] name; // 释放数组 name = nullptr; // 防止野指针 ``` ⚠️ 忘记释放 → 内存泄漏 ⚠️ 多次释放 → 程序崩溃 ✅ 释放后置空 → 安全编程习惯 --- ### 六、封装成函数(如 Init) 回到你的类中的 `Init` 函数: ```cpp void Init(char* n, char* h, char* p, char* e) { FreeSpace(); // 先释放旧内存 if (n != nullptr) { name = new char[strlen(n) + 1]; strcpy(name, n); } // 其他字段同理... } ``` 这就是标准的“安全深拷贝”流程。 --- ### 七、替代方案:使用 std::string(推荐现代 C++) 如果你不想手动管理内存,可以直接用 `std::string`: ```cpp class StudentContact { private: string name; string hometown; string phone; string email; public: void Init(const string& n, const string& h, const string& p, const string& e) { name = n; hometown = h; phone = p; email = e; } void Print() { cout << "姓名: " << name << endl; // 不用手动释放!string 自动管理内存 } }; ``` 👉 优点:无需 `new/delete`,不会内存泄漏,更安全简洁。 --- ### ✅ 总结:给指针分配内存的正确姿势 | 步骤 | 操作 | |------|------| | 1. 判断是否为空 | `if (str != nullptr)` | | 2. 计算长度 | `strlen(str) + 1` | | 3. 分配内存 | `new char[length]` | | 4. 复制内容 | `strcpy(ptr, str)` | | 5. 使用完毕 | `delete[] ptr; ptr = nullptr;` | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值