我正试着深入了解以下几行:
char* filename="file.txt";
我在使用fopen()
;
我的问题是:
filename
应该包含一个字符的地址(core2Duo中的36位)。我们为什么要在其中添加“字符串”?
为什么编译器不会生成错误,因为您可能存储了一个永远不存在的地址?
答案 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
中的字符串是比较复杂的数据结构,然后是简单的值变量,如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[]
。
要了解两个声明中的更多差异,请阅读: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)
fopen
的调用实际上是在运行。