无法解释C中简单字符串操作的输出
代码如下:无法解释C中简单字符串操作的输出,c,string,pointers,C,String,Pointers,代码如下: #include<stdio.h> #include<string.h> int main() { char *s = "name"; int n = strlen(s); int i; s = &s[n+1]; for(i=0; i<=n; i++) { printf("%d %c",i,*s);
#include<stdio.h>
#include<string.h>
int main()
{
char *s = "name";
int n = strlen(s);
int i;
s = &s[n+1];
for(i=0; i<=n; i++)
{
printf("%d %c",i,*s);
s++;
}
return 0;
}
我无法理解输出。尽管没有转义序列,为什么要打印%。这一行
s=&s[n+1]代码>会导致指针指向某个地方。之后,你开始从中读取随机垃圾。显然,随机垃圾包含一些%
字符。这一行s=&s[n+1]代码>会导致指针指向某个地方。之后,你开始从中读取随机垃圾。显然,随机垃圾包含一些%
字符。首先分配s=&s[n+1]
然后使用*s
访问printf中的超限内存。根据C标准,代码在未定义的行为下运行
s[]
的最大索引可以是包含\0
的字符串的长度。记住索引值从0
开始到(数组/字符串)-1的大小
您的字符串存储在内存中,如下所示:
s 23 24 25 26 27 28
+----+ +----+----+----+----+----+----+
| 23 | | n | a | m | e | \0 | ? |
+----+ +----+----+----+----+----+----+
0 1 2 3 4 5
s points to string "name"
string length of "name" is 4
length("name") + 1 = 5
? means garbage values
在表达式中s=&s[n+1]
,n+1
是五个5
,它指向“name”
字符串的分配内存之外的位置,并且在printf语句中,您使用*
解引用运算符访问内存会导致无效内存访问,并且此代码在运行时的行为是。这就是为什么在不同的执行过程中代码的行为会有所不同
您的代码可以正确编译,因为它在语法上是正确的,但在运行时,操作系统内核可以检测到对未分配内存的访问。这可能会导致操作系统内核向进程发送导致异常的内核转储信号。(值得注意的是:当操作系统检测到进程违反内存权限时——对有效内存的无效访问给出:SIGSEGV,对无效地址的访问给出:SIGBUS)。在值得一提的情况下,您的程序可以无故障地执行,它会产生垃圾结果 第一次赋值s=&s[n+1]
然后使用*s
访问printf中的超限内存。根据C标准,代码在未定义的行为下运行
s[]
的最大索引可以是包含\0
的字符串的长度。记住索引值从0
开始到(数组/字符串)-1的大小
您的字符串存储在内存中,如下所示:
s 23 24 25 26 27 28
+----+ +----+----+----+----+----+----+
| 23 | | n | a | m | e | \0 | ? |
+----+ +----+----+----+----+----+----+
0 1 2 3 4 5
s points to string "name"
string length of "name" is 4
length("name") + 1 = 5
? means garbage values
在表达式中s=&s[n+1]
,n+1
是五个5
,它指向“name”
字符串的分配内存之外的位置,并且在printf语句中,您使用*
解引用运算符访问内存会导致无效内存访问,并且此代码在运行时的行为是。这就是为什么在不同的执行过程中代码的行为会有所不同
您的代码可以正确编译,因为它在语法上是正确的,但在运行时,操作系统内核可以检测到对未分配内存的访问。这可能会导致操作系统内核向进程发送导致异常的内核转储信号。(值得注意的是:当操作系统检测到进程违反内存权限时——对有效内存的无效访问给出:SIGSEGV,对无效地址的访问给出:SIGBUS)。在值得一提的情况下,您的程序可以无故障地执行,它会产生垃圾结果 s=&s[n+1]代码>是来自越界的赋值。s[n]的值为'\0',在此之后,s[n+1]
将有一些垃圾值
上面显示的分配是将s[n+1]
的基址分配给s
,稍后您将尝试打印此新s
中的值,因此所有值都将是垃圾。越界访问是未定义的行为。s=&s[n+1]代码>是来自越界的赋值。s[n]的值为'\0',在此之后,s[n+1]
将有一些垃圾值
s = &s[n+1];
上面显示的分配是将s[n+1]
的基址分配给s
,稍后您将尝试打印此新s
中的值,因此所有值都将是垃圾。越界访问是未定义的行为
s = &s[n+1];
使s
指向未知内存。此后引用s
将调用未定义的行为,当您在printf
中访问它时,可能会发生任何事情
使s
指向未知内存。此后引用s
会调用未定义的行为,当您在printf
中访问它时,可能会发生任何事情,因为无论何时执行s[n+1]
,其中n
是字符串的长度。此外,您正在将此新地址再次分配到s
。从这个位置开始访问每个索引将导致未定义的行为,因为您不知道这些位置上有什么,并且您是否可以访问它
您可以尝试在定义的字符串之后立即定义另一个字符串
char *s = "name";
char *d = "hello test";
在这种情况下,如果编译器恰好将字符串“name”之后的字符串存储在只读区域中,则可能会打印字符串“hello test”中的字符。这不是保证
底线是,这段代码不正确,会导致未定义的行为。未定义的行为,因为无论何时执行s[n+1]
,其中n
是字符串的长度。此外,您正在将此新地址再次分配到s
。从这个位置开始访问每个索引将导致未定义的行为,因为您不知道这些位置上有什么,并且您是否可以访问它
s = &s[n+1];
您可以尝试在定义的字符串之后立即定义另一个字符串
char *s = "name";
char *d = "hello test";
在这种情况下,如果编译器恰好将字符串“name”之后的字符串存储在只读区域中,则可能会打印字符串“hello test”中的字符。这不是保证
底线是,这段代码不是正确的
#include <stdio.h>
int main () {
const char *s = "%d %c";
int i;
for (i = 0; i < 5; ++i) {
printf("%d %c", i, *s);
s++;
}
puts("");
return 0;
}
/* output is:
0 %1 d2 3 %4 c
*/
0 %
1 d
2
3 %
4 c
n = 4;
s = &s[n + 1] = &s[5];