使用纯指针表示法和循环

时间:2018-04-12 03:07:20

标签: c arrays loops pointers

我有这个完成的任务,我必须从数组符号转换为纯指针符号,它正在工作,我完成了它但我想问一些我不清楚的问题。我真的想完全理解指针,因为它们是我的垮台。

- 首先,我不需要将指针指向变量的地址吗?例如......

char *ps1 = &s1;
char *ps2 = &s2;

我以为我做了,但没有它,代码工作正常。为什么?我的指针如何知道该变量的第一个元素?是因为我的指针只是本地的,只在我的函数中?

- 另外,现在总是生成一个新的随机字符串,如果我希望它始终是相同的字符串,我该怎么做? 我试过......

getRandomStr(s1);
originals1[41] = getRandomStr;
在do while循环之外,然后在do while中替换

puts(s1);

 puts(originals1);

并且有效,总是相同的字符串但我前面有一堆奇怪的字符。

这是我的代码。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void getRandomStr(char *ps1);
void strfilter(char *ps1, char *ps2, char c);
void check(char *ps2);
char cont(void);

int main()
{
    char s1[41];
    char s2[21], c;
    char entry;

    /* I dont need these declarations below? */
    //char *ps1 = &s1;
    //char *ps2 = &s2;



    do {
        /* get random string (s1) through function getRandomStr */
        getRandomStr(s1);
        printf("Random string generated (s1): ");
        /* print rand string by printf("Random string generated: %s\n", s1);*/
        /* OR.. */
        puts(s1);
        printf("\nPlease enter up to 20 letters to be replaced (s2): ");
        /* operator enters string (s2) up to 20 chars */
        gets(s2);
        printf("\nPlease enter a replacement character (Ex. *, $, etc.) (c): ");

        /* operator enters a replacement char (c) */
        c = getchar();

        /* Error check to verify entered string (s2) is a letter A-Z */
        check(s2);

        printf("\nFiltered string (s1): ");
        strfilter(s1, s2, c);
        /* print filtered string s1 */
        puts(s1);


        entry = cont();

    } while (entry == 'y' || entry == 'Y');

}


void getRandomStr(char *ps1) 
{

    int i;

    srand(time(NULL));
    for (i = 0; i < 41; i++) {

        char letter = rand() % 26 + 'A';
        *(ps1 + i)  = letter;

    }

    *(ps1 + 41) = '\0';
}

void strfilter(char *ps1, char *ps2, char c)
{
    int i = 0;

    while (*(ps2 + i) != '\0') {

        for (int j = 0; *(ps1 + j) != '\0'; j++) {

            if (*(ps1 + j) == *(ps2 + i))
            {

                *(ps1 + j) = c;

            }
        }

        i++;
    }
    /* if want to print filtered s1 from function */
    /* puts(s1); */
}


char cont()
{

    char entry;
    printf("\nDo you want to run the program again (Y/N)? ");
    scanf_s(" %c%*c", &entry);
    return entry;
}

void check(char *ps2)
{
    int i = 0;

    /* while s2 is not end of string */
    while (*(ps2 + i) != '\0')
    {
        /* if s2 not a letter A-Z, give error message */
        if (!(*(ps2 + i) >= 'A' && *(ps2 + i) <= 'Z'))
        {

            printf("An invalid character was entered.\n");
            break;
        }

        i++;
    }
}

1 个答案:

答案 0 :(得分:2)

好消息是你的思维非常接近正确。坏消息是非常接近部分是合格的正确性。首先,让我们解决你对指针的困惑。

指针只是一个变量,它将地址保存为其值。换句话说,指针指向可以找到其他内容的地址。例如,int a = 5;存储立即值5作为其值,int *b;创建指向int的指针,b = &a;存储a的地址(当前存储5的内存地址)作为其值。如果您需要存储在内存地址b处的值,则取消引用 b使用一元'*'运算符,例如int c = *b;将初始化c = 5)。由于b指向存储5的地址,如果您更改该值(例如*b = 6;6现在存储在5所在的地址之前。由于b指向a的地址,并且您更改了该地址的值,a现在等于6

指针的type设置使用指针算法时提前的字节数。如果你有一个指针(这里是一个字符串文字),例如

char *p = "abc";

然后,每次递增指针p时,p保持的地址会增加1-byte。例如,在p++;之后,p指向b,因此putchar (*p);将输出'b';由于每个字符串都是 nul-terminated ,并带有 nul-character '\0'(带小数值0),因此您可以简单地遍历使用指针p

的字符串
while (*p)
    putchar (*p++);
putchar ('\n');

将输出abc,然后输出newline。使用integer数组时,每个元素都需要4-bytestype设置大小的美丽int array[] = {1, 2, 3}; int *p = array,你可以同样迭代array推进指针p++(但请注意,整数数组不是 nul-terminated,因此您必须手动限制迭代次数,例如

while (p < array + 3)
    printf (" %d", *p++);
putchar ('\n');

哪个会输出" 1 2 3",然后输出newline

考虑到这一点,您可以在代码中解决许多问题。首先,不要在代码中使用幻数(例如21, 41),如果需要常量,请定义它。您还可以使用全局枚举来定义常量。这里,48的单个常数对两者都是足够的,例如:

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

#define MAXC 48     /* if you need a constant, define one (or more) */
...

接下来,将srand()移至main(),以便只调用一次:

int main (void)
{
    char s1[MAXC] = "", 
        s2[MAXC] = ""; 
    int c, entry = 0;       /* c must be type int to detect EOF */
    size_t len = 0;         /* len to trim trailing '\n' from fgets */

    srand(time(NULL));
    ...

通过声明常量MAXC(对于最多字符),您的getRandomStr会相应更改:

void getRandomStr (char *ps1) 
{
    int i;

    /* must leave room for nul-terminating character */
    for (i = 0; i < MAXC - 1; i++) {

        int letter = rand() % 26 + 'A';
        *(ps1 + i)  = letter;
    }

    *(ps1 + MAXC - 1) = '\0';
}

您还应该更新main()中的提示,以使用最大字符数来告知用户限制,例如

    do {
        /* get random string (s1) through function getRandomStr */
        getRandomStr (s1);
        printf ("Random string generated (s1): ");
        /* print rand string by printf("Random string generated: %s\n", s1);*/
        /* OR.. */
        puts (s1);
        printf ("\nPlease enter up to %d letters to be replaced (s2): ",
                MAXC - 1);
    ...

永远,因为害怕遭到枪击和殴打,请使用gets()。如上所述,它是如此不安全,并且很容易被缓冲区溢出利用,它已经完全从C11库中删除。而是使用fgets

        /* operator enters string (s2) up to 20 chars */
        if (!fgets (s2, MAXC, stdin)) { /* validate all user-input */
            fprintf (stderr, "(user canceled input)\n");
            break;
        }

所有有效的面向行的输入函数(例如fgets和POSIX getline)都会读取并在其填充的缓冲区中包含尾随'\n'。 (使用fgets - 只要有足够的空间来读取整行。因此,您应该通过使用 nul-terminatedating 字符覆盖它来删除尾随'\n',或者在代码中考虑它的存在。处理此问题的一种简单方法是获取缓冲区中字符串的长度,并覆盖'\n',例如

        len = strlen (s2);                  /* get length */
        if (len && s2[len - 1] == '\n')     /* check last char == '\n' */
            s2[--len] = 0;                  /* overwrite with nul-char */

您还可以使用您喜欢的任何string.h函数来查找尾随'\n'并覆盖它。

接下来,如上所示,您必须验证所有用户输入,以避免代码出现问题。因此,当您使用getchar()时,您需要检查用户是否只是按 Enter 。您还需要处理输入缓冲区('\n'此处)中未读取的stdin,以确保它不会破坏您的下一个输入。由于您将重复执行此操作,因此编写一个简单的辅助函数来帮助它是有意义的,例如

void empty_stdin (void)
{
    int c = getchar();

    while (c != '\n' && c != EOF)
        c = getchar();
}

您还必须知道stdin中何时需要清空其他字符。在空empty_stdin上调用stdin只会阻止,直到有一个字符被读取...

使用您的新功能,您现在可以更加健壮地获取"replacement character"

        printf("\nPlease enter a replacement character (Ex. *, $, etc.) (c): ");

        /* operator enters a replacement char (c) */
        while ((c = getchar()) == '\n') {
            if (c == EOF) {
                fprintf (stderr, "(user canceled input)\n");
                goto done;
            }
            fprintf (stderr, "error: invalid (empty) input.\n");
        }
        empty_stdin();
        ...
    } while (entry == 'y' || entry == 'Y');
    done:;
}

接下来,check()必须返回一个值来表示成功/失败。否则,您无法知道支票的结果。如果您只需要知道某些事情是成功还是失败,您只需为该指示返回int,例如

int check (char *ps2)
{
    /* while s2 is not end of string */
    while (*ps2)
    {
        /* if s2 not a letter A-Z, give error message */
        if (*ps2 < 'A' || 'Z' < *ps2)
        {
            fprintf (stderr, "An invalid character was entered.\n");
            return 1;
        }
        ps2++;
    }
    return 0;
}

然后使用check()实际上可以在main()中有意义,例如

        /* Error check to verify entered string (s2) is a letter A-Z */
        if (check (s2) == 1) {
            fprintf (stderr, "error: s2 contains char not A-Z.\n");
            empty_stdin();
            continue;
        }

对于cont()也是如此,因为你在该函数中接受输入,你必须有一种方法在main()中指明你是否得到了有效的输入,以及那个输入是什么。同样,返回int可以完成两者。输入失败或输入无效,return 0;,否则为return c;,例如

int cont (int c)
{
    printf ("\nDo you want to run the program again (Y/N)? ");
    if ((c = getchar()) == '\n' || c == EOF) {
        fprintf (stderr, "(empty or user canceled input)\n");
        return 0;
    }
    empty_stdin();

    return c;
}

(是的,c, entry, check & cont的类型必须为int,因为EOFchar EOFint可以对main()进行测试(4字节))

您在 if ((entry = cont(c)) == 0) break; 中验证:

strfilter

最后,您可以对传递给void strfilter(char *ps1, char *ps2, char c) { while (*ps2) { char *p = ps1; while (*p) { if (*p == *ps2) *p = c; p++; } ps2++; } /* if want to print filtered s1 from function */ /* puts(ps1); */ } 的指针使用简单迭代来简化函数,例如。

-Wall -Wextra -pedantic

如果你完全放弃,你最终会得到一个不会输出奇怪字符的程序。为了更好地看不到有趣的字符,总是使用警告启用进行编译,这意味着将cl.exe添加到gcc / clang上的编译器选项或VS(/W3 )至少使用scanf_s) - 并且在没有警告的情况下编译之前不接受代码。 (你可以通过听取编译器告诉你的内容来学习很多C语言,并解决它告诉你的关于它给你的行号的问题)。所有人都说(并将getchar替换为scanf_s - 我不在Windows上,并且gcc没有实现包含#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define MAXC 48 /* if you need a constant, define one (or more) */ void getRandomStr(char *ps1); void strfilter(char *ps1, char *ps2, char c); int check(char *ps2); int cont(int c); void empty_stdin(void); int main (void) { char s1[MAXC] = "", s2[MAXC] = ""; int c, entry = 0; /* c must be type int to detect EOF */ size_t len = 0; /* len to trim trailing '\n' from fgets */ srand(time(NULL)); do { /* get random string (s1) through function getRandomStr */ getRandomStr (s1); printf ("Random string generated (s1): "); /* print rand string by printf("Random string generated: %s\n", s1);*/ /* OR.. */ puts (s1); printf ("\nPlease enter up to %d letters to be replaced (s2): ", MAXC - 1); /* operator enters string (s2) up to 20 chars */ if (!fgets (s2, MAXC, stdin)) { /* validate all user-input */ fprintf (stderr, "(user canceled input)\n"); break; } len = strlen (s2); /* get length */ if (len && s2[len - 1] == '\n') /* check last char == '\n' */ s2[--len] = 0; /* overwrite with nul-char */ printf("\nPlease enter a replacement character (Ex. *, $, etc.) (c): "); /* operator enters a replacement char (c) */ while ((c = getchar()) == '\n') { if (c == EOF) { fprintf (stderr, "(user canceled input)\n"); goto done; } fprintf (stderr, "error: invalid (empty) input.\n"); } empty_stdin(); /* Error check to verify entered string (s2) is a letter A-Z */ if (check (s2) == 1) { fprintf (stderr, "error: s2 contains char not A-Z.\n"); empty_stdin(); continue; } printf ("\nFiltered string (s1): "); strfilter (s1, s2, c); /* print filtered string s1 */ puts (s1); if ((entry = cont(c)) == 0) break; } while (entry == 'y' || entry == 'Y'); done:; } void getRandomStr (char *ps1) { int i; /* must leave room for nul-terminating character */ for (i = 0; i < MAXC - 1; i++) { int letter = rand() % 26 + 'A'; *(ps1 + i) = letter; } *(ps1 + MAXC - 1) = '\0'; } void strfilter(char *ps1, char *ps2, char c) { while (*ps2) { char *p = ps1; while (*p) { if (*p == *ps2) *p = c; p++; } ps2++; } /* if want to print filtered s1 from function */ /* puts(ps1); */ } int cont (int c) { printf ("\nDo you want to run the program again (Y/N)? "); if ((c = getchar()) == '\n' || c == EOF) { fprintf (stderr, "(empty or user canceled input)\n"); return 0; } empty_stdin(); return c; } int check (char *ps2) { /* while s2 is not end of string */ while (*ps2) { /* if s2 not a letter A-Z, give error message */ if (*ps2 < 'A' || 'Z' < *ps2) { fprintf (stderr, "An invalid character was entered.\n"); return 1; } ps2++; } return 0; } void empty_stdin (void) { int c = getchar(); while (c != '\n' && c != EOF) c = getchar(); } 的建议扩展 - 没有错如果没有这个功能,你可以做一些事情,比如

$ ./bin/rndstring
Random string generated (s1): QFYFRUKOJVTZIXQXNQWATHJULIYYEMOWTMBKINYUKTVSQLP

Please enter up to 47 letters to be replaced (s2): QFYRU

Please enter a replacement character (Ex. *, $, etc.) (c): J

Filtered string (s1): JJJJJJKOJVTZIXJXNJWATHJJLIJJEMOWTMBKINJJKTVSJLP

Do you want to run the program again (Y/N)? Y
Random string generated (s1): DBOFXXORIYQHEEVAXKDJUQHQAALTTXKYAYGXVURGVJNZNKC

Please enter up to 47 letters to be replaced (s2): DBOFX

Please enter a replacement character (Ex. *, $, etc.) (c): Z

Filtered string (s1): ZZZZZZZRIYQHEEVAZKZJUQHQAALTTZKYAYGZVURGVJNZNKC

Do you want to run the program again (Y/N)? N

示例使用/输出

{{1}}

仔细看看,如果您有其他问题,请告诉我。