"str" - "str" 在 C 中如何工作?它们是如何储存的?

How does "str" - "str" in C work? How are they stored?

免责声明:此问题询问 "str literal" + "str literal" 如何工作

关于 'a' + 'b''9' - '0' = 9 ('character' + 'character') 的工作原理:


问题:

致所有更熟悉 C 语言的人,感谢阅读

(用 clang 编译,标准=C11)

Example:

(trying to print __FILE__ without its ".c" extension)

printf("%s\n", __FILE__); returns filename.c

printf("%.*s\n", (int)(".c" - __FILE__), __FILE__); returns filename

1. How does C typecast string/string literals to int? Are whitespaces ignored?

  • What does the value of an (int)"string" represent?

Another example:

(int)("word" - "rd") = 6273
(int)("rd" - "word") = -6273
(int)("word" - "  rd") = -5
(int)("  rd" - "word") = -5

Why does (int)(".c" - __FILE__) even work?

3. 上面的 printf 功能真的有效吗?

4.有没有等价于'a' + 1 = 'b'的字符串?

提前谢谢大家!



无关猜测:

1 为什么 (int)(".c" - __FILE__) 甚至有效?

猜中

  some value of (first?) pointer to ".c" 
- some value of (first?) pointer to __FILE__ string literal 

2.(int)"string"的值究竟代表什么?

同上,但这是另一个例子:

printf("%i", (int)".c");
printf("%i", (int)__FILE__);
printf("%i", (int)(".c" - __FILE__));
printf("%i", (int)(__FILE__ - ".c"));
printf("%./*i", (int)(".c" - __FILE__), (int)__FILE__);
printf("%./*i", (int)(".c" - __FILE__), (int)("c" - __FILE__));

output
---------------------
(int) ".c"= 4357629
(int) __FILE__= 4357620
(int) (".c" - __FILE__) : 9
(int) (__FILE__ - ".c"): -9
(int with precision specified) __FILE__ : 004357620
(int with precision specified) (".c" - __FILE__): 000000009 
$

3. printf 真的有效吗?

假设是,可能:

printf("%.*s",(int)(".c" - __FILE__), __FILE__)
    width = (int)(".c" - __FILE__)   
    specifier/str = __FILE__

printf__FILE__ 打印为宽度为 (".c" - __FILE__) 的字符串(少两个字符)

一个字符串(带双引号),例如"abc",在用作表达式 时被转换为指针。如果你给一个指针加上一个整数,你会得到一个新的指针。如果你减去两个兼容的指针,你会得到一个整数。

一个字符(带单引号),例如'x',只是一个整数。您可以像任何其他整数一样添加、减去它们等。

__FILE__ 扩展为字符串,因此 ".c" - __FILE__ 是两个指针相减所得的整数。

如果您将指针转换为一个整数,您将得到一个整数。

请记住,一些涉及指针的表达式可能没有明确定义,但数据类型是。

您的示例中发生了三件事。

首先在 C 指针算术规则中,两个指针相减可以得出两个指针之间 地址 的差值。例如:

char test[2] ;
char* t1 = &test[0] ;
char* t2 = &test[1] ;
ptrdiff_t d = t2 - t1  ; // d == 1

其中 ptrdiff_t 是一个整数类型,能够保存 any 两个指针之间的差异。转换为 int 可能会出错,因为对于 32 位 int 它将仅跨越 2Gb - 因此不太可能出现错误。

发生的第二件事是,在表达式中使用诸如 "word" 的字符串文字是指向字符串内容的 指针

发生的第三件事是您的linker 执行了重复字符串消除。它已详尽地搜索您的代码以查找相同的字符串文字,并用单个指针替换它们。您观察的这一部分取决于实现,可能不适用于所有工具链,甚至可能不适用于具有不同 compiler/linker 设置的同一工具链。

内置宏__FILE__是一个字符串文字,包含实例化它的源文件的名称。在示例中:

(int)(".c" - __FILE__)

__FILE__ == "filename.c" 并且 linker 在其中找到重复的 ".c"(它必须在末尾,因为 nul 终止符必须匹配)。所以两个指针值的差是8("filename"的长度)。所以声明:

printf("%.*s\n", (int)(".c" - __FILE__), __FILE__);

打印字符串 "filename.c" 的前 8 个字符,即 "filename".

发生了更复杂的事情:

(int)("word" - "rd") = 6273
(int)("rd" - "word") = -6273
(int)("word" - "  rd") = -5
(int)("  rd" - "word") = -5 

在第一种和第二种情况下,您可能会从第一个 __FILE__ 示例中分别期望 -2 和 2,但是除了在这种情况下 linker 可能与"rd"" rd" 字符串的结尾而不是 "word" 的结尾。 linker 行为是实现定义的并且是不确定的。结果可能会有所不同,例如,如果您删除了第三个和第四个表达式以使字符串文字不再存在。可能会引用来自完全不同 link 模块的字符串。

重点是您不能完全依赖此 undefined/implementation 行为(字符串消除即指针算术和文字字符串指针行为已明确定义)。作为对 linker 行为的检查很有趣,但作为编程技术没有用。