c语言 getchar_3个C语言编程易犯的错误:也许你也犯过(附代码)

963fffe83903aa5b8aee39926e5e0933.png

来源于公众号“C语言与程序设计 ”,作者:薛定谔的黑白猫


典型错误1:
指针指向

880680de875db05344da8a81f9eefdda.png

上述代码意图比较明显: 定义了一个int变量a和指针变量pa,并且把a的地址给了指针pa。接着通过键入给a赋值,但运行结果如下: d80b5baec78f9157d9e073f649239275.png 其实这个问题是我们学习指针的时候的一个典型错误了,我们知道调用scanf函数给变量赋值时,赋值对象要为地址的形式,通常是加取址符“&”,但是这里采用的是*pa的格式,这里涉及的指针相关知识前面给大家讲过,为了更好地理解本题,就再重复一下: 对于指针来说,有己址、己值、它址、它值等特点,己址就是指针变量本身的地址,己值就是指针变量本身地址所存放的值,也就是我们通常说的指向的地址,这也正是它址,所以己值和它址意义是一样的,而它值就是指针指向地址位置所存放的值。 而这里的*pa表示的意义就是它值a,那就是说这么写的话下面两行代码是等价的:
scanf("%d", *pa);scanf("%d", a);
对比过后显然是错误的,大家一眼看出a要写成&a,这没问题。但也有人说可以把*pa改成&pa,这样行吗? 其实这么说的人还是对指针中己值和己址的概念没搞清楚,&pa表示的意义是己址,即指针变量本身的地址,就是说你试图用scanf修改指针变量本身地址上的值,而这个值原本是变量a的地址,其实就是在修改指针的指向!正确的写法应该这样:
scanf("%d", pa);
pa表示a的地址,即为它址,也就是&a,所以上面写法才与下面的等价:
scanf("%d", &a);

典型错误2:getchar函数

char c;while((c=getchar())!=EOF){...}

这段代码的本意是用getchar函数读取缓冲区字符直到结束,但是在编译运行时,发现上面几行代码一直报错!逻辑上没问题啊,那这究竟错在哪里?读者可以自己思考一下再往下看。 其实产生报错的原因有两点,一个是对getchar函数理解不到位,另一个是EOF的问题。
我们首先来说说getchar函数的问题,标准库中给出了该函数的使用说明:在它读取一个字符后,会将其转换为int类型返回,所以首先char c要改为int c,关于getchar的问题还没讲完,后面还要说。 我们接着来看看EOF的问题,初学者对它的理解经常会有偏差,首先它是一个宏,定义于头文件,为-1;其次它并不是很多人理解的文件结束符,实际上它是一个标志位,区别于其他所有字符的存在,表示一种没有其他字符的信号。 讲到这里,我们再回到getchar函数,由上面可以看出它的返回值必须是一个能包含所有字符的数据类型,方便它表示任意字符和EOF等标志位。 因此,上面代码的错误就很明显了,可能有两种情况: 1.如果编译器中的char是有符号的且EOF被定义为-1,而恰好有字符等于0xff,那么getchar就会提前结束。当然,如果输入全部是7位以下的字符,那很长时间不会有错误。 2.如果编译器中的char是无符号的,则实际的EOF值会被截断,不再会识别为EOF,将会陷入无限循环。 这里肯定会有人问我们键入-1来模拟EOF跳出循环不行吗?实际上是不行的,-1是有-和1两个字符组成的,而getchar一次只能读取一个字符,所以上述代码EOF与从键盘输入的字符无关,那这岂不是只能死循环了?当然不是,我们可以通过按键组合ctrl+d或者ctrl+z来指示结束,当然,这里的按键组合输入只是我们的一种约定,不应该显示检查按键组合的值。

典型错误3:存储机制

char *p = NULL;p = "hello world";strcpy(p, "hello world");

题目很简单,就问这段代码写的有没有问题,如果有,问题在哪里? 其实这个问题如果你对C语言的存储机制非常熟悉的话,应该是很简单的:我们简单分析一下,第一行代码是没问题的,第二行意思是让指针p指向字符串常量,单看也没问题,而问题就出在第三行了,它的意图是对指针p指向地址的内容进行修改,当然还用“hello world”只是为了增加点迷惑性而已。 上面说到了C语言的内存机制,其实第二行代码过后,hello world 作为字符串常量存放于内存中的常量区,且是只读,而此时指针p存放的是字符串常量的地址,第三行代码企图通过strcpy修改只读段的内容,因此很明显会报错,这也是这三行代码的问题所在了。 关于C的存储问题,可能有的人还不太了解,那就借这个机会简单给大家提几句,这也是以前我写过的问题: 一个编译的C程序占用的内存分为以下几个部分:
1、栈区(stack)—也称自动类型存储区,由编译器自动分配释放,存放函数的参数值,局部变量的值等,例如函数调用结束后自动释放。
2、堆区(heap)—也称动态分配内存区,由程序员分配释放,从分配到程序结束为止,若不释放,程序结束时可能由OS回收,比如malloc分配的内存,free释放的内存。
3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。
4、文字常量区—常量字符串放在这里,程序结束后由系统释放。
5、程序代码区—编译后的程序代码放在这里。           来看一个具体的C程序 f1b7117eea9e08bfd27ea0e300883866.png 怎么样?问题虽然简单,但也给我们以后写代码提了个醒,这种不易察觉的错误大家一定要小心再小心,尽量避免,就说到这里吧,感谢大家耐心阅读! -END-
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个基于C语言的2048小游戏代码: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> #define ROW 4 #define COL 4 void init_board(int board[][COL]) { int i,j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { board[i][j] = 0; } } } void print_board(int board[][COL]) { int i,j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { printf("%4d",board[i][j]); } printf("\n"); } } void random_number(int board[][COL]) { int i,j; int count = 0; int rand_num = (rand()%2+1)*2; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (board[i][j] == 0) { count++; } } } int x = rand()%ROW; int y = rand()%COL; int n = rand()%count; count = 0; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (board[i][j] == 0) { if (count == n) { board[i][j] = rand_num; return; } count++; } } } } int can_move(int board[][COL]) { int i,j; for (i = 0; i < ROW; i++) { for (j = 0; j < COL; j++) { if (board[i][j] == 0) { return 1; } if (i > 0 && board[i][j] == board[i-1][j]) { return 1; } if (i < ROW-1 && board[i][j] == board[i+1][j]) { return 1; } if (j > 0 && board[i][j] == board[i][j-1]) { return 1; } if (j < COL-1 && board[i][j] == board[i][j+1]) { return 1; } } } return 0; } void move_left(int board[][COL]) { int i,j,k; for (i = 0; i < ROW; i++) { k = 0; for (j = 1; j < COL; j++) { if (board[i][j] != 0) { if (board[i][j] == board[i][k]) { board[i][k] *= 2; board[i][j] = 0; } else if (board[i][k] == 0) { board[i][k] = board[i][j]; board[i][j] = 0; } else { k++; board[i][k] = board[i][j]; if (k != j) { board[i][j] = 0; } } } } } } void move_right(int board[][COL]) { int i,j,k; for (i = 0; i < ROW; i++) { k = COL-1; for (j = COL-2; j >= 0; j--) { if (board[i][j] != 0) { if (board[i][j] == board[i][k]) { board[i][k] *= 2; board[i][j] = 0; } else if (board[i][k] == 0) { board[i][k] = board[i][j]; board[i][j] = 0; } else { k--; board[i][k] = board[i][j]; if (k != j) { board[i][j] = 0; } } } } } } void move_up(int board[][COL]) { int i,j,k; for (j = 0; j < COL; j++) { k = 0; for (i = 1; i < ROW; i++) { if (board[i][j] != 0) { if (board[i][j] == board[k][j]) { board[k][j] *= 2; board[i][j] = 0; } else if (board[k][j] == 0) { board[k][j] = board[i][j]; board[i][j] = 0; } else { k++; board[k][j] = board[i][j]; if (k != i) { board[i][j] = 0; } } } } } } void move_down(int board[][COL]) { int i,j,k; for (j = 0; j < COL; j++) { k = ROW-1; for (i = ROW-2; i >= 0; i--) { if (board[i][j] != 0) { if (board[i][j] == board[k][j]) { board[k][j] *= 2; board[i][j] = 0; } else if (board[k][j] == 0) { board[k][j] = board[i][j]; board[i][j] = 0; } else { k--; board[k][j] = board[i][j]; if (k != i) { board[i][j] = 0; } } } } } } int main() { int board[ROW][COL]; char ch; srand(time(NULL)); init_board(board); random_number(board); random_number(board); print_board(board); while (1) { ch = getchar(); if (ch == 'q') { break; } switch(ch) { case 'a': move_left(board); break; case 'd': move_right(board); break; case 'w': move_up(board); break; case 's': move_down(board); break; default: continue; } if (can_move(board)) { random_number(board); print_board(board); } else { printf("Game over!\n"); break; } } return 0; } ``` 这是一个控制台版的2048小游戏,你可以使用wasd或者方向键来移动方块,q键退出游戏。祝你玩得愉快!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值