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

时间:2013-07-13 05:52:05

标签: c

我正试着深入了解以下几行:

char* filename="file.txt";    

我在使用fopen();

时这样做

我的问题是:

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

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

4 个答案:

答案 0 :(得分: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<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中的字符串是比较复杂的数据结构,然后是简单的值变量,如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[]

要了解两个声明中的更多差异,请阅读:What does sizeof(&arr) return?

字符串文字可能会在您使用它的上下文中为您提供值或地址依赖性。例如,尝试: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分配了可能不存在的虚假地址。

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

好吧,编译example-2代码很好,ptr被分配了一个伪地址,编译器也不会生成任何错误/警告,即使使用了stick check flags选项:-Wall -pedantic

但执行此代码是错误的。它尝试在printf语句中访问分配给ptr的内存地址,并且程序将表现异常(在不同的执行实例中)。在C - 语言标准中,其名称为Undefined behavior

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

了解并了解访问非法内存时可能发生的情况:strcat() implementation works but causes a core dump at the end

答案 1 :(得分:1)

  

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

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

  

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

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

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

答案 2 :(得分:1)

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

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

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

你需要明确地要求编译器提供所有警告(例如Linux上的gcc -Wall)来获取它们。

答案 3 :(得分:1)

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