所以我现在正在练习和学习C,遇到了CodeWars的一个相当简单的挑战,要求打印出一串"Aa~","Pa!"和"Aa!",这取决于n是否<= 6。我知道我们如何用数组做到这一点,但为了提高效率,我想尝试使用动态分配的字符数组,例如使用 malloc。
我想确保我通过这些问题掌握基础知识
-
所以我知道在其他带有 int 的 malloc 示例中,我们设置了一个指针(int 类型)来指向分配的内存块。当我声明"char *ptr"指向分配的内存块时,它仍然是一个指针,因为请原谅我,我认为"char *anything"意味着它是表示字符串的约定。所以不确定如果我没有像我想象的那样设置像"char **ptr"这样的指针,为什么下面的工作有些。
-
为什么当我尝试返回答案时,尤其是当"val"为 1 或 0 时,我会收到"对象 0x100000fab 的 malloc:*** 错误:未分配被释放的指针"类型的错误?我在某处读到将指针(字符串?),答案更改为NULL将解决问题,但不完全确定为什么会起作用。
3 为了继续上述问题,为了释放空间,如果我们在一个函数中动态分配一个内存块,但需要从该函数返回一个值,最好的方法是什么?例如,我们是释放之后还是之前的空间?
谢谢大家的投入。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define val 1
char *sc(int); // function declaration/prototype
int main(int argc, const char * argv[]) {
char *answer = sc(val);
printf("The answer is %sn", answer);
answer = NULL; // why does this work
free(answer);
return 0;
}
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
char *ptr = (char*) malloc(n*4);
if (ptr == NULL){
printf("malloc failed");
}
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
ptr = "";
}
else {
printf("Error!");
exit(0);
}
return ptr;
}
代码的主要问题是你没有从malloc()
那里得到的缓冲区清零,所以第一个strcat()
不一定写在字符串的开头,而是写在最后。
您可以在malloc()
调用后立即使用strcpy()
来解决此问题并检查:
strcpy(ptr, "");
或者,等效地,您可以将缓冲区的第一个字节设置为零。由于 C 字符串是以零结尾的字符串,因此将字符设置为零将指示它位于末尾:
ptr[0] = 0;
您似乎也分配了太短的缓冲区。如果您写入Aa~
(4 个字节)的n-1
副本加上一个Pa! Aa!
的副本(包含终止零时为 8 个字节!),您实际上需要4 * (n+1)
作为空间。因此,要么始终分配它,要么这样做n < 6
以防您需要额外的字节。
这也是一个问题:
ptr = "";
因为现在您的ptr
不再指向malloc()
返回的缓冲区,而是指向二进制文件中的静态(空)字符串。这很可能是你从free()
那里得到麻烦的地方,因为在你的二进制文件中的静态字符串上调用它肯定是错误的。
此外,设置ptr = ""
后,您将不再引用您分配的缓冲区,这意味着您很可能刚刚创建了内存泄漏!
在这种情况下,您应该简单地使用strcpy()
或将第一个字节设置为零。但是,如果您在程序开始时执行此操作,则无需在此处执行此操作。
最后,free(NULL);
工作(如 in,不会引发错误),因为这是其规范的一部分,您可以向它传递一个 NULL 指针,它什么也不做。但请注意,它不会释放您分配的缓冲区,因此此处也有内存泄漏。
我会进一步重构代码的第二部分,这样你就不会有太多重复的附加字符串:
char *sc(int n) {
/* if n <= 6 then will have an extra "Aa!"
* after "Pa!" at the nth position.
*/
char *ptr;
if (n < 0) {
printf("Error!");
return NULL;
}
ptr = (char*) malloc(4 * (n+1));
if (ptr == NULL){
printf("malloc failed");
return NULL;
}
strcpy(ptr, "");
if (n > 1) {
for (int i = 1; i < n; i++){
strcat(ptr, "Aa~ ");
}
strcat(ptr, "Pa!");
if (n <= 6) {
strcat(ptr, " Ah!");
}
}
return ptr;
}
另请注意,您不需要每次都strcat()
的结果赋值回ptr
,因为它总是返回其第一个参数,因此在那里分配它实际上不会改变任何东西。
- 我认为"char *anything"意味着它是表示字符串的约定。所以不确定如果我没有像我想象的那样设置像"char **ptr"这样的指针,为什么下面的工作有些。
您正在为字符串分配内存,因此只需要char *
.char **
将用于包含多个字符串的数组,或指向包含指向字符串的指针的变量的指针。
- 为什么我收到"对象 0x100000fab 的 malloc:*** 错误:未分配正在释放的指针">
当你做ptr = "";
时,你会得到它。执行此操作后,ptr
不再指向分配的内存malloc
,它指向该字符串文本。如果要将分配的内存设置为空字符串,可以
ptr[0] = ' ';
这会在字符串的第一个元素中放置一个空终止符。
您还需要在使用strcat()
追加到字符串的代码之前执行此操作。否则,将追加到未初始化的数据。最简单的方法是在分配内存后立即执行此操作(然后您在n <= 1
块中不需要它。
不需要else
块。除了您测试的 3 种可能性之外,没有其他可能性,除非 CPU 出现故障(在这种情况下,所有赌注都关闭)。但是,您应该在调用malloc()
之前检查n < 1
,因为您无法分配负内存,并且malloc(0)
可能会返回NULL
。
当你为ptr
分配空间时,你需要为字符串的终止空值添加 1 个字节。
char *sc(int n) {
// if n < 6 then will have an extra "Aa!" after "Pa!" at the nth position
if (n >= 1) {
char *ptr = malloc(n*4 + 1);
} else {
char *ptr = malloc(1);
}
if (ptr == NULL){
printf("malloc failed");
exit(1);
}
ptr[0] = ' '; // initialize empty string
char *first = "Aa~ ";
char *second = "Pa! Aa!";
char *third = "Pa!";
if (n <= 6 && n >1) {
for (int i = 0; i <n-1; i++){
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, second);
}
else if (n> 6){
for (int i = 1; i < n; i++) {
ptr = strcat(ptr, first);
}
ptr = strcat(ptr, third);
}
else if (n <= 1){
// nothing to do
}
return ptr;
}