你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

给大家分享几个C语言中的坑

[复制链接]
gaosmile 发布时间:2020-4-30 16:05
今天给大家分享几个C语言中的坑。
一、带参数的宏展开顺序#include <stdio.h>. X. l, ?* ^9 Z6 i* T" {9 w  r( v
#define f(a,b) a##b
- c% o: U9 I0 K8 B#define g(a) #a5 H6 D  m! n% I5 n* _
#define h(a) g(a)
6 f* f" ^6 |' H! R% ]5 {
& ]7 ^  y# B7 W3 X8 lint main(void)$ c5 @. T% a/ r" E
{: J9 n8 ~( r( e
     printf("%s\n",h(f(1,2)));
# v; Y4 {5 e/ Y: a# w& n     printf("%s\n",g(f(1,2)));
; j" R6 p# D8 [$ P2 ~# I
6 R, s6 G- V* `% j     return 0;
9 i: N/ e; N/ y}

- B5 e: Q0 z* r: |3 ~) w% \9 K
运行结果:

) ~" j+ [2 D' e7 e: k0 j
123 t) F5 d3 j& R/ \( M" Y6 d

0 ~+ D6 v) S. x* D% N. Mf(1,2)
/ K& G" A1 L; m  N4 e" E+ i
浅析:
本题中的#运算符可以利用宏参数创建字符串。##运算符和#运算符一样也可以用于类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分,这个运算符可以把两个语言符号组合成单个语言符号,所以该运算符也被成为“预处理粘合剂”。类参数宏展开遵循一定的顺序,先从外层开始探寻如果遇到#即刻结束探寻,从遇到#处开始一步一步向外层展开,如果没有遇到#探寻到最里层结束探寻,然后一步一步向外层展开。

% n+ @4 d* K7 |( @# @
所以printf("%s\n",h(f(1,2)));这条语句的展开顺序为:h(f(1,2))(没有#)  --->>  f(1,2)(到达最里层依然没有#)  ---->>  h(12)  ---->>  12。
) `: T  m1 u+ `/ o
然而printf("%s\n",g(f(1,2)));这条语句的展开顺序是:g(f(1,2))(碰到#即刻结束探寻,开始展开)  ----->>f(1,2)。
二、类型转换#include <stdio.h>" Z% Q/ C% x5 M( Y3 Z
int main(void)* E* F8 B4 `( c5 `5 A
{! T+ f- i' ?9 i$ y8 D
int a = -10;4 M6 r9 E. K( W! `
unsigned b = 1;5 Y: y! C. F$ B
if(a+b > 0)
3 W9 u* k& u- y" C: Zprintf("a + b > 0\n");# b- H3 s, B' @& f: d) A' u
else if (a + b < 0)
4 i! {3 q, D! C. ]. dprintf("a + b < 0\n");* P% v+ @4 v5 e% w- T9 ]
else1 S3 h# a* z9 {0 S9 H
printf("a + b = 0\n");/ [7 V& C/ j" W# r
return 0;
4 Y2 s. E  {  G* p1 S}
  Q4 C- w5 i! y( u6 O0 v" b
运行结果:

& g( j: B7 \% |  {
a + b > 0

% c+ L; P' U2 [4 M1 `& n" Y
浅析:
第一眼看到这道题心里想到这不明摆着 -10 + 1 < 0么,如此easy的题目还要算吗?当程序运行出结果时顿时傻眼了,仔细看了看数据类型发现问题出在了类型的转换上。众所周知,在不同类型的数据进行运算时如果不进行特别的转换那么在数据运算时会先将表示范围较小的数据自动转换成表示范围更广的数,再参与运算,所以本题中会先将int型的a转换成unsigned int型,通过补码运算得知该值为:4294967286,该值加上1会肯定会远大于0,因此输出的是a+b>0。
三、溢出问题
程序一:

2 A! U8 |8 y# \, @, L$ D
unsigned i;
, w$ w  r' A. ?  r8 nfor (i = 110; i >= 0; i--)
* f8 k# g% w% u& B  printf("%u\n",i);
5 @+ E- b) X) r- X) n0 z. I2 p
运行结果:
死循环

% i5 v' a3 @; c  }& u9 w! Z
浅析:
该题的坑就在于没有注意到unsigned int 的存储范围,当小于零溢出时又会从unsigned int 的最大值开始递减,这就仿佛进入了一个圆环,永远都没有办法找到跳出圆环形跑道的缺口。

% B7 B# b" v0 [" G- q4 O# \
程序二:
2 D3 q8 [; T3 M# }: p" V- a$ V# g
#include <stdio.h>
: Z! S2 I) G, w. [9 W- m#include <string.h>
7 Z2 G5 a1 C$ b2 n" tint main(void)
: e. g. W1 X5 N9 L- [+ Q) O8 g
{
& E) j; `# `1 x- ?! \  char a[1000];# g9 S* o/ l- B4 _" u/ m
  int i;6 x1 I$ U1 z6 h8 e  W3 B8 t* E
  for(i = 0; i < 1000; i++): L' S5 Z5 @1 h  @# Q
    a = -1 -  i;
* b  I  {+ N5 Y7 f  printf("%d\n",strlen(a));
' U5 ]9 T: P* @. r+ {  return 0;
. u8 L% I  ?4 y% g0 n8 e}
7 }! Y' V. E7 z8 |# u! X0 d7 _; l
运行结果:255
: N, E+ f, O/ Z
浅析:这道题看上去很简单但是却暗藏杀机,很少有人能够答对,当i从0开始自增,自增到127时-1 - 127 = -128,而这个数正好是char型变量所能表示的最小数字,i再自增一次就会溢出,变成char所能表示的最大数字,这样又进入了上一题的那个“环”,当i增加到255时-1 - i = 0,此时第一次出现了0,而strlen函数碰到'\0'就结束(不包括),因此输出结果为255。四、strcpy函数void test()) f, h5 z) l. w9 N0 a( l
{( y5 `7 J; x: d' U
char str[10],str1[10];
/ S8 ], F: @3 z# B, A9 N6 fint i;  w0 b& X4 a- p
for(i = 0; i < 10; i++)
" B8 r$ C8 \! u  {
% @: R6 |  [* W. `/ }( c4 b3 u7 ]    str1 = 'a' + i;
4 p# T  \0 b/ O  }: M  L% P% J) T. o) r- e
strcpy(str,str1);7 x2 Q% J- S. ^/ R* d. g9 X
}
' b  `  ]$ h' g. C) S
浅析:
这段代码第一眼看过去是没问题的,但是再看一眼就能够很轻松找到错误了,strcpy函数是拷贝字符串的函数,它是以'\0'为结尾的,因此当程序运行strcpy这一行时会发生内存非法访问导致程序崩溃。0 |" C1 n. V4 c7 m6 }
赞 收藏 评论0 发布时间:2020-4-30 16:05

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版