为什么我们将字符串存储在字符指针中,例如在fopen()中?
我想更深入地了解以下内容:
char* filename="file.txt";
我在使用fopen()
时这样做;
我的问题是:
-
filename
应该包含一个字符的地址 (core2Duo中的36位)。 我们为什么要在其中添加“ 字符串 ”? -
为什么编译器不会生成错误,因为您可能存储了一个永远不存在的地址?
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
中的字符串是复杂的数据结构,然后是简单的值变量,如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)返回什么?
字符串文字可以在您使用它的上下文中为您提供值或地址依赖性。 例如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
的指针,因为修改它是非法的(导致未定义的行为)。 读这个。
为什么编译器不会生成错误,因为您可能存储了一个永远不存在的地址?
对不起,你在谈论什么样的永不存在的地址? 字符串文字中第一个字符的地址始终显式有效!
(但即使它是 – 编译器对语义也知之甚少。如果有可能使用无效指针,它根本不可能总是警告你。当然,有时它可以,但没有很高的期望。)
- 是的,它正在持有一个地址,但它也恰好是地址后面是隔壁邻居的地址,以及它们的隔壁邻居的地址等。通过这个“字符串”的地址,值是实际发现。
- 编译器不应该生成错误,因为它很可能会在将来的某个时刻创建“filename.txt”文件,并且在程序实际运行之前不会评估对
fopen
调用。
请阅读一些优秀的C编程书籍,他们解释什么是指针和数组。
像"file.txt"
这样的字符串文字是一个char[]
数组(但你应该把它想象成const char[]
因为在字符串文字中分配如"abc"[1]='D';
是不确定的行为 ,味道不好,和gcc -Wall
会警告你)。 数组可以(并且通常是)被理解为指向它们的第一个单元格(索引0)的指针 – 换句话说,数组被衰减为指针 – 因此您可以将字符串文字指定给char
指针。
但是,数组不是C中的指针; 例如,某个数组的sizeof
是每个元素大小的合适倍数,特别是sizeof("abcde") == 6
(因为每个字符串文字中的终止空字节)。 但是某些指针的大小与指向区域的大小无关,并且在大多数系统中,所有指针都具有相同的大小,即机器字的大小。
您需要明确地向编译器询问所有警告(例如Linux上的gcc -Wall
)以获取它们。