使用纯指针表示法和循环 [英] working with pure pointer notation and loops

查看:89
本文介绍了使用纯指针表示法和循环的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经完成了这项工作,必须将其从数组表示法转换为纯指针表示法,它正在工作,我已经完成了,但是我想问一些我不清楚的问题.我真的很想完全理解指针,因为它们是我的失败.

I have this finished assignment that I had to convert from array notation to pure pointer notation, it is working, Im done with it but I want to ask some questions that I am not clear of. I really want to fully understand pointers as they are my downfall.

-首先,是否不需要将指针分配给变量的地址?例如...

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

我以为我做到了,但是没有它,代码可以正常工作.为什么?我的指针如何知道去该变量的第一个元素?是因为我的指针仅在函数中是局部的吗?

I thought I did, but the code works fine without it. Why? How does my pointer know to go the first element of that variable? Is it because my pointers are only local, only in my functions?

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

-Also, right now theres always a new random string generated, if I want it to always be the same string, how would I do that? I tried doing..

getRandomStr(s1);
originals1[41] = getRandomStr;

在do while循环之外,然后在do while替换中

outside of the do while loop and then in the do while replace

puts(s1);

使用

 puts(originals1);

一直有效,始终是相同的字符串,但是我的前面有很多奇怪的字符.

and that worked, was always same string but I had a bunch of weird characters in the front.

这是我的代码.

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

推荐答案

好消息是您的想法非常正确.坏消息是非常接近部分是正确的正确数量.首先,让我们解决您对指针的困惑.

The good news is your thinking is very close to correct. The bad news is the very close part is a qualified amount of correctness. First, let's address your confusion over pointers.

指针只是一个变量,它保存其他内容的地址作为其值.换句话说,指针指向可以找到其他内容的地址.例如,当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.

A pointer is simply a variable that holds the address of something else as its value. In other words, a pointer points to the address where something else can be found. For example, while int a = 5; stores the immediate value 5 as its value, int *b; creates a pointer to int, and b = &a; stores the address of a (the memory address where 5 is currectly stored) as its value. If you need the value stored at the memory address b points to, you dereference b using the unary '*' operator, e.g. int c = *b; will initialize c = 5). Since b points to the address where 5 is stored, if you change that value (e.g. *b = 6;) 6 is now stored at the address where 5 was before. Since b points to the address of a and you have changed the value at that address, a now equals 6.

指针的type设置使用指针算术时的高级字节数.如果您有一个指针(这里是字符串文字),例如

The type for the pointer sets the number of bytes advanced when using pointer arithmetic. If you have a pointer (here to a string literal), e.g.

char *p = "abc";

然后,每次增加指针p时,p所保存的地址都会增加1-byte.例如,在p++;之后,p指向b,因此putchar (*p);将输出'b';由于每个字符串都是C,且以 nul-字符 '\0'(具有十进制值0)为 nul终止,因此您可以使用指针p

Then each time you increment the pointer p, the address held by p is incremented by 1-byte. For example, after p++;, p points to b, so putchar (*p); will output 'b'; Since each string is C is nul-terminated with the nul-character '\0' (with decimal value 0), you can simply iterate over the string using pointer p with

while (*p)
    putchar (*p++);
putchar ('\n');

将输出abc,然后输出newline.对于integer数组,每个元素都需要4-bytes. type设置大小的妙处在于int array[] = {1, 2, 3}; int *p = array,您也可以在array上进行迭代,使指针p++前进(但请注意,整数数组不是以n终止的,因此您必须手动限制迭代次数,例如

will output abc and then the newline. With an integer array each element requires 4-bytes. The beauty of the type setting the size is for int array[] = {1, 2, 3}; int *p = array, you can likewise iterate over array advancing the pointer p++ (but note, and integer array is not nul-terminated, so you have to manually limit the number of iterations, e.g.

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

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

Which would output " 1 2 3" and then the newline.

考虑到这一点,您可以在代码中解决许多问题.首先,请勿在代码中使用魔术数字(例如21, 41),如果您需要一个常量,请对其进行定义.您还可以使用全局枚举来定义常量.在这里,48的单个常数对于两个都足够,例如:

With that in mind, there are a number of issues you can address in your code. First, don't use magic numbers in your code (e.g. 21, 41), if you need a constant, define it. You can also use a global enum to define constants. Here, a single constant of 48 is adequate for both, e.g.:

#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(),以便仅调用一次:

Next, move srand() to main() so it is only called once:

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将相应地更改:

By declaring a constant MAXC (for max characters), your getRandomStr will change accordingly:

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()中更新提示,以使用最大字符数来向用户告知限制,例如

You should also update your prompts in main() to use the maximum number of characters to advise the user of the limits, e.g.

    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

Do NOT ever, ever, over fear of being shot and riduculed, use gets(). As explained, it is so insecure and so prone to exploit by buffer overrun, it has been completely removed from the C11 library. Instead use 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-termination 字符覆盖尾随的'\n'来删除尾随的'\n',或者以其他方式在代码中说明其存在.一种简单的处理方法是获取缓冲区中字符串的长度,然后覆盖'\n',例如

All valid line-oriented input functions (e.g. fgets and POSIX getline) read and include the trailing '\n' in the buffer they fill. (with fgets -- provided there is enough space to read the entire line). So you should either remove the trailing '\n' by overwriting it with a nul-terminating character, or otherwise account for its existence in your code. A simple way to handle this is get the length of the string in the buffer, and overwrite the '\n', e.g.

        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'并将其覆盖.

You can also use any of the string.h functions you like to locate the trailing '\n' and overwrite it.

下一步,如上所述,您必须验证所有用户输入,以避免代码中出现问题.因此,当您使用getchar()时,您需要检查用户是否只是点击了 Enter .您还需要处理输入缓冲区(此处为stdin)中尚未读取的'\n',以确保它不会破坏您的下一个输入.由于您会重复执行此操作,因此编写一个简单的帮助程序功能来进行帮助很有意义,例如

Next, as indicated above, you must validate all user input in order to avoid problems in your code. So when you use getchar() you need to check that the user didn't just simply hit Enter. You also need to handle the '\n' that remains unread in the input buffer (stdin here) to insure it doesn't torpedo your next input. Since you will do this repeatedly, it makes sense to write a simple helper function to assist, e.g.

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

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

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

You must also know when there are additional characters in stdin that you need to empty. Calling empty_stdin on an empty stdin will simply block until there is a character to be read...

有了新功能,您现在可以以更强大的方式获得"replacement character":

With your new function, you can now get your "replacement character" in a more robust manner:

        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,例如

Next, check() must return a value to indicate success/failure. Otherwise, you have no way of knowing the result of your check. If you simply need to know whether something succeeded or failed, you can simply return an int for that indication, e.g.

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()中有意义,例如

Then using check() can actually make sense in main(), e.g.

        /* 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;,例如

The same is true with cont(), since you take input in that function, you must have a way to indicate in main() whether you got valid input, and what that input was. Again, returning an int can accomplish both. Failed input or invalid input, return 0;, otherwise, return c;, e.g.

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,因为由于EOFint(4字节),因此可以使用charEOF进行测试)

(and yes the type for c, entry, check & cont must be int, because you can test against EOF with char due to EOF being an int (4-bytes))

您在main()中使用以下命令进行验证:

You validate in main() with:

        if ((entry = cont(c)) == 0)
            break;

最后,您可以对传递给strfilter的指针进行简单的迭代,以简化该函数,例如

Lastly, you can use simple iteration over the pointer passed to strfilter to simplify the function a bit, e.g.

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

如果完全放在一起,最终将得到一个不提供输出奇怪字符的程序.要有更好的机会看不到有趣的字符,请始终使用 warnings enable 进行编译,这意味着将-Wall -Wextra -pedantic添加到gcc/clang的编译器选项中,或者对于VS(cl.exe)至少使用/W3)-并且不接受代码,直到代码在没有警告的情况下进行编译. (通过听编译器告诉您的内容,并解决它告诉您的有关行号的问题,您可以学到很多C语言).所有人都说过(用getchar替换scanf_s -我不在Windows上,并且gcc没有实现建议的包含scanf_s的扩展名-否则该功能没有任何问题),您可以做些什么例如,

If you put it altogether, you end up with a program that does not give output weird characters. To have a better chance at not seeing funny characters, always compile with warnings enable, that means adding -Wall -Wextra -pedantic to your compiler options on gcc/clang, or for VS (cl.exe) use at least /W3) -- and do not accept code until it compiles without warning. (you can learn a lot of C, just by listening to what your compiler tells you -- and fixing the problems it tells you about on the line numbers it gives you). All said (and replacing scanf_s with getchar -- I'm not on windows, and gcc doesn't implement the proposed extension containing scanf_s -- there is nothing wrong with that function otherwise), you could do something like, e.g.

#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

仔细检查一下,如果还有其他问题,请告诉我.

Look things over and let me know if you have further questions.

这篇关于使用纯指针表示法和循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆