我有一个简单的C程序,它应该根据以下规则扩展字符串FX
:
X -> X+YF+
Y-> -FX-Y
该计划如下:
#include <stdio.h>
void expandOnce( )
{
char* s = (char*)malloc(sizeof(char)*100);
int i=0;
char str[]="FX";
while(str[i]!='\0')
{
if(str[i]=='X')
{
s[i]="X+YF+";
}
if(str[i]=='Y')
{
s[i]="-FX-Y";
}
i++;
}
printf("%s",s);
}
void expandNtimes (int n)
{
for (int i =0; i < n; i++){
expandOnce();
}
}
int main(){
expandNtimes(2);
return 0;
}
如果程序正确执行,则应打印出来:
FX+YF++-FX-YF+
这是在使用上述规则进行2次FX
扩展之后。
问题:
1.如何在不打印乱码的情况下完成这项工作?
2.如何从expandNtimes()和expandOnce()返回展开的数组,以便我可以在main函数中使用它?
我试试这个,但我一直在记忆错误。
根据您的建议,我做了以下更改:
#include <stdio.h>
#include <string.h>
#define NTIMES 2
#define MAXC 100
void expandOnce( )
{
char s[MAXC];
int i=0;
s[0]='F';
s[1]='X';
while(s[i]!='\0')
{
if(s[i]=='X'){
//shift all letters to the right by 5 steps
for (i; i < MAXC; i++){ //warning: statement with no effect (Wunused value)
s[i + 5]= s[i];
if(s[i]=='\0'){
break;}
}
//Fill in the expansion
s[i]='X';
s[i+1] ='+';
s[i+2] ='Y';
s[i+3]='F';
s[i+4] ='+';
}
if(s[i]=='Y'){
//shift all letters to the right by 5 steps
for (i; i < MAXC; i++){ //warning: statement with no effect (Wunused value)
s[i + 5]=s[i];
if(s[i]=='\0'){
break;}
}
//Fill in the expansion
s[i]='-';
s[i+1] ='F';
s[i+2] ='X';
s[i+3]='-';
s[i+4] ='Y';
}
i++;
}
printf("%s\n",s);
printf("Me\n");
}
void expandNtimes (int n)
{
for (int i =0; i < n; i++){
expandOnce();
}
}
int main(){
expandOnce();
expandNtimes(NTIMES);
return 0;
}
我的两个for循环都有一个我不明白的警告warning: statement with no effect (Wunused value)
。最后,我收到了这个错误:
Segmentation fault (core dumped)
是否允许在while循环中执行for循环?
答案 0 :(得分:2)
哦,男孩......你的代码中是否有一份“你不能那样做......”的完整清单。
首先,不需要强制转换malloc
,这是不必要的。请参阅:Do I cast the result of malloc?。
char *s = malloc (sizeof *s * 100);
就是所需要的(注意:如果你总是使用sizeof dereference_pointer
,你的版本大小总是正确的。
这提出了另一点,如果你需要常量#define
一个(或更多)或使用全局enum
来完成同样的事情,例如。
#define NTIMES 2
#define MAXC 100
或
enum { NTIMES = 2, MAXC = 100 };
然后你可以使用它们,
char *s = malloc (sizeof *s * MAXC);
和
expandNtimes(NTIMES);
通过定义常量,您可以在代码顶部有一个方便的位置,以便在将来需要时进行更改,而不必选择每个分配或函数调用来更改所有硬编码的幻数
您的记忆泄漏
您在s
中分配expandOnce
,但从不free
s
,并且不会返回指针,以便稍后可以在main()
中释放它(或任何其他呼叫功能)。
您应该在free (s);
之后添加printf("%s",s);
(您还应该包含newline
以防止多个输出一起运行,并且您的程序符合POSIX标准 - 输出最终'\n'
),例如printf("%s\n",s);
此外,“为什么还要动态分配?”只需使用固定缓冲区,您就不会尝试返回它。只需使用:
char s[MAXC] = "";
s
未初始化且您离开s[0]
未初始化
s[i]="X+YF+";
和s[i]="-FX-Y";
从索引s
向1
写入4个字符。永远不会初始化s[0]
,导致后续尝试从s
读取未定义行为。您不能简单地将指向字符串文字 "X+YF+";
的指针分配给字符s[i]
- 您的编译器应该尖叫警告和错误。< / p>
您可以使用以下内容将文字复制到s
strcpy (s, "X+YF+");
或者你可以随便循环重复复制,例如
int i;
for (i = 0; str[i]; i++) /* loop over each char in str */
s[i] = str[i]; /* assign to s */
s[i] = 0; /* don't forget to nul-terminate s */
在启用警告的情况下进行编译
始终使用警告启用进行编译,不接受代码,直到干净地编译而不发出警告。要启用警告,请将-Wall -Wextra
添加到gcc
或clang
编译字符串中。 (添加-pedantic
以获得其他几个警告)。对于clang
,您可以使用-Weverything
。对于 VS (windoze上为cl.exe
),请添加/W3
(或使用/Wall
启用所有警告)。
阅读并理解每个警告。他们将识别任何问题,以及它们发生的确切线。您可以通过简单地聆听编译器告诉您的内容,从大多数教程中学到很多关于编码的知识。
如果您还有其他问题,请使用这些建议然后编辑您的问题(或询问新问题)。
修改后的其他评论
Gakuo,显然你仍然在努力整理字符串/字符数组处理以使扩展工作。正如我在评论中提到的那样,每当你遇到这样的问题时,不拿起键盘并开始啄食,希望你能够修补足够长的时间,神圣的介入会以某种方式引导你找到解决方案(它不会......)。
相反,首先拿起一张8 1/2 x 11张纸(方格纸效果很好)并写出你的原始字符串,然后计划从原始字符串到你需要的最终字符串所需的每一步(不要忘记在你的布局中代表'\0'
)
例如,您可以从原始字符串开始:
+---+---+---+
| F | X |\0 |
+---+---+---+
下一步是表示为您的扩展腾出空间的转变(您将原始角色留在原处,因为您只需覆盖它):
+---+---+---+---+---+---+---+
| F | X | | | | |\0 |
+---+---+---+---+---+---+---+
现在需要将"X+YF+"
复制到'X'
的位置,导致:
+---+---+---+---+---+---+---+
| F | X | + | Y | F | + |\0 |
+---+---+---+---+---+---+---+
然后只需重复此过程,直到完全展开N次为止。正如您所做的那样,您将能够确定扩展N次的模式 - 也就是说是时候拿起键盘了。
对于您的情况,不要手动循环所有内容,而是使用memmove
和memcpy
来帮助在字符串中进行移位和复制。 (你可以通过循环手动移动和复制 - 只要你能找到更多的字母为你循环变量而不仅仅是'i'
)。让记忆功能有所帮助,您可以执行以下操作:
(注意:已修改,以适应s
中expandonce()
的更改长度
#include <stdio.h>
#include <string.h>
#define NTIMES 2
#define MAXC 100
#define SDEF "FX" /* string (default) */
#define XEXP "X+YF+" /* X expansion */
#define YEXP "-FX-Y" /* Y expansion */
/* returns pointer to 1-past last expansion on success, NULL otherwise */
char *expandonce (char *s)
{
char *p = s,
*expanded = NULL;
size_t lxexp = strlen (XEXP),
lyexp = strlen (YEXP);
while (*p) {
if (*p == 'X') { /* handle 'X' found */
size_t lens = strlen (s); /* get new length of s */
if (MAXC > lens + lxexp) { /* room to expand? */
size_t lenp = strlen (p); /* total to shift */
/* shift chars to right of 'X' by lxexp-1 including '\0' */
memmove (p + lxexp, p + 1, lenp + lxexp - 1);
memcpy (p, XEXP, lxexp); /* copy expansion */
p += lxexp; /* add offset */
expanded = p; /* set return */
}
}
else if (*p == 'Y') { /* handle 'Y' found */
size_t lens = strlen (s); /* same for 'Y' */
if (MAXC > lens + lyexp) {
size_t lenp = strlen (p);
memmove (p + lyexp, p + 1, lenp + lyexp - 1);
memcpy (p, YEXP, lyexp);
p += lyexp;
expanded = p;
}
}
else /* handle all other chars */
p++;
}
return expanded;
}
/* returns number of successful expansion loops */
int expandntimes (char *s, int n)
{
int i = 0;
char *p = s;
for (i = 0; i < n; i++)
if ((p = expandonce (s)) == NULL)
break;
return i;
}
int main (int argc, char **argv) {
char str[MAXC] = "";
int n = 0;
if (argc > 1 && strlen (argv[1]) < MAXC - 1)
strcpy (str, argv[1]);
else
strcpy (str, SDEF);
printf ("string: %s\nX_exp : %s\nY_exp : %s\n\n", str, XEXP, YEXP);
n = expandntimes (str, NTIMES);
if (n == 0) {
fputs ("error: no expansions possible.\n", stderr);
return 1;
}
printf ("%d expansions => %s\n", n, str);
return 0;
}
(注意:这是一个最小的例子 - 您应该在逐步完成设计时验证是否涵盖了所有角落案例)
示例使用/输出
你的例子:
$ ./bin/expand2 "FX"
string: FX
X_exp : X+YF+
Y_exp : -FX-Y
2 expansions => FX+YF++-FX-YF+
略有不同的字符串:
$ ./bin/expand2 "XY"
string: XY
X_exp : X+YF+
Y_exp : -FX-Y
2 expansions => X+YF++-FX-YF+-FX+YF+--FX-Y
答案 1 :(得分:0)
你得到了内存错误,因为你试图在内存中写入你不应该做的任何事情。如果你想追加字符串,你必须使用strcat()函数!线s [i] =“X + YF +”;永远不会编译,因为你试图写入多个字符,只允许一个字符。我认为最好将s全局化并将字符串逐个附加到带有strcat的s,直到你创建字符串。您可以访问main中的全局变量。然后打印字符串s。