我有这个完成的任务,我必须从数组符号转换为纯指针符号,它正在工作,我完成了它但我想问一些我不清楚的问题。我真的想完全理解指针,因为它们是我的垮台。
- 首先,我不需要将指针指向变量的地址吗?例如......
char *ps1 = &s1;
char *ps2 = &s2;
我以为我做了,但没有它,代码工作正常。为什么?我的指针如何知道该变量的第一个元素?是因为我的指针只是本地的,只在我的函数中?
- 另外,现在总是生成一个新的随机字符串,如果我希望它始终是相同的字符串,我该怎么做? 我试过......
getRandomStr(s1);
originals1[41] = getRandomStr;
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++;
}
}
答案 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-bytes
。 type
设置大小的美丽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
,因为EOF
因char
EOF
而int
可以对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}}
仔细看看,如果您有其他问题,请告诉我。