【问题标题】:Modifying String Literal [duplicate]修改字符串文字[重复]
【发布时间】:2018-08-04 23:49:40
【问题描述】:

为什么我运行这个程序没有输出。

#include<stdio.h>

int main()
{
    char* t="C++";
    t[1]='p';
    t[2]='p';
    printf("%s",t);
    return 0;
}

【问题讨论】:

  • main 返回int,而不是void。
  • 如果没有'\n',printf() 输出可能不会在下一个语句执行之前显示(流 stdout,默认情况下是行缓冲的)。试试printf("%s\n", t); 或printf("%s", t); fflush(stdout);
  • @pmg:文本输出流是否需要关闭'\n' 由实现定义。如果是,则行为未定义,即使使用fflush(stdout)。仅打印换行符就更容易、更可靠。 (在许多系统上,不需要换行符,但如果缺少它,输出将与下一个 shell 提示符相邻,这很难看。)
  • 我会敦促你接受基思的回答,这是一个更好的答案,我已经投了赞成票。

标签: c


【解决方案1】:

一个 C 字符串文字创建一个匿名数组 char。任何修改该数组的尝试都有未定义的行为。理想情况下,这将通过创建数组 const 来强制执行,但 C 并不总是有 const,将其添加到字符串文字会破坏现有代码。

char* t="C++";

这是合法的,但可能存在风险。包含字符'C', '+', '+', '\0' 的数组可以存储在读写内存或只读内存中,由编译器决定。

t[1]='p';

这里你的程序的行为是未定义的,因为你试图修改一个字符串文字的内容。编译器不需要在编译时或运行时警告您,也不需要做任何事情来使其“工作”。

如果想让编译器知道字符串是只读的,最好自己加上const限定符:

const char *t = "C++";

如果您尝试修改字符串文字,编译器至少应该警告您 - 至少如果您尝试通过 t 这样做。

如果你希望能够修改它,你应该将t设为可写数组:

char t[] = "C++";

不是将t 设为指向"C++" 开头的指针,而是将t 设为一个数组,将"C++" 的内容复制到其中。你可以对t的内容做你喜欢的事,只要你不超出它的范围。

您的代码中还有一些 cmets:

#include<conio.h>

&lt;conio.h&gt; 特定于 Windows(和 MS-DOS)。如果您不需要您的程序在任何其他系统上运行,那很好。如果您希望它便携,请将其移除。

void main()

这是错误的;正确的声明是 int main(void)(int main() 在 C 中是有问题的,但在 C++ 中是正确的。)

printf("%s",t);

您的输出应以换行符结尾;如果不这样做,可能会发生各种不好的事情。做这个:

printf("%s\n", t);

(问题最初包括在结束}之前的这一行:

getch();

OP后来将其删除。这是特定于 Windows 的。可能有必要在程序完成时阻止您的输出窗口关闭,这是 Windows 开发系统的一个不幸问题。如果您想要更标准的方式来执行此操作,getchar() 只需从标准输入中读取一个字符,然后让您按 Enter 即可完成(尽管它不会给您提示)。或者,如果您从 IDE 或命令提示符运行程序,它们中的大多数都不会立即关闭窗口。)

最后,由于main 返回了int 类型的结果,它实际上应该这样做;你可以添加

return 0;

在关闭} 之前。这并不是真正需要的,但这不是一个坏主意。 (C99 增加了一个隐含的return 0;,但微软不支持C99。)(2019 年更新:微软对C99 功能的支持稍微好一点。我不确定return 0; 是否有必要。)

【讨论】:

  • 对于char s[] = "foobar";,是在编译期间还是在运行时将字符串文字内容复制到数组中?
  • @torez233 是的。在抽象机器中,字符串文字"foobar" 导致存在char[7] 类型的静态数组,其内容被复制到s,它也是char[7] 类型。根据s 的定义方式(特别是它是否是静态的),只要结果行为正确,编译器就可以生成不执行复制的代码。没有任何东西可以引用字符串字面量对应的匿名数组的地址,所以它不需要实际存在于内存中。
【解决方案2】:

“C++”是存储在只读位置中的字符串文字,因此无法修改。有了这个-

char* t="C++"; // t is pointing to a string literal stored in read only location

相反,你应该有 -

char t[] = "C++" ;  // Copying the string literal to array t

实际去做-

t[1] = 'p' ;

【讨论】:

  • 如果没有错误,为什么我们不会收到警告?代码编译正确,但执行后应用程序崩溃。
【解决方案3】:

您的代码还有其他几个问题。

  1. 指针通常用于指向已经存在的数据,所以可以这样使用

    char arr[] = "C++";

    char* t = &arr[0];

也可以修改,

t[1] = 'p';

t[2] = 'p';

当然还有一种特殊的使用字符串的方式——让指针指向一个字符串常量。就像你使用的方式:

char *t = "C++";   // you cannot modify it in most operating systems
t[1] = 'p';
t[2] = 'p';

有更好的使用方式,更便携,更容易理解:

const char* t="C++"; 

2.你的代码有很多c标准中没有的地方

#include <stdio.h> // You'd better add a space between, for this is a good coding convention
#include <conio.h> // only supported by vc/vs in windows, you can use getchar() instead

int main()  // main returns int
{
    char* t = "C++";

    t[1] = 'p';
    t[2] = 'p';
    printf("%s\n", t);  // it's a good habit to add a '\n' when printing a string
    getchar();   // getchar() is supported by c standard library 

    return 0; // return 0 here
}

3.关于打印字符串

Linux 是行缓冲的(如果您使用的是 windows :P,请忽略这一点)& 为了在控制台中更容易阅读,您最好在打印字符串的末尾添加一个 '\n':

printf("%s\n",t);

如果您不想在字符串后有回车符。在 Windows 中随心所欲地使用:

printf("%s",t);

在 Linux 中,您应该在 stdlib.h 中添加一个 fflush()。

printf("%s",t);
fflush(stdout);

【讨论】:

  • char* t="C++"; 没有任何问题。字符串文字以静态分配的 4 个元素数组的形式提供“已经存在的数据”。 (指针应该是const,但不一定非要如此。)
  • fflush 在&lt;stdio.h&gt; 中声明,而不是在&lt;stdlib.h&gt; 中。无论如何,最好打印换行符;如果你不想,好吧,无论如何你都应该这样做。
  • #include&lt;stdio.h&gt; 非常便携——但添加空格确实提高了可读性。无需调用fflush(stdout),程序终止时会刷新所有输出文件。
【解决方案4】:

标准运行时库的某些版本需要\n 才能显示输出。

printf("%s\n",t);

【讨论】:

  • 我认为这根本不是问题。
  • 很明显。我回答了我认为被问到的问题。
  • 事实证明,事实并非如此。
  • @Sadique:来自未来 7 年的问候!丢失的\n 不是问题,但可能是个问题。 “最后一行是否需要终止换行符是实现定义的。” --N15707.21.2p2。如果实现需要终止换行,而程序没有提供,则行为未定义。在 Windows 和 POSIX/Unix/Linux 下,它只会打印没有换行符的字符串(如果将 shell 提示连接到您的输出上,这可能会在视觉上造成混淆)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多