C 这是优化内存使用的正确方法吗?
我的目标是优化内存使用。。。我从未在任何教程中看到过它,这让我觉得这不是正确的方法C 这是优化内存使用的正确方法吗?,c,memory-management,C,Memory Management,我的目标是优化内存使用。。。我从未在任何教程中看到过它,这让我觉得这不是正确的方法 #include <stdio.h> #include <string.h> #include <stdlib.h> struct Player { char* username; int hp; int mp; }; int main(void) { struct Player test, *p = &test; p->
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Player {
char* username;
int hp;
int mp;
};
int main(void) {
struct Player test, *p = &test;
p->username = (char*)malloc(50 * sizeof(char));
scanf("%s", p->username);
p->username = realloc(p->username, (strlen(p->username) + 1) * sizeof(char));
printf("%s", p->username);
return 0;
}
一些提示:
示例代码太小,不重要
永远不要用malloc来做你永远想要的事情。相反,预先分配,例如作为全局变量或如果它足够小,则使用局部变量以避免malloc的开销。例如:
不要把数据到处传播。您希望所有数据都位于同一位置,且缓存线数量最少,同时使用的数据段彼此尽可能接近。一种方法是合并多个项目。例如:
struct Player {
char username[50];
int hp;
int mp;
};
int main(void) {
struct Player test, *p = &test;
如果某个东西最多占用50个字符的内存;不要费心使用realloc浪费CPU时间,并可能浪费更多内存。不要忘记malloc和realloc的内部代码将向每个分配的内存块添加元数据,这些内存块可能需要额外的16字节或更多
一般而言;对于性能,malloc和realloc以及新的和。。。应完全避免,尤其是大型项目。它们将数据随机传播到任何地方,破坏了获得良好局部性的任何希望,这对于最小化多个非常昂贵的事情非常重要—缓存未命中、TLB未命中、页面错误、交换空间使用等
注意:scanf和get也应该被禁止。它们无法防止缓冲区溢出,例如,当只有足够的内存分配给50个字符时,用户提供超过50个字符,目的是故意破坏/破坏其他数据,从而导致巨大的安全漏洞
优化内存使用的正确方法
临时重复使用的缓冲区通常是慷慨的,大小是固定的
为内存分配合适的大小对成员来说是有意义的。代码的用户名可以用于数百万struct Player
IOWs,对代码的可变大小方面使用分配。如果struct Player是用于两人国际象棋,则字符用户名[50]大小是有意义的。对于多玩家世界,char*是有意义的
而不是调用*OLC两次考虑一个正确大小的调用。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Reasonable upper bound
#define USERNAME_SIZEMAX 50
struct Player {
char* username;
int hp;
int mp;
};
int main(void) {
puts("Enter user name");
// Recommend 2x - useful for leading/trailing spaces & detecting excessive long inputs.
char buf[USERNAME_SIZEMAX * 50];
if (fgets(buf, sizeof buf, stdin) == NULL) {
puts("No input");
} else {
trim(buf); // TBD code to lop off leading/trailing spaces
if (!valid_name(buf)) { // TBD code to validate the `name`
printf("Bad input \"%s\"\n", buf);
} else {
struct Player test = { 0 }; // fully populate
test.username = malloc(strlen(buf) + 1);
// Maybe add NULL check here
strcpy(test.username, buf);
// Oh happy day!
printf("%s", p->username);
return EXIT_SUCCESS;
}
}
return EXIT_FAILURE;
}
scanf%s,基本上是scanf衣服上的一个小玩意儿,几乎从来都不对。谢谢你的评论,我通常使用fGet。缺点是如果你经常使用它,可能会将可用内存碎片化。此外,可能会分配一个最小的块大小,比如说16字节或1024字节或其他什么,这会浪费精力。我会输入一个慷慨的本地字符串,该字符串将在函数退出时消失,并在结构中只分配一次内存,然后复制该字符串。请参阅strdup函数。@Brendan函数scanf确实提供了一种防止缓冲区溢出的方法。@WeatherVane:是的,从技术上讲,它有可选的支持,人们会忘记使用。u对于性能,malloc和realloc以及新的和。。。应该完全避免uu你应该/你建议我什么时候使用它们?另一件事,当我对char test[50]进行declat并在其中输入整数时,整数会像往常一样取2字节还是取1字节作为一个字符?@Mohamed:性能和开发时间之间总是有一个折衷。当开发人员的时间比性能更重要时,使用malloc之类的工具。备选方案取决于具体的场景,可能使用局部变量,可能使用类似objstack的东西,可能分配一个数组,然后管理数组项,可能实现您自己的支持内存池的堆,…-那么像这样,username将准确获得所需的存储量-为什么只对两个玩家使用这个没有意义-当您执行test.username=mallocstrlenbuf+1时;您分配了字符数组所需的字节数,因此假设输入为20个字符,它将分配21个字节,但如果输入为10个整数,这将需要20个字节,但该函数将只分配1个字节…不确定我的问题是否清楚here@Mohamed但如果输入是10个整数,则需要20个字节,这一点尚不清楚。用户输入仅限于字符。1234\n是5个字符。Moha\n为5个字符@\n是5个字符。用户输入应该只包含字符,但如果用户出于任何原因决定输入整数,则会产生问题,因此我认为必须首先检查输入是否仅由characters@Mohamed如果用户出于任何原因决定输入整数,->让我们看一个较低的级别。用户不能输入整数。用户可以输入3个字符,如“4”、“2”和“\n”。FGET将读取为3个字符,并形成一个由“4”、“2”、“n”组成的字符串,并附加一个“\0”。即使代码使用scanf%d和&x,3个字符的输入也会被读取为3个字符并转换为int 42。42在x处保存。代码无法控制用户输入。没有魔法可以阻止任何打字。代码可以接受文本输入并转换为int、double、string等。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Reasonable upper bound
#define USERNAME_SIZEMAX 50
struct Player {
char* username;
int hp;
int mp;
};
int main(void) {
puts("Enter user name");
// Recommend 2x - useful for leading/trailing spaces & detecting excessive long inputs.
char buf[USERNAME_SIZEMAX * 50];
if (fgets(buf, sizeof buf, stdin) == NULL) {
puts("No input");
} else {
trim(buf); // TBD code to lop off leading/trailing spaces
if (!valid_name(buf)) { // TBD code to validate the `name`
printf("Bad input \"%s\"\n", buf);
} else {
struct Player test = { 0 }; // fully populate
test.username = malloc(strlen(buf) + 1);
// Maybe add NULL check here
strcpy(test.username, buf);
// Oh happy day!
printf("%s", p->username);
return EXIT_SUCCESS;
}
}
return EXIT_FAILURE;
}