为什么我们将字符串存储在字符指针中,例如在fopen()中?

我想更深入地了解以下内容:

char* filename="file.txt"; 

我在使用fopen()时这样做;

我的问题是:

  1. filename应该包含一个字符的地址 (core2Duo中的36位)。 我们为什么要在其中添加“ 字符串 ”?

  2. 为什么编译器不会生成错误,因为您可能存储了一个永远不存在的地址?

filename应该包含一个字符的地址(core2Duo中的36位),为什么我们在其中放入一个’字符串’?

表达式char* filename="file.txt"; 是一个有效的表达。 Reason是字符串文字的类型,C是char[N] ,可以很容易地衰减成char*char[N]char*是兼容的。 因此,您可以将字符串地址分配给char*指针变量,就像在此表达式中一样。

为什么编译器不会生成错误,因为您可能存储了一个永远不存在的地址?

请阅读以下三点:

  • 它是一个有效的地址:

不,在表达式char* filename="file.txt";filename被分配一个有效的地址。 它的概念类似(假设地址从21开始):

 filename 21 22 23 24 25 26 27 28 29 +---+ +------------------------------------+ |21 |---> |'f'|'i'|'l'|'e'|'.'|'t'|'x'|'t'|'\0'| +---+ +------------------------------------+ filename pointing to string, a valid address. 

它没有给出任何错误或警告:尝试下面的代码示例:

例如-1:

 #include int main(int argc, char **argv){ char* filename = "filename.txt"; printf("%s", filename); return 0; } 

编译它:

 :~$ gcc yc -Wall -pedantic ~$ ./a.out filename.txt 

没有错误和警告,它的编译和执行完美。

关于你评论我的回答:

C中的字符串是复杂的数据结构,然后是简单的值变量,如intcharfloat

如果是基本数据类型:

 int i = 5; You are assigning value 5, to variable i char c = 'A'; You are assigning value 'A' to char variable c. float f = 5.5f; You are assigning value 5.5f to float variable f. 

但对于字符串,当你这样做时:

  char* filename = "filename.txt"; 

然后,您实际上将字符串"filename.txt"地址分配给char *指针变量filename

如果你这样做:

  char filename[] = "filename.txt"; // ^ notice [] in declaration 

然后你分配字符串“filename.txt”; 到char filename[]的数组,这里的filename类型是char[]

要了解两个声明中的更多差异,请阅读: sizeof(&arr)返回什么?

字符串文字可以在您使用它的上下文中为您提供值或地址依赖性。 例如try: printf(" address: %p, value: %s", "Hello", "Hello");

  • 编译器不validation地址但检查语法。

编译器不负责validation地址是否合法。 编译器移植代码并检查语法错误(例如,不可编译的类型不匹配)。 假设你为一个指针分配一个假地址,它就不会给你一个警告(如果你正确输入了强制转换地址)。 考虑以下示例:

例如-2:

 #include int main(int argc, char **argv){ char* filename = "filename.txt"; char* ptr = (char*)0x020202; printf("%s %s\n", filename, ptr); return 0; } 

编译:

 $ gcc yc -Wall -pedantic 

它不会产生任何错误或警告。 因为语法上所有都是好的和有效的。
(而ptr分配了一个可能不存在的虚假地址)。

  • 无效地址会在运行时导致未定义的行为

好吧,很好的编译示例2代码,其中ptr被赋予一个假地址,并且编译器不会生成任何错误/警告,即使使用棒检查标志选项: -Wall -pedantic

但执行此代码是错误的。 它尝试在printf语句中访问分配给ptr内存地址,并且程序将表现exception(在不同的执行实例中)。 在C -Language标准中,它称为: 未定义的行为 。

当您执行此代码时OS(但不是编译器)检测到进程的内存权限违规 – 对有效内存的无效访问给出:SIGSEGV访问无效地址给出:SIGBUS。 这可能会导致进程因某些分段错误和coredump而终止/崩溃。

要了解并了解访问非法内存时可能发生的情况,请执行以下操作 : strcat()实现可行,但最后会导致核心转储 。

filename应该包含一个字符的地址(core2Duo中的36位),为什么我们在其中放入一个’字符串’?

可悲的是,我们不是。 我们正在指向字符串文字的第一个字符。 字符串"file.txt"的类型为char[9] ,当分配给指针时,它会衰减为char * 。 但是你应该将它分配给指向const char的指针,因为修改它是非法的(导致未定义的行为)。 读这个。

为什么编译器不会生成错误,因为您可能存储了一个永远不存在的地址?

对不起,你在谈论什么样的永不存在的地址? 字符串文字中第一个字符的地址始终显式有效!

(但即使它是 – 编译器对语义也知之甚少。如果有可能使用无效指针,它根本不可能总是警告你。当然,有时它可以,但没有很高的期望。)

  1. 是的,它正在持有一个地址,但它也恰好是地址后面是隔壁邻居的地址,以及它们的隔壁邻居的地址等。通过这个“字符串”的地址,值是实际发现。
  2. 编译器不应该生成错误,因为它很可能会在将来的某个时刻创建“filename.txt”文件,并且在程序实际运行之前不会评估对fopen调用。

请阅读一些优秀的C编程书籍,他们解释什么是指针和数组。

"file.txt"这样的字符串文字是一个char[]数组(但你应该把它想象成const char[]因为在字符串文字中分配如"abc"[1]='D';是不确定的行为 ,味道不好,和gcc -Wall会警告你)。 数组可以(并且通常是)被理解为指向它们的第一个单元格(索引0)的指针 – 换句话说,数组被衰减为指针 – 因此您可以将字符串文字指定给char指针。

但是,数组不是C中的指针; 例如,某个数组的sizeof是每个元素大小的合适倍数,特别是sizeof("abcde") == 6 (因为每个字符串文字中的终止空字节)。 但是某些指针的大小与指向区域的大小无关,并且在大多数系统中,所有指针都具有相同的大小,即机器字的大小。

您需要明确地向编译器询问所有警告(例如Linux上的gcc -Wall )以获取它们。