-8

我正在尝试更深入地研究以下行:

char* filename="file.txt";    

当我们使用时我会这样做fopen()

我的问题是:

  1. filename应该保存一个字符的地址(core2Duo 中的 36 位)。为什么我们要在里面放一个“字符串”?

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

4

4 回答 4

2

文件名应该包含一个字符的地址(core2Duo 中的 36 位),为什么我们要在其中放入一个“字符串”?

该表达式char* filename="file.txt";是一个有效的表达式。原因是 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<stdio.h>
int main(int argc, char **argv){
    char* filename = "filename.txt";
    printf("%s", filename);
    return 0;
}

编译它:

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

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

关于您对我的回答的评论:

中的字符串C是位复杂的数据结构,然后是简单的值变量,如int, char, float.

对于基本数据类型:

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) 返回什么?

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

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

编译器不负责验证地址是否合法。编译器移植代码并检查语法错误(例如不可编译的类型不匹配)。假设如果您将假地址分配给指针,它不会给您警告(如果您正确键入强制转换地址)。考虑下面的例子:

示例 2:

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

编译:

$ gcc y.c  -Wall -pedantic 

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

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

好吧,编译ptr分配了假地址的 example-2 代码很好,即使使用棒检查标志选项,编译器也不会生成任何错误/警告:-Wall -pedantic.

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

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

要了解并了解访问非法内存时会发生什么,请阅读:strcat() 实现有效,但最后会导致核心转储

于 2013-07-13T05:55:33.393 回答
1

文件名应该包含一个字符的地址(core2Duo 中的 36 位),为什么我们要在其中放入一个“字符串”?

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

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

打扰一下,但是你在说什么不存在的地址?字符串文字中第一个字符的地址始终明确有效!

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

于 2013-07-13T05:54:53.813 回答
1

请阅读一些不错的 C 编程书籍,它们解释了什么是指针和数组。

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

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

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

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