【问题标题】:Reversing array in c - will not print -c中的反转数组-不会打印-
【发布时间】:2017-09-02 05:43:09
【问题描述】:

此代码的问题在于,在用户在命令行中输入一些文本后,它实际上并没有打印任何内容。

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

示例输入 = the big red dog

示例输出 = dog red big the

#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 

【问题讨论】:

  • temp 未初始化且不指向任何内容,因此您无法将其传递给strcat(word + ' ') 不会像你想的那样做。
  • 为什么不用strlen()来获取输入行的长度呢?
  • 你不能使用C的标准字符串函数如strlen()strtok()吗?但是你可以使用strcat()strncpy()
  • 这是什么word_len == word_len+1;

标签: c arrays printf reverse fgets


【解决方案1】:

如果您使用更多的string.h 函数,例如strlen,您的问题将会得到简化。此外,您必须使用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 缓冲区中的字符数,以及为word 分配的内存量。 word 是我们积累单词的地方。由于输入行会被反向解析,word 缓冲区最初会被反向解析,但我们稍后会反转它。

变量nRetnallocRet有类似的用途:它们分别是ret缓冲区中的字符数和分配给ret的字符数。 ret 是我们将存储整个输入行的缓冲区,但每个单词的位置都颠倒了。

然后我们强制执行两个前提条件:字符串的长度必须为正数,并且line 输入缓冲区不能为 NULL。我们通过使用来自&lt;assert.h&gt;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 循环由一系列测试组成。

    1234563控制权将落入下一个if,即
  1. 如果我们在一个单词(inword == IN)中并且当前字符是单词字符,那么我们将当前字符添加到word的末尾,并增加字符数nWord。在其中,我们检查word 是否已用尽,在这种情况下,内存会被重新分配。如果重新分配失败,我们返回NULL。重新分配的工作原理是将nallocWord 增加ALLOCSTEP,这是我们将调整缓冲区大小的字符数。

  2. 1234563并通过调用revword 将其反转。我们的下一步是将word 添加到ret 的末尾。但是,我们必须首先检查是否有足够的空间来进行连接。条件nRet + nWord &gt; nallocRet 检查ret 中的字符数加上word 中的字符数是否超过nallocRet,即分配给ret 缓冲区的字符数。如果条件为真,则重新分配内存。如果重新分配失败,我们返回NULL。我们需要检查i == 0,因为当循环即将结束时,我们希望将最终单词推入ret

现在,我们可以通过调用strcatword 附加到ret。我们还添加了一个空格,这样单词之间就会有空格。

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 &lt; N; i++) 内。

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

【讨论】:

    【解决方案2】:
    #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));
        }
    }
    

    【讨论】:

      【解决方案3】:

      reverseWords 不打印任何内容应该不足为奇。为什么?

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

      line 指向哪里? (至temp)。 temp 是在哪里声明的? (在reverseWords)。为temp 分配了多少存储空间(无——它是一个未初始化的指针)

      此外,与函数reverseWords 相关联的内存在返回时会发生什么情况? (它被破坏了......),所以即使你做了类似char temp[strlen(line)+1] = ""; 的事情,reverseWords 也会冒险进入未定义行为,因为你返回的指针指向reverseWords 堆栈中的某个位置当reverseWords 返回时被破坏的框架...

      你如何解决这个问题?您有三个选择,(1)将第二个指针传递给具有足够存储空间的第二个数组,例如:

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

      或者,(2) temp动态分配存储空间,以便与temp关联的内存在reverseWords返回后仍然存在,或者

      (3) 在reverseWords 中为temp 使用一个足够大的数组,并在返回之前用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 */
      }
      

      注意:如果在传递给revwordsline 中不存在'\n',您可能会尝试读取比SIZE 字符长的行(或者您正在阅读last 行,文件末尾没有 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 变量或函数名称,而支持所有 小写 em> 同时保留 大写 名称以用于宏和常量。为 java 或 C++ 保留 caMelCaseMixedCase。 (它的风格,所以它是你的选择,但它确实说明了你的代码的第一印象)

      【讨论】:

      • ...除非您的 C 代码使用 Xlib,在这种情况下混合大小写是不可避免的
      • 当然,但除非您编写了 Xlib,否则此时它不在您的控制范围内。
      【解决方案4】:

      看来你喜欢困难。

      为你的目的采用这个

      #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
      

      如果你因为某些原因没有strrev,这里是

      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;
      }
      

      【讨论】:

      • 我希望您知道strrev() 并非在每个平台上都可用。见Is the strrev() function not available in Linux?
      • @datell 已添加,手工制作,本例
      • 太棒了!您可以使用 #if defined(__MACH__) || defined(__linux__) the custom implementation #endif 之类的预处理器或类似的东西来检查平台。我猜那将是非常 C 风格的
      猜你喜欢
      • 2019-02-14
      • 1970-01-01
      • 1970-01-01
      • 2013-07-08
      • 1970-01-01
      • 2021-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多