簡體   English   中英

在c中反轉數組 - 不會打印 -

[英]Reversing array in c - will not print -

此代碼的問題在於,在用戶在命令行中輸入某些文本后,它實際上不會打印任何內容。

代碼的目的是接受用戶在文件名后通過命令提示符輸入的行數。 然后用戶將輸入內容進行反轉。 該程序應該反轉每行的用戶輸入。

示例輸入=大紅狗

示例輸出=狗紅大

#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 

毫不奇怪, 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] = "";這樣的事情char temp[strlen(line)+1] = ""; reverseWords會冒險進入Undefined Behavior,因為你返回的指針指向reverseWords堆棧框架內的某個地方,當reverseWords返回時它被銷毀...

你是如何解決這個問題的? 您有三個選項,(1)將第二個指針傳遞給具有足夠存儲空間的第二個數組,例如:

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

或者,(2)為temp 動態分配存儲空間,以便與temp相關聯的內存在reverseWords的返回中幸存下來,或者

(3)使用用於足夠大陣列tempreverseWords並覆蓋line與數據temp返回之前。 (例如,使用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字符更長的行(或者你正在讀取最后一行沒有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變量或caMelCase名稱來支持所有小寫,同時保留用於宏和常量的大寫名稱。 保留caMelCaseMixedCase for java或C ++。 (這是它的風格,所以這是你的選擇,但它確實說明了第一印象時你的代碼)

看來你喜歡困難。

為您的目的采用這個

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

如果你使用了更多的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] )是字母數字(即單詞中的一個字符),那么我們將inword更改為IN 控制將落到下一個if ,即

  2. 如果我們在一個單詞( inword == IN )中並且當前字符是單詞字符,那么我們將當前字符添加到word的結尾,並增加字符數nWord 在內部,我們檢查word是否已用盡,在這種情況下,內存被重新分配。 如果重新分配失敗,我們返回NULL 重新分配的工作原理是通過ALLOCSTEP增加nallocWord ,即我們將調整緩沖區大小的字符數。

  3. 如果我們介於單詞之間( inword == IN && isspace(line[i] ),或者如果我們位於行的末尾(i == 0) ,那么我們將inword更改為OUT ,null-terminate word ,以及通過調用revword來反轉它。我們的下一步是將word添加到ret的結尾。但是,我們必須首先檢查是否有足夠的空間進行連接。條件nRet + nWord > nallocRet檢查是否ret的字符數加上word的字符數超過nallocRet ,這是為ret緩沖區分配的字符數。如果條件為true,則重新分配內存。如果重新分配失敗,則返回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;
}

該函數使用兩個字符指針pq p被指定為指向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的返回值存儲在臨時myrev中,這樣我們就可以在free它時使用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM