- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有以下两个函数,它们接受字符串数组并使其小写(就地)--
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
void to_lower(char ** strings) {
char * original_string;
char * lower_string;
for (int i=0; (original_string=strings[i]) != NULL; i++) {
lower_string = malloc(strlen(original_string + 1) * sizeof(char));
for (int j=0; j<=strlen(original_string); j++) {
lower_string[j] = tolower(original_string[j]);
}
strings[i]=lower_string;
}
}
int main(void) {
char * strings[] = {"Hello", "Zerotom", "new", NULL };
to_lower(strings);
to_lower(strings); // this and successive calls won't change
to_lower(strings); // anything but are here to try and understand malloc
to_lower(strings);
to_lower(strings);
return 0;
}
main
之前的
to_lower
函数的开头,消耗了多少内存?我猜是字符数组的16个字节(15个字符加上结尾的1个空字节)。
to_lower
运行5次之后和函数返回之前,消耗了多少内存?我应该在哪里“空闲”—使用传递到函数中的字符串(我的想法是每次复制/小写字符串时都调用
malloc
,它会创建额外的内存,但永远不会释放任何内容。
to_lower
函数看起来是否正常,或者如何修改它以使其当前不泄漏内存?
最佳答案
通过将分配和转换组合为一个void to_lower(char ** strings)
函数,您将自己包裹在轴上(使自己感到困惑)。正如您所发现的,如果您想在同一个对象上调用函数两次,那么您必须free
分配给在两次调用之间保存小写字符串的内存,但是您已经丢失了指向原始字符串的指针。。
虽然在一个函数中组合多个操作没有错,但是您必须后退一步,确保您所做的事情有意义,并且不会导致比它解决的问题更多的问题。
在修改strings
中包含的字符串之前,需要分配和复制strings
,因为您将strings
初始化为指向字符串文本的指针数组。字符串文本是不可变的(除了极少数系统外),它是在只读内存(通常是可执行文件的.rodata
部分)中创建的。尝试修改字符串文字几乎可以保证SegFault(除了一些古怪的系统之外)
此外,如果您已经用保存小写结果的分配内存指针覆盖了指向原始字符串的指针地址,您将如何获取原始字符串?(更不用说当可以free
这些指针时,用指向已分配内存的指针替换指向文本的指针会产生跟踪问题)。
在这里,最好保持原始值strings
不变,只需分配原始值的副本(通过分配指针,包括sentinel值的指针,并为每个原始字符串分配存储空间,然后在转换为小写之前复制原始字符串)。这解决了内存泄漏的问题以及丢失字符串文本的原始指针的问题。您可以根据需要释放小写字符串,并且始终可以制作原件的另一个副本以再次发送到转换函数。
那么,你将如何着手实施这个计划呢?最简单的方法就是声明一个指向char指针的指针(例如双指针),您可以为任意数量的指针分配一个内存块。在这种情况下,只需分配与strings
数组中相同数量的指针,例如:
char *strings[] = {"Hello", "Zerotom", "new", NULL };
size_t nelem = *(&strings + 1) - strings; /* number of pointers */
/* declare & allocate nelem pointers */
char **modstrings = malloc (nelem * sizeof *modstrings);
if (!modstrings) { /* validate EVERY allocation */
perror ("malloc-modstrings");
}
sizeof strings / sizeof *strings
来获取元素的数量)
modstrings
分配了一块内存,其中包含与
strings
中相同数量的指针,您可以简单地分配足够容纳每个字符串文本的内存块,并将每个块的起始地址分配给
modstrings
中的连续指针,将最后一个指针设置为哨兵,例如。
void copy_strings (char **dest, char * const *src)
{
while (*src) { /* loop over each string */
size_t len = strlen (*src); /* get length */
if (!(*dest = malloc (len + 1))) { /* allocate/validate */
perror ("malloc-dest"); /* handle error */
exit (EXIT_FAILURE);
}
memcpy (*dest++, *src++, len + 1); /* copy *src to *dest (advance) */
}
*dest = NULL; /* set sentinel NULL */
}
NULL
参数传递为
src
而不仅仅是
char * const *src
,您可以向编译器指示不会更改
char **src
,从而允许编译器进行进一步的优化。
src
应该是类似的,但这一讨论将留待另一天进行)
restrict
功能减少到:
void to_lower (char **strings) {
while (*strings) /* loop over each string */
for (char *p = *strings++; *p; p++) /* loop over each char */
*p = tolower (*p); /* convert to lower */
}
to_lower
之前将
strings
复制到
modstrings
,所以您可以将这两个函数组合成一个包装器(组合起来确实有意义),例如。
void copy_to_lower (char **dest, char * const *src)
{
copy_strings (dest, src); /* just combine functions above into single */
to_lower (dest);
}
to_lower
和
print_array
——稍后再添加)
free_strings
的每个
copy_to_lower
和
print_array
之间,需要释放分配给每个指针的存储空间,以便再次调用
modstrings
时不会泄漏内存。一个简单的
copy_to_lower
函数可以是:
void free_strings (char **strings)
{
while (*strings) { /* loop over each string */
free (*strings); /* free it */
*strings++ = NULL; /* set pointer NULL (advance to next) */
}
}
free_strings
中任意多次分配、复制、转换为lower、打印和释放。您只需重复呼叫:
copy_to_lower (modstrings, strings); /* copy_to_lower to modstrings */
print_array (modstrings); /* print modstrings */
free_strings (modstrings); /* free strings (not pointers) */
copy_to_lower (modstrings, strings); /* ditto */
print_array (modstrings);
free_strings (modstrings);
...
main()
时,您正在释放每个字符串的存储空间,但是您将留下包含分配给
free_strings
的指针的内存块。因此,要完成所有已分配内存的释放,不要忘记释放指针,例如。
free (modstrings); /* now free pointers */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
void print_array (char **strings)
{
while (*strings)
printf ("%s, ", *strings++);
putchar ('\n');
}
void free_strings (char **strings)
{
while (*strings) { /* loop over each string */
free (*strings); /* free it */
*strings++ = NULL; /* set pointer NULL (advance to next) */
}
}
void copy_strings (char **dest, char * const *src)
{
while (*src) { /* loop over each string */
size_t len = strlen (*src); /* get length */
if (!(*dest = malloc (len + 1))) { /* allocate/validate */
perror ("malloc-dest"); /* handle error */
exit (EXIT_FAILURE);
}
memcpy (*dest++, *src++, len + 1); /* copy *src to *dest (advance) */
}
*dest = NULL; /* set sentinel NULL */
}
void to_lower (char **strings) {
while (*strings) /* loop over each string */
for (char *p = *strings++; *p; p++) /* loop over each char */
*p = tolower (*p); /* convert to lower */
}
void copy_to_lower (char **dest, char * const *src)
{
copy_strings (dest, src); /* just combine functions above into single */
to_lower (dest);
}
int main(void) {
char *strings[] = {"Hello", "Zerotom", "new", NULL };
size_t nelem = *(&strings + 1) - strings; /* number of pointers */
/* declare & allocate nelem pointers */
char **modstrings = malloc (nelem * sizeof *modstrings);
if (!modstrings) { /* validate EVERY allocation */
perror ("malloc-modstrings");
}
copy_to_lower (modstrings, strings); /* copy_to_lower to modstrings */
print_array (modstrings); /* print modstrings */
free_strings (modstrings); /* free strings (not pointers) */
copy_to_lower (modstrings, strings); /* ditto */
print_array (modstrings);
free_strings (modstrings);
copy_to_lower (modstrings, strings); /* ditto */
print_array (modstrings);
free_strings (modstrings);
copy_to_lower (modstrings, strings); /* ditto */
print_array (modstrings);
free_strings (modstrings);
free (modstrings); /* now free pointers */
}
$ ./bin/tolower_strings
hello, zerotom, new,
hello, zerotom, new,
hello, zerotom, new,
hello, zerotom, new,
modstrings
是正常的选择。每个平台都有类似的内存检查程序。它们都很容易使用,只要运行你的程序就可以了。
$ valgrind ./bin/tolower_strings
==5182== Memcheck, a memory error detector
==5182== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==5182== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==5182== Command: ./bin/tolower_strings
==5182==
hello, zerotom, new,
hello, zerotom, new,
hello, zerotom, new,
hello, zerotom, new,
==5182==
==5182== HEAP SUMMARY:
==5182== in use at exit: 0 bytes in 0 blocks
==5182== total heap usage: 13 allocs, 13 frees, 104 bytes allocated
==5182==
==5182== All heap blocks were freed -- no leaks are possible
==5182==
==5182== For counts of detected and suppressed errors, rerun with: -v
==5182== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
关于c - 就地修改数组并了解其内存分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58127467/
我正在尝试解决以下问题: We are given an array containing ‘n’ objects. Each object, when created, was assigned a
考虑以下代码: a=(1 2 3) a='seven' export a declare -p a 输出(来自declare)是: declare -ax a='([0]="seven" [1]="2
我正在尝试将 ['1','2','3','4'] 转换为 [1,2,3,4]我想就地进行此转换。有可能做到吗?如果不是,最佳解决方案是什么。 最佳答案 我觉得用map比较好对于这类任务。这会创建迭代器
好的,所以我之前发布了关于尝试(没有任何预建函数)删除额外空间的信息 "this is a test"会回来的 "this is a test" Remove spaces from a strin
我有一个名为Media的插件,该插件应负责图像大小调整等工作。 它具有以下依赖性: dependencies { compile group: 'org.ccil.cowan.tagsoup'
我需要将一个大字符串向左“移动”X 个空格。它太大了,无法放入内存,所以我需要就地做。我需要使用最少量的系统调用来完成此操作。 我知道我可以使用缓冲区并重用内存来最大限度地减少内存消耗,然后使用 fs
我想知道是否可以在不需要临时数组的情况下通过 Cholesky 分解获得矩阵的逆。截至目前,我可以在不使用临时数组的情况下进行 cholesky 分解,但从那里我还没有想出一种方法来获得原始矩阵的逆矩
是否有任何用于 Javascript 的就地编辑插件..像 firebug 之类的东西,它对即时 CSS 编辑和预览非常有用,但不允许就地 JS 编辑..那么,有没有我们可以立即更新和更新的工具或插件
题目如下:给定一个 linked list,将备用 indices 移到 list 的后面 例如: input: : [0] -> [1] -> [2] -> [3] -> [4]
在我看来,std::copy_if 对于过滤容器非常有用: std::vector vec { 1, 2, 3, 4 }; auto itEnd = std::copy_if(vec.begin(),
在 C++ 中相交两个集合的标准方法是执行以下操作: std::set set_1; // With some elements std::set set_2; // With some othe
在 Python 中,字符串是不可变的。 逐个字符遍历字符串并对其进行修改的标准习语是什么? 我能想到的唯一方法是一些与加入结果字符串相关的真正臭名昭著的黑客攻击。 -- 在 C 中: for(i
我有一个 ListBuffer。我想删除满足特定条件的所有元素。 我可以迭代它并删除每个元素。但是 Scala 对改变你正在迭代的列表有什么看法呢?它会起作用,还是会删除错误的元素/不返回所有元素?
我需要重新绑定(bind)两个大数据帧。现在我用的是 df 根据 nikola 的评论,这里是 ?rbindlist 的描述(v1.8.2 中的新增功能): Same as do.call("rbi
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 3 年前。 Improve th
我在带有 LVS_EDITLABELS 的无模式 Win32 对话框中有一个小图标模式的 ListView 放。无论编辑是通过单击鼠标还是通过调用 ListView_LabelEdit() 以编程方式
所以基本上不能/允许创建一个新数组。除了实际更改和操作当前数组外,无法返回任何内容。您如何获取字符数组并简单地翻转/反转它们。 Starting array: ['P','e','r','f','e'
我不明白为什么下面的代码没有对 vector 的前两个元素进行排序: int main() { std::vector v = {2,1,3,1,2}; std::sort(v.beg
我有以下(简化的)代码: a = a[::3] b = b[::3] c = c[::3] d = d[::3] a,b,c,d,其实都是很复杂的表达式,所以我想这样写: for l in [a, b
可以对数组进行不依赖于数组秩的操作。迭代器也不总是合适的解决方案。给定数组 double[,] myarray = new double[10,5]; 实现以下工作流程是可取的: 将 Rank>1 的
我是一名优秀的程序员,十分优秀!