c中的反向数组将不会打印 -

问题描述:

此代码的问题在于,用户在命令行中输入某些文本后不会实际打印任何内容。c中的反向数组将不会打印 -

该代码的目的是接受用户通过命令提示符在文件名后输入的行数。然后用户将输入一些内容以反转。该程序应该为每行反转用户输入。

例输入=红色的大狗

示例输出=狗红色大的

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 
#define SIZE 80 

char * reverseWords(char *string); 

//argc is the count of cmd arguments. 
//each command line argument is of type string 
int main(int argc, char *argv[]){ 

    //initialize local variables 
    int i; 
    int N; 
    char str[SIZE]; 

    for(i = 1; i <argc; i++) 
    { 
     //set N equal to the users number in the command line 
     N = atoi(argv[i]); 
    } 

    if(argc != 2){//2 means that something is in the argument. 
     printf("ERROR: Please provide an integer greater than or equal to 0"); 
     exit(1);//exit the program 
    }else if(N < 0){//We cant have a negative array size. 
     printf("ERROR: Please provide an integer greater than or equal to 0"); 
     exit(1);//exit the program 
    }else{ 
     for(i = 0; i < N; i++){ 
      /* 
      fgets(pointer to array, max # of chars copied,stdin = input from keyboard) 
      */ 
      fgets(str,SIZE,stdin); 

      printf("%s", reverseWords(str)); //<---does not print anything.... 
     }   
    } 
    return 0; 
} 


char * reverseWords(char *line){ 

    //declare local strings 
    char *temp, *word; 
    //instantiate index 
    int index = 0; 
    int word_len = 0; 
    /*set index = to size of user input 
     do this by checking if the index of line is 
     equal to the null-character. 
    */ 
    for(int i = 0; line[i] != '\0';i++) 
    { 
     index = i;//index = string length of line. 
    } 

    //check if index is less than 0. 
    //if not we decrement the index value. 

    for(index; index != -1; index--){ 
     //checking for individual words or letters 
     if(line[index] == ' ' && word_len > 0){ 
      strncpy(word,line,word_len); 
      strcat(temp , (word + ' ')); 
      word_len = 0; 

     }else if(isalnum(line[index])){ 
      word_len == word_len+1; 
     }//end if 

    }//end loop 

    //copy over the last word after the loop(if any) 
    if(word_len > 0){ 
     strncpy(word,line,word_len); 
     strcat(temp,word); 
    }//end if 
    line = temp; 
    return line; 
}//end procedure 
+5

'temp'是未初始化的,并不指向任何东西,所以你不能将它传递给'strcat'。 '(字+'')'不符合你的想法。 – aschepler

+1

为什么不使用'strlen()'来获取输入行的长度? – Barmar

+0

你不允许使用C的标准字符串函数,比如'strlen()'和'strtok()'吗?但是你可以使用'strcat()'和'strncpy()'? – Barmar

reverseWords什么也不打印。为什么?

char * reverseWords(char *line){ 
    ... 
    char *temp, *word; 
    ... 
    line = temp; 
    return line; 
} //end procedure 

line哪里指向? (到temp)。 temp宣布在哪里? (在reverseWords)。 temp(none - 它是一个未初始化的指针)分配了多少存储空间

此外,当函数返回时与内存函数reverseWords相关的内存会发生什么变化? (它破坏了...),所以即使你做了类似char temp[strlen(line)+1] = "";reverseWords冒昧断成未定义行为因为您返回指针,当回到reverseWords这是破坏了reverseWords栈帧中的某处点...

你如何解决这个问题?有三种选择:(1)一个第二指针传递到具有足够存储的第二阵列,例如:

char *revwords (char *rline, char *line) 

或,(2)动态分配存储为temp使得与temp相关联的存储器生存的返回的reverseWords,或

(3)使用一个temp足够大阵列中reverseWords和与回流之前在temp数据改写line。 (例如使用strcpy代替分配line = temp;

虽然动态分配是直线前进,并在reverseWords创建一个单独的数组是好的,你可能更好使第二足够大小的阵列作为一个参数reverseWords

您的代码中的argcargv参数完全不清楚您正在做什么,下面的示例中省略了main的参数。以下是从stdin读取每行反转的话很短的例子,

#include <stdio.h> 
#include <string.h> 

#define SIZE 256 

char *revwords (char *rline, char *line); 

int main (void) { 

    char line[SIZE] = "", rline[SIZE] = ""; /* storage for line/rline */ 

    while (fgets (line, SIZE, stdin)) { /* for each line on stdin */ 
     printf ("\n line: %s\nrline: %s\n", line, revwords (rline, line)); 
     *rline = 0; /* set first char in rline to nul-byte */ 
    } 

    return 0; 
} 

char *revwords (char *rline, char *line) 
{ 
    size_t lnlen = strlen (line); /* length of line */ 
    /* pointer, end-pointer, rev-pointer and flag pointer-to-space */ 
    char *p = line + lnlen - 1, *ep = p, *rp = rline, *p2space = NULL; 

    if (!line || !*line) { /* validate line not NULL and not empty */ 
     fprintf (stderr, "revwords() error: 'line' empty of null.\n"); 
     return NULL; 
    } 

    if (*ep == '\n') /* if line ends in '\n' -- remove it */ 
     *ep-- = 0; 
    else    /* warn if no '\n' present in line */ 
     fprintf (stderr, "warning: no POSIX '\\n' found in line.\n"); 

    for (; ep >= line; ep--) { /* for each char from end-to-beginning */ 
     if (*ep == ' ') {    /* is it a space? */ 
      size_t len = p - ep;  /* get the length of the word */ 
      strncat (rp, ep + 1, len); /* concatenate word to rline */ 
      if (p == line + lnlen - 1) /* if first word, append ' ' */ 
       strcat (rp, " "); 
      p = ep;      /* update p to last ' ' */ 
      p2space = ep;    /* set flag to valid pointer */ 
     } 
    } 
    strncat (rp, line, p - line);  /* handle first/last word */ 

    if (!p2space) { /* validate line contained ' ', if not return NULL */ 
     fprintf (stderr, "revwords() error: nothing to reverse.\n"); 
     return NULL; 
    } 

    return rline; /* return pointer to reversed line */ 
} 

注:如果传递给revwords时没有'\n'出现在line,你很可能试图读取线较长比SIZE字符(或者您正在阅读上一个行,在文件末尾没有POSIX '\n'),并且您需要根据需要处理该行。我在这里只是警告。

示例使用/输出

$ printf "my dog has fleas\nmy cat does too\n" | ./bin/str_rev_words 

line: my dog has fleas 
rline: fleas has dog my 

line: my cat does too 
rline: too does cat my 

看东西了,让我知道,如果你有任何问题。有几十种方法可以解决这个问题,如果他们以合理有效的方式妥善处理逆转问题,那么没有比这更好的方法。拿你的选择。

如果你喜欢使用,而不是指针运算字符串库函数,你总是可以做类似如下:

char *revwords (char *rline, char *line) 
{ 
    /* length, pointer, end-pointer, pointer-to-space, copy of line */ 
    size_t len = strlen (line); 
    char *p = NULL, *p2space = NULL, copy[len+1]; 

    if (!line || !*line) { /* validate line not NULL and not empty */ 
     fprintf (stderr, "revwords() error: 'line' empty of null.\n"); 
     return NULL; 
    } 

    if (line[len-1] == '\n') /* remove trailing newline */ 
     line[--len] = 0; 
    else    /* warn if no '\n' present in line */ 
     fprintf (stderr, "warning: no POSIX '\\n' found in line.\n"); 

    strncpy (copy, line, len + 1); /* copy line to 'copy' */ 

    /* for each ' ' from end-to-beginning */ 
    while ((p = strrchr (copy, ' '))) { 
     strcat (rline, p + 1);   /* append word to rline */ 
     strcat (rline, " ");   /* followed by a space */ 
     p2space = p;     /* set p2space to p  */ 
     *p2space = 0;     /* nul-terminate copy at p */ 
    } 

    if (p2space) {    /* validate space found in line */ 
     *p2space = 0;   /* nul-terminate at space  */ 
     strcat (rline, copy); /* concatenate first/last word */ 
    } 
    else {      /* no ' ' in line, return NULL */ 
     fprintf (stderr, "revwords() error: nothing to reverse.\n"); 
     return NULL; 
    } 

    return rline; /* return pointer to reversed line */ 
} 

注:虽然不是错误,对于C标准的编码风格避免了使用caMelCaseMixedCase变量或函数名支持所有小写,同时保留大写用于宏和常量的名称。对于java或C++,请留下caMelCaseMixedCase。 (它的风格,所以这是你的选择,但它确实谈谈第一印象你的代码),如果你使用更多的string.h功能,如strlen

+0

...除非你的C代码使用Xlib,其中混合的情况下是不可避免的 – technosaurus

+0

授予,但除非你写了Xlib - 它不在你的控制范围内。 –

看来你喜欢的困难。

如果你没有strrev采用此为您的目的

#include <string.h> 
#include <stdio.h> 

char* reverse_words(char* str); 

int main() { 
    char arr[] = "the big red dog"; 
    printf("%s", reverse_words(arr)); 
    return 0; 
} 


char* reverse_words(char* str) { 
    char delim = ' '; // space 
    int left = 0; 
    int reverse_index = 0; 
    int right = 0; 
    int len = strlen(str); 
    char tmp; 
    while (left < len) { 
     while (str[right] != delim && right < len) 
      right++;  
     reverse_index = right - 1; 
     while (left < reverse_index){ 
      tmp = str[left]; 
      str[left] = str[reverse_index]; 
      str[reverse_index] = tmp; 
      left++; 
      reverse_index--; 
     } 
     right++;   
     left = right; 
    } 

    strrev(str); 
    return str; 
} 


//output is: dog red big the 

由于某些原因,你在这里

char* strrev(char *str) { 
    char *p1, *p2; 

    if (! str || ! *str) 
     return str; 

    for (p1 = str, p2 = str + strlen(str) - 1; 
          p2 > p1; ++p1, --p2) { 
     *p1 ^= *p2; 
     *p2 ^= *p1; 
     *p1 ^= *p2; 
    } 

    return str; 

}

也更加清晰的方式,但更慢得

char* strrev(char *str) { 
    int left = 0; 
    int right = strlen(str) - 1; 
    char tmp; 
    while(left < right) { 
     tmp = str[left]; 
     str[left] = str[right]; 
     str[right] = tmp; 
     left++; 
     right--; 
    } 

    return str; 
} 
+0

我希望你知道'strrev()'在每个平台上都不可用。请参见[是否在Linux中不提供strrev()函数?](http://*.com/questions/8534274/is-the-strrev-function-not-available-in-linux)。 – datell

+1

@datell补充,手工制作一个,对此案例 – bobra

+0

太棒了!您可以使用'#if defined(__ MACH__)||等预处理器来检查平台定义(__ linux__)自定义实现#endif或类似的东西。这将是非常* C风格*我猜 – datell

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 

#define SIZE 80 

char *reverseWords(char line[SIZE]){ 
    char temp[SIZE]; 
#if SIZE > 255 
    unsigned index[SIZE]; 
#else 
    unsigned char index[SIZE]; 
#endif 
    int i, index_count = 0; 
    int inside_word = !!isalpha((unsigned char)line[0]), word = inside_word; 

    for(index[index_count++] = i = 0; line[i]; ++i){//copy & make index table 
     unsigned char ch = temp[i] = line[i]; 
     if(inside_word && !isalpha(ch) || !inside_word && isalpha(ch)){//Edge 
      index[index_count++] = i; 
      inside_word = !inside_word; 
     } 
    } 
    index[index_count] = i; 

    int last_word_index = index_count - 1;//last index 
    last_word_index -= !word^(last_word_index & 1);//to word 

    char *p =line; 
    for(i = 0; i < index_count-1; ++i){ 
     int len; 
     if(word){ 
      len = index[last_word_index+1] - index[last_word_index]; 
      memcpy(p, &temp[index[last_word_index]], len); 
      last_word_index -= 2; 
     } else { 
      len = index[i+1] - index[i]; 
      memcpy(p, &temp[index[i]], len); 
     } 
     word = !word; 
     p += len; 
    } 

    return line; 
} 

int main(void){ 
    char str[SIZE]; 

    while(fgets(str, sizeof str, stdin)){ 
     printf("%s", reverseWords(str)); 
    } 
} 
+0

[DEMO](http://ideone.com/VIglfa) – BLUEPIXY

您的问题将被简化。此外,您必须动态分配内存malloccalloc - 固定大小的缓冲区不会在这里执行。

我现在修改后的reverseWords

char *myrev(const char *line) 
{ 
    char *revword(char *); 

    size_t i = strlen(line); 
    int inword = OUT; 

    size_t nWord = 0, nallocWord; 
    char *word;  // will store the word 

    size_t nRet = 0, nallocRet; 
    char *ret;  // will store the entire line, but reversed 

    // establish preconditions 
    assert(i > 0); 
    assert(line != NULL); 

    // alloc memory for word and ret 
    if ((word = malloc(nallocWord = INITALLOC)) != NULL && 
       (ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) { 

     // walk backwards through line 
     while (i--) { 
      if (inword == OUT && isalnum(line[i])) 
       inword = IN; // we just entered a word 

      if (inword == IN && isalnum(line[i])) { 
       // we're inside a word; append current char to the word buffer 
       word[nWord++] = line[i]; 

       // word buffer exhausted; reallocate 
       if (nWord == nallocWord) 
        if ((word = realloc(word, nallocWord += ALLOCSTEP)) == NULL) 
         return NULL; 
      } 

      // if we are in between words or at the end of the line 
      if (i == 0 || inword == IN && isspace(line[i])) { 
       inword = OUT; 
       word[nWord] = '\0'; 

       word = revword(word); 

       // ret buffer exhausted; reallocate 
       if (nRet + nWord > nallocRet) 
        if ((ret = realloc(ret, nallocRet += ALLOCSTEP)) == NULL) 
         return NULL; 

       // append word to ret 
       strcat(ret, word); 
       strcat(ret, " "); 
       nRet += nWord + 1; 

       nWord = 0; 
      } 
     } 
     free(word); 

     // remove trailing blank 
     ret[strlen(ret) - 1] = '\0'; 
     return ret; 
    } 
    // in case of mem alloc failure 
    return NULL; 
} 

我现在来解释一下这个函数的操作。

第一行声明函数revwords,我将在稍后显示。

下一行是变量定义。变量i将用作迭代器向后走。我们将它初始化为line字符串的长度,包括零终止符。

变量inword很重要。它用于跟踪我们是否在一个词里面。它将被分配两个常量中的一个:INOUT

#define IN  0 /* inside a word */ 
#define OUT  1 /* outside a word */ 

nWordnallocWord变量是分别在word缓冲区中的字符的数量,以及有多少内存被分配用于wordword是我们将积累一个词的地方。由于输入行将被向后解析,因此word缓冲区将初始向后,但稍后我们会将其反转。

变量nRetnallocRet具有类似的目的:它们分别是字符在ret缓冲器和数目分配给ret的字符数。 ret是我们将存储整个输入行的缓冲区,但每个单词的位置都是相反的。

然后我们强制执行两个前提条件:字符串的长度必须是正数,并且输入缓冲区不能为NULL。我们通过使用来自<assert.h>assert宏来执行这些操作。

我们现在输入功能的肉。我们在此功能中的策略是首先抓取我们的wordret缓冲区的一定数量的内存,然后在需要时增加缓冲区的大小。所以我们就是这么做的。

线

if ((word = malloc(nallocWord = INITALLOC)) != NULL && 
       (ret = calloc(nallocRet = INITALLOC, sizeof(char))) != NULL) { 

出现在第一可怕的,但如果我们把它分成两个部分,它会更容易。 AND运算符左边的部分为word分配INITALLOC字符,并检查返回值是否不为NULL(表示失败)。但INITALLOC被分配到nallocWord,正如我们前面所述,这是分配给word的字符数。

AND的右侧部分为ret分配INITALLOC字符,并检查返回值是否不为NULL。但INITALLOC分配给nallocRet。请注意,我们使用calloc函数而不是malloc。区别在于calloc零初始化它的返回值,但malloc没有。我们需要我们的ret缓冲区进行零初始化;你会看到为什么以后。

#define INITALLOC 16 /* number of characters initially alloc'ed */ 
#define ALLOCSTEP 32 /* number of characters to increase by */ 

这些宏的值并不真正的问题,但让没有进行太多(慢),重新分配你应该还是会选择有意义的值他们。

无论如何,在这个if声明中,我们有一个while循环,它从尾部迭代字符串line。 while循环由一系列测试组成。

  1. 如果我们是一个字(inword == OUT)和当前字符(line[i])外面是字母数字(即,一个字内的字符),则我们改变inwordIN。控制将下降到下一if,这是

  2. 如果我们是一个字(inword == IN)和当前字符里面是一个单词字符,那么我们当前字符添加到word末,增加人物计数nWord。其中,我们检查word是否已经耗尽,在这种情况下,内存被重新分配。如果重新分配失败,我们将返回NULL。重新分配的作用是将nallocWord增加ALLOCSTEP,这是我们将调整缓冲区大小的字符数。

  3. 如果我们在字(inword == IN && isspace(line[i])之间,或如果我们在该行(i == 0)的端部,那么,我们改变inwordOUT,空终止word,并与到revword呼叫扭转它。我们的下一步是将word添加到ret的末尾。但是,我们必须首先检查是否有足够的空间进行连接。条件nRet + nWord > nallocRet检查字符的ret数目加上字符在word数量超过nallocRet,这为ret缓冲器分配的字符的数目。如果条件为真,则重新分配内存。如果重新分配失败,我们将返回NULL。我们需要检查i == 0,因为当循环即将完成时,我们要将最后一个单词推入ret。现在

,我们可以追加wordret一起strcat通话。我们还添加一个空格,以便这些单词之间会有空格。

nRet更新为ret中的新字符数。 + 1是为了说明单词之间的空间。 nWord设置为0,因此下一次循环迭代将覆盖不再需要的旧内容word

一旦循环完成,我们发布word,因为它需要更长时间,然后在ret的末尾删除尾部空格。然后我们返回ret。顺便说一下,调用者的责任是释放这些内存。对于每次致电malloc/calloc,都必须有相应的free

现在让我们转向revword,这是扭转一个字符串的函数。

char *revword(char *word) 
{ 
    char *p, *q; 

    assert(word != NULL); 
    assert(*word != '\0'); 

    for (p = word, q = word + strlen(word) - 1; q > p; ++p, --q) { 
     char tmp; 

     tmp = *p; 
     *p = *q; 
     *q = tmp; 
    } 

    return word; 
} 

该函数使用两个字符指针,pqp被指定为指向word的开始,而q被指定指向word的末尾。 p指针在每个循环迭代中递增,并且q递减,而q大于p。在循环体中,我们交换了pq指向的值。

最后,我们返回反转的word

现在,我将展示我修改过的main的一点点。

fgets(str, SIZE, stdin); 
str[strlen(str) - 1] = '\0'; 

char *myrev(const char *line); 

char *res = myrev(str); 

printf("%s", res); 
free(res); 

这是在循环内for (i = 0; i < N; i++)

我们必须从str缓冲区中删除尾随的换行符,其中fgets留在那里。然后我们声明myrev函数,然后在临时存储myrev的返回值,这样我们就可以使用这个指针的时候我们free吧。