在c中反转数组 - 将无法打印 -

时间:2017-04-07 00:49:51

标签: c arrays printf reverse fgets

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

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

示例输入=大红狗

示例输出=狗红色大

#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 

4 个答案:

答案 0 :(得分:6)

It should come without surprise that reverseWords prints nothing. Why?

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

Where is line pointing? (to temp). Where was temp declared? (in reverseWords). How much storage was allocated to temp (none -- it is an uninitialized pointer)

Further, what happens to the memory associated with the function reverseWords when it returns? (it's destroyed...), so even if you had done something like char temp[strlen(line)+1] = "";, reverseWords would venture off into Undefined Behavior because the pointer you return, points somewhere within the reverseWords stack frame that was destroyed when reverseWords returned...

How do you fix this? You have three options, (1) pass a second pointer to a second array with sufficient storage, e.g.:

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

or, (2) dynamically allocate storage for temp so that the memory associated with temp survives the return of reverseWords, or

(3) use an adequately sized array for temp in reverseWords and overwrite line with the data in temp before the return. (e.g. use strcpy instead of the assignment line = temp;)

While dynamic allocation is straight forward, and creating a separate array in reverseWords is fine, you are probably better passing a second sufficiently sized array as a parameter to reverseWords.

It is completely unclear what you are doing with the argc and argv arguments in your code, the arguments to main have been omitted from the example below. The following is a short example of reversing the words in each line read from 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 */
}

Note: if there is no '\n' present in line when passed to revwords, you are likely trying to read a line longer than SIZE chars (or you are reading the last line where there is no POSIX '\n' at the end of the file), and you need to handle that as required. Here I simply warn.

Example Use/Output

$ 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

Look things over and let me know if you have any questions. There are dozens of ways to approach this problem, no one more right than another if they properly handle the reversal in a reasonable efficient manner. Take your pick.

If you like using the string library functions instead of pointer arithmetic, you could always do something like the following:

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 */
}

Note: While not an error, the standard coding style for C avoids the use of caMelCase or MixedCase variable or funciton names in favor of all lower-case while reserving upper-case names for use with macros and constants. Leave caMelCase or MixedCase for java or C++. (it's style, so it's your choice, but it does say something about your code on first impression)

答案 1 :(得分:1)

似乎你喜欢困难。

为您的目的采用此

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

答案 2 :(得分:0)

#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 :(得分:0)

如果您使用了更多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。我们使用<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的末尾,并增加字符数{ {1}}。在其中,我们检查nWord是否已用尽,在这种情况下,内存将被重新分配。如果重新分配失败,我们将返回word。重新分配的工作原理是NULL增加nallocWord,这是我们将调整缓冲区大小的字符数。

  3. 如果我们介于单词(ALLOCSTEP)之间,或者如果我们位于行inword == IN && isspace(line[i]的末尾,那么我们会将(i == 0)更改为inword ,null-terminate OUT,并通过调用word来反转它。我们的下一步是将revword添加到word的末尾。但是,我们必须首先检查是否有足够的空间进行连接。条件ret会检查nRet + nWord > nallocRet中的字符数加上ret中的字符数是否超过word,这是为nallocRet分配的字符数缓冲。如果条件为真,则重新分配内存。如果重新分配失败,我们将返回ret。我们需要检查NULL,因为当循环即将完成时,我们希望将最后一个单词推送到i == 0

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

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

    循环完成后,我们会释放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; } pq被指定为p的开头,而word被指定为指向q的结尾。 word指针在每次循环迭代时递增,p递减,而q大于q。在循环体中,我们交换pp指向的值。

    最后,我们返回反转的q

    现在,我将展示我改变的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时使用该指针。