关于C#:char *导致segfault但char []没有

char* leads to segfault but char[] doesn't

本问题已经有最佳答案,请猛点这里访问。

我想我知道自己问题的答案,但我想确认一下,我完全理解这个问题。

我编写了一个返回字符串的函数。我传递一个char*作为参数,函数修改指针。

它工作正常,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void get_file_name(char* file_name_out)
{
    char file_name[12+1];
    char dir_name[50+12+1];

    strcpy(file_name,"name.xml");
    strcpy(dir_name,"/home/user/foo/bar/");
    strcat(dir_name, file_name);

    strcpy(file_name_out, dir_name); // Clarity - equivalent to a return
}

int main()
{
    char file_name[100];

    get_file_name(file_name);
    printf(file_name);

    return 0;
}

但是如果我用char *filename;char *filename ="";替换char file_name[100];的话,我会在strcpy()中发现一个分段错误。

我不知道为什么?

我的函数以char*为参数,strcpy()也是。

据我所知,char *filename ="";创建了一个只读字符串。strcpy()正试图写入只读变量,这是不允许的,因此错误是有意义的。

但是当我写char *filename;时会发生什么?我的猜测是,堆栈上分配了足够的空间来容纳指向char的指针,因此我只能在file_name_out点的地方编写一个字符。调用strcpy()将尝试写入至少2,因此会出现错误。

它将解释以下代码编译并生成预期输出的原因:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void foo(char* a, char* b)
{
    *a = *b;
}

int main()
{
    char a = 'A', b = 'B';
    printf("a = %c, b = %c
"
, a, b);
    foo(&a, &b);
    printf("a = %c, b = %c
"
, a, b);
    return 0;
}

另一方面,如果我使用char file_name[100];,我会在堆栈上为100个字符分配足够的空间,这样strcpy()就可以愉快地写入file_name_out

我说的对吗?


As far as I understand, char *filename =""; creates a read-only
string. strcpy() is then trying to write into a read-only variable,
which is not allowed so the error makes sense.

是的,没错。它本质上不同于声明字符数组。将字符指针初始化为字符串文字将使其只读;尝试更改字符串的内容将导致UB。

But what happens when I write char *filename; ? My guess is that
enough space to fit a pointer to a char is allocated on the stack, so
I could write only one single character into my file_name_out
variable.

您分配了足够的空间来存储指向字符的指针,就这样。您不能写入*filename,甚至不能写入单个字符,因为您没有分配空间来存储*filename指向的内容。如果要更改filename指向的内容,首先必须初始化它以指向有效的地方。


我想这里的问题是

1
char string[100];

将内存分配给字符串-您可以使用字符串作为指针访问该字符串但是

1
char * string;

不会为字符串分配任何内存,因此会出现SEG故障。

为了获得你可以使用的记忆

1
string = calloc(100,sizeo(char));

例如,但是您需要在末尾记住,以便用

1
free(string);

或者你可能会有内存泄漏。

另一个内存分配路由使用malloc

所以总结一下

1
char string[100];

等于

1
2
3
4
char * string;
string = calloc(100,sizeo(char));
...
free(string);

尽管严格地说calloc将所有元素初始化为零,但是在字符串[100]decalation中,除非使用String〔100〕={}如果使用malloc来对内存进行分级,则内容是未定义的。

@paulrooney提出的另一点是char字符串[100]在堆栈上分配内存,而calloc使用堆。有关堆和堆栈的详细信息,请参阅此问题和答案…


1
char *filename;

什么都不分配。它只是指向一个未指定位置的指针(该值是以前内存中的任何值)。使用这个指针永远不会起作用,因为它可能指向您的程序允许使用的内存范围之外。

1
char *filename ="";

指向程序数据段的一部分。正如您已经说过的,它是只读的,因此试图更改它会导致segfault。

在最后一个示例中,您处理的是单个char值,而不是char值的字符串,并且您的函数foo将它们视为这样。因此char*值指向的缓冲区长度没有问题。


char file_name[100];创建一个100个字符的连续数组。在这种情况下,file_name是类型(char*)的指针,它指向数组中的第一个元素。

char* file_name;创建一个指针。但是,它没有初始化为特定的内存地址。此外,此表达式不分配内存。