为什么我不能返回 NULL?

why can't I return NULL?

提问人:asdfg 提问时间:3/3/2023 最后编辑:asdfg 更新时间:3/4/2023 访问量:102

问:

我是一个初学者,试图在 c 语言中学习动态内存分配。

如果文本中没有任何内容,我尝试返回 NULL,如果它包含某些内容,我想返回文本。

char* check_for_NULL(const char *text){
    if(text == NULL){
        return NULL;
    }
...
}

(程序的其余部分按预期工作)

如果我把 NULL 放在我称之为它的地方:

int main(){
char *check_NULL;

    check_NULL = check_for_NULL(NULL);
    printf("%s", check_NULL);
    free(check_NULL);

}

我得到分段错误而不是 null。

当我在 valgrind 中运行时,我得到的结果:

Invalid read of size 1

Address 0x0 is not stack'd malloc'd or (recently) free'd

尝试使用 calloc 为 NULL 腾出空间,尝试放置 0 而不是 NULL,但没有任何效果

看到一个类似的帖子:

Valgrind:大小 1 的读取无效

但没有得到修复是什么。

c 指针 null dynamic-memory-allocation

评论

0赞 Jerry Jeremiah 3/3/2023
check_NULL = char* check_for_NULL(NULL);不是瓦洛德。你可能想要但是如果你这样做,你必须在你这样做之前检查NULLcheck_NULL = check_for_NULL(NULL);printf("%s", check_NULL); free(check_NULL);
3赞 Vlad from Moscow 3/3/2023
@asdfg 很明显,这个语句 printf(“%s”, check_NULL);当指针等于 NULL 时调用未定义的行为。那么问题出在哪里呢?
0赞 asdfg 3/3/2023
是的,很抱歉,这是一个错别字,无法从虚拟机复制它
0赞 pmacfarlane 3/3/2023
创建测试指针是否为 NULL 的函数是没有意义的。你说“如果文本中没有任何内容”——你是在测试它是一个空字符串吗?text[0] == '\0'
2赞 Vlad from Moscow 3/3/2023
@asdfg 不,不应该。它不输出指针。它 true 输出由访问内存的指针指向的字符串,其地址作为值存储在指针中。

答:

1赞 Vlad from Moscow 3/3/2023 #1

如果指针等于则此语句check_NULLNULL

printf("%s", check_NULL);

调用未定义的行为。

至少你应该写

if ( check_NULL != NULL ) printf("%s", check_NULL);

请注意这一点,以使此陈述正确

free(check_NULL)

该函数应返回 null 指针或指向动态分配内存的指针。

注意这句话

文本中没有任何内容,

表示将空字符串传递给函数。要检查字符串是否为空,您应该编写例如

if ( text[0] == '\0' )
//...

if ( !*text )
//...
0赞 pmacfarlane 3/3/2023 #2

看来你想要这样的东西:

char* check_for_NULL(const char *text){
    if(text == NULL){
        return "(null)";
    }
...
}

C 不像 Ruby 或 Python 或其他什么。它不会通过魔术将 NULL 指针转换为字符串。

您还调用了未动态分配的指针。您不得明确释放这些内容;您只能释放通过 和 分配的内容。字符串文字,如永远存在,无法释放。free()malloc()calloc()realloc()"(null)"

1赞 Fe2O3 3/3/2023 #3

另一种解决方案可以取代您的:printf()

printf( "%s", check_NULL ? check_NULL : "(null)" );
2赞 Noam Bechhofer 3/3/2023 #4

这里的其他答案都很好。实质上,不能取消引用 NULL。我还想对此给出一些直觉。

回想一下,在 C 语言中,字符串只是内存中的字节序列(表示 ASCII 字符)。我们可以将内存中的任何地方(它本身只是一个长字节序列)视为一个字符串,一旦我们命中值为 0(字符)的字节,我们将计算要终止的字符串。'\0'

为了说明这一点,让我们看一个示例实现:strlen

int strlen(char *str) {
    int len = -1;
    // will evaluate to 0 (false is just 0 in C) when we reach a null terminator.
    while (str[++len]);

    return len;
}

我们从引用的地址开始,不断迭代内存,直到找到 0 值,每次递增长度。str

请记住,NULL 是 0,因此当您选择将其作为指针传递时,它会引用 address 。#define0x0

首先,你的函数本质上是说:拿一个指针,如果它的地址是 NULL,则返回 NULL。否则,返回其地址。看看这是无关紧要的吗?该函数只接受一个指针,然后吐出相同的指针。check_for_NULLtext.

现在,您决定传递并打印生成的字符串。 想要将其参数视为字符串,换句话说,将其视为 ,并将开始取消引用该指针的值,直到它读取值为 0 的字节(“null terminator”, char )。这里的问题是这意味着 printf 取消引用的第一个值是在 address ,我们不允许取消引用。NULLcheck_for_NULL%schar *'\0'0x0

了解该地址不是我们使用的有效地址。这就是我们使用指针来表示错误、无效值等的原因。这是理想的垃圾价值,因为它永远不会不是垃圾。0x0NULL

几个侧面要点:

使用 -g 标志编译程序(对于 gcc,假设这是您正在使用的编译器)以添加调试信息。如果这样做,valgrind 将指向代码行中问题的确切行,这使得调试变得更加容易。

不要 free() 。您在堆指针上使用 free(),而不是堆栈指针。如果你还不明白这一点,只要知道除非指针是由(或任何其他函数)提供给你的,否则它不需要被释放。check_NULLmalloc()alloc

有趣的小旁观:总是等价于 .我的高级编程教授称其为“指针的大统一定理”。arr[i]*(arr + i)