社区导航

 

搜索
查看: 1131|回复: 16

[源码分析] 【辛版的小编程题】指针作为参数传递?

[复制链接]

7903

TA的帖子

54

TA的资源

裸片初长成(中级)

Rank: 11Rank: 11Rank: 11Rank: 11

荣誉会员勋章

发表于 2017-12-10 22:10 | 显示全部楼层 |阅读模式
本帖最后由 辛昕 于 2018-3-30 02:11 编辑

这个小小的编程题,答案很简单。
但是,它背后引发出来的一句简短结论,却很值得玩味——

以指针(地址)的形式传递形参,形参可以被修改。
那么问题来了,如果要修改的就是指针的值呢?
那——自然就要传入 指针的 指针(地址)了。


很多时候,由于大多数语言的函数只能至多返回一个返回值(Go除外)。
所以我们经常会把想返回的多几个变量,放在形参里。
这就引发一个问题。
我们太过于熟悉 把地址传入形参,就可以修改该地址指向的内容 这个常识。
由于传递形参时,作为地址传递 和 指针 长得太像。
所以包括我在内,相信绝大多数人都下意识 把 指针 和 地址等同起来。
于是,当有一天我们要修改的是 指针本身的时候,就往往很难想起来
这次要传递的是 指针的指针(二级指针)了。


楼下这段代码,是我最近写的一个EGE上的小函数,用来支持图片不同颜色格式的兼容。
这个函数的用意简单解释如下:
      图像文件以数组形式给出,最后因为不同的RGB位数,它们在逐点取的时候,偏移的量是不同的,
RGB16是2字节,RGB24是3字节,RGBA24是4字节。
      下面这个函数,为了传递出下一个读取的位置,又因为返回值已经被占用,因此,把返回的下一个
图像数组的位置以形参的形式传递。
  1. color_t get_pixel_color(uint8_t *pSrc,uint8_t *pNext)
  2. {
  3.     uint8_t *p;

  4.     if(ColorType == RGB32)
  5.     {
  6.         p = pSrc;
  7.         pNext = pSrc + 4;
  8.         return (EGERGB(*p,*(p + 1),*(p + 2) ) );
  9.     }
  10.     else if(ColorType == RGB24)
  11.     {
  12.         p = pSrc;
  13.         pNext = pSrc + 3;
  14.         return (EGERGB(*p,*(p + 1),*(p + 2) ) );
  15.     }
  16.     else if(ColorType == RGB16)
  17.     {
  18.         uint16_t rgb16;
  19.         rgb16 = *(uint16_t *)pSrc;
  20.         pNext = pSrc + 2;
  21.         return rgb162color(rgb16);
  22.     }
  23.     else
  24.         return (EGERGB(0,0,0) );    // 非支持格式,输出全黑以示错误
  25. }
复制代码


这个函数里使用了一个特定的图形函数库 EGE,所以对于一些非标ANSI的定义和宏,请
直接忽略,它们一点都不影响这个问题本身。

八年一梦,洗尽铅华,重头再来


回复

使用道具 举报

7903

TA的帖子

54

TA的资源

裸片初长成(中级)

Rank: 11Rank: 11Rank: 11Rank: 11

荣誉会员勋章

 楼主| 发表于 2017-12-10 22:16 | 显示全部楼层
然而,上面这个函数虽然可以通过编译,但是,它实际运行时,不仅无法实现原来的意图。
甚至会导致数组越界死机。

就当这是个小编程题。其实不难很简单,但是一个相当不容易注意的地方。
我写这个函数一开始就是这样实现。
直到出错后,我离开电脑前去洗澡时,才想到问题出在哪。

继而我想到这个问题,其实我不止一次犯过,所以,我特地拿出来,
给大家解解闷。

最先给出正确答案的——
简要说明原因并且在这个函数基础上改成正确的,发帖即可。
版主私人转给100芯币。

八年一梦,洗尽铅华,重头再来


回复

使用道具 举报

787

TA的帖子

2

TA的资源

纯净的硅(初级)

Rank: 4

发表于 2017-12-10 22:27 | 显示全部楼层
占楼坐等第一个赚100芯币的小伙伴
物致DIY 欢迎你的加入~
QQ群:646461928 公众号:智物知心致成
小店

回复

使用道具 举报

7903

TA的帖子

54

TA的资源

裸片初长成(中级)

Rank: 11Rank: 11Rank: 11Rank: 11

荣誉会员勋章

 楼主| 发表于 2017-12-10 22:29 | 显示全部楼层
皈依 发表于 2017-12-10 22:27
占楼坐等第一个赚100芯币的小伙伴

八年一梦,洗尽铅华,重头再来


回复

使用道具 举报

1429

TA的帖子

1

TA的资源

纯净的硅(中级)

Rank: 5Rank: 5

发表于 2017-12-11 08:21 来自手机 | 显示全部楼层
坐等第一个改正的小伙伴

回复

使用道具 举报

91

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2017-12-11 08:36 | 显示全部楼层
楼主写的这个函数应该是在一个循环里不断读取文件图像数组吧。同时这个函数可以进行三种偏移:2,3,4字节。我猜发生数组越界是因为你的循环次数*4大于了你定义的数组长度了。也许应该在函数里加一个计数器记下你偏移多少了,防止发生越界。等待楼主的正确答案了。

点评

这也是对的。 崩掉只是一个与主题无关的小失误,我回忆了一下,大概就是这样子。  详情 回复 发表于 2017-12-11 23:08

回复

使用道具 举报

5714

TA的帖子

207

TA的资源

版主

Rank: 6Rank: 6

发表于 2017-12-11 08:41 | 显示全部楼层
uint8_t **pNext

点评

漂亮 就是这样。 100芯币归你了  详情 回复 发表于 2017-12-11 10:17
你的答案应该是正确的。  详情 回复 发表于 2017-12-11 10:15

EEWORLD开发板置换群:309018200,——电工们免费装β的天堂,上班摸鱼场,释放压力好地方!商家勿入!加群暗号:喵


回复

使用道具 举报

1775

TA的帖子

0

TA的资源

五彩晶圆(初级)

Rank: 7Rank: 7Rank: 7

发表于 2017-12-11 08:45 | 显示全部楼层
rgb16 = *(uint16_t *)pSrc;pSrc在定义的时候未半字对齐,访问出错?

回复

使用道具 举报

120

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2017-12-11 09:24 | 显示全部楼层
7楼正解,只改了形参的值

回复

使用道具 举报

171

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2017-12-11 10:15 | 显示全部楼层

你的答案应该是正确的。

回复

使用道具 举报

7903

TA的帖子

54

TA的资源

裸片初长成(中级)

Rank: 11Rank: 11Rank: 11Rank: 11

荣誉会员勋章

 楼主| 发表于 2017-12-11 10:17 | 显示全部楼层

漂亮
就是这样。
100芯币归你了

点评

形参该为uint8_t **pNext之后,你函数里使用的时候应该是用*pNext吧。我简化了你的函数功能写了个小例子,感觉应该也能实现你想要的结果,希望能讨论一下。 [mw_shl_code=c,true]int test(int *present,int *next)  详情 回复 发表于 2017-12-11 18:24

八年一梦,洗尽铅华,重头再来


回复

使用道具 举报

91

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2017-12-11 18:24 | 显示全部楼层
本帖最后由 hljjxzhla 于 2017-12-11 18:29 编辑
辛昕 发表于 2017-12-11 10:17
漂亮
就是这样。
100芯币归你了

形参该为uint8_t **pNext之后,你函数里使用的时候应该是用*pNext吧。我简化了你的函数功能写了个小例子,感觉应该也能实现你想要的结果,希望能讨论一下。
  • int test(int *present,int *next)
  • {
  •     *next+=4;
  •     return (*present+*(present+1)+*(present+2)+*(present+3));
  • }
  • int main(void)
  • {
  •     int i=0;
  •     int a1[3];
  •     const int a[12]={1,2,3,4,5,6,7,8,9,10,11,12};
  •     for(i=0;i<3;i++)
  •     {
  •         static int n=0;
  •         a1 [ i ]=test(&a[n],&n);
  •         printf("%d ",a1 [ i ]);
  •     }
  •     printf("\n");
  •     return 0;
  • }


点评

我不是很理解你这个实现的意义,想做什么?  详情 回复 发表于 2017-12-11 23:10

回复

使用道具 举报

7903

TA的帖子

54

TA的资源

裸片初长成(中级)

Rank: 11Rank: 11Rank: 11Rank: 11

荣誉会员勋章

 楼主| 发表于 2017-12-11 23:08 | 显示全部楼层
hljjxzhla 发表于 2017-12-11 08:36
楼主写的这个函数应该是在一个循环里不断读取文件图像数组吧。同时这个函数可以进行三种偏移:2,3,4字节 ...

这也是对的。
崩掉只是一个与主题无关的小失误,我回忆了一下,大概就是这样子。

八年一梦,洗尽铅华,重头再来


回复

使用道具 举报

7903

TA的帖子

54

TA的资源

裸片初长成(中级)

Rank: 11Rank: 11Rank: 11Rank: 11

荣誉会员勋章

 楼主| 发表于 2017-12-11 23:10 | 显示全部楼层
hljjxzhla 发表于 2017-12-11 18:24
形参该为uint8_t **pNext之后,你函数里使用的时候应该是用*pNext吧。我简化了你的函数功能写了个小例子 ...

我不是很理解你这个实现的意义,想做什么?

点评

因为我只是看见了你函数的代码,不知道你在函数外面是怎么用这个函数的,但是我猜你的程序应该是定义了两个指针变量(就是赋给你函数的两个形参的实参),我写的例子里直接取数组元素的地址和下标,是为了少定义一个  详情 回复 发表于 2017-12-12 08:55

八年一梦,洗尽铅华,重头再来


回复

使用道具 举报

91

TA的帖子

0

TA的资源

一粒金砂(中级)

Rank: 2

发表于 2017-12-12 08:55 | 显示全部楼层
辛昕 发表于 2017-12-11 23:10
我不是很理解你这个实现的意义,想做什么?

因为我只是看见了你函数的代码,不知道你在函数外面是怎么用这个函数的,但是我猜你的程序应该是定义了两个指针变量(就是赋给你函数的两个形参的实参),我写的例子里直接取数组元素的地址和下标,是为了少定义一个指针变量,省去调用函数之后把下一个地址的指针的值赋给当前地址指针。当然这是在C代码中是这样的,编译之后是简单了还是麻烦了就不知道了,水平有限看不懂汇编。总结一下我的想法就是想简化一下调用函数的代码,执行效率有没有增加就不知道了,当然我希望是能增加的,但是不知道怎么查看,有了解的高手可以指点一下。

点评

我很少看汇编。 又不是优化,没到那一步。  详情 回复 发表于 2017-12-12 10:00
哦,我是这样调用的。 get_pixel_color(pimg,&pimg); 在同一副图像上移动指针。 也就是移到下一个像素  详情 回复 发表于 2017-12-12 09:57

回复

使用道具 举报

7903

TA的帖子

54

TA的资源

裸片初长成(中级)

Rank: 11Rank: 11Rank: 11Rank: 11

荣誉会员勋章

 楼主| 发表于 2017-12-12 09:57 | 显示全部楼层
hljjxzhla 发表于 2017-12-12 08:55
因为我只是看见了你函数的代码,不知道你在函数外面是怎么用这个函数的,但是我猜你的程序应该是定义了两 ...

哦,我是这样调用的。

get_pixel_color(pimg,&pimg);

在同一副图像上移动指针。
也就是移到下一个像素

八年一梦,洗尽铅华,重头再来


回复

使用道具 举报

7903

TA的帖子

54

TA的资源

裸片初长成(中级)

Rank: 11Rank: 11Rank: 11Rank: 11

荣誉会员勋章

 楼主| 发表于 2017-12-12 10:00 | 显示全部楼层
hljjxzhla 发表于 2017-12-12 08:55
因为我只是看见了你函数的代码,不知道你在函数外面是怎么用这个函数的,但是我猜你的程序应该是定义了两 ...

我很少看汇编。
又不是优化,没到那一步。

八年一梦,洗尽铅华,重头再来


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

关闭

站长推荐上一条 1/6 下一条

  • 论坛活动 E手掌握

    扫码关注
    EEWORLD 官方微信

  • EE福利  唾手可得

    扫码关注
    EE福利 唾手可得

Archiver|手机版|小黑屋|电子工程世界 ( 京ICP证 060456 )

GMT+8, 2020-3-30 14:51 , Processed in 0.461925 second(s), 19 queries , Gzip On, MemCache On.

快速回复 返回顶部 返回列表