[color=Blue]特此说明,这点小技俩,仅供娱乐~[/color]
用位操作虽然可以实现加1,但似乎还得要 循环 来帮助。
最简单的事情莫过于让 [color=Red]编译器[/color] 来帮你实现 加 1 功能。
int _inc(int i)
{
char (*p)[2] = (char (*)[2])i;
return (int)&((*p)[1]);
}
int main()
{
printf("%d\n", _inc(100));
return 0;
}
----------------------------
当然可以更简单点,直接定义为宏:
#define _INC(x) (int)&((*((char(*)[2])x))[1])
[ 本帖最后由 mik 于 2006-11-20 23:20 编辑 ]
mik 回复于:2006-10-06 11:44:53
让它来得更通用一点:
#define _INC(x) ( \
{ typeof(x) _t = x; \
(int)&((*((char(*)[2])_t))[1]); })
emacsnw 回复于:2006-10-06 15:03:49
这个有点意思,a[1] 其实就是 *(a+1),不过这样也算不用加法吗?
mik 回复于:2006-10-06 15:07:57
呵呵,这些计算是在编译器层面上,编译代码时,编译器计算的,用户代码没有计算这一步
whyglinux 回复于:2006-10-06 15:48:23
1. 似乎不是实现的加 1 功能,而是加 2,因为 p 指向一个具有两个char元素的数组,所以 p 和 p+1 之间的距离是 2 字节。要实现加 1 应该使用长度为 1 的字符数组。
2. 按照你这个思路,_inc 函数的实现还可以更简单些,即使 p 指向单个字符,而不是一个字符数组:
char* p = (char*)i;
return (int)&p[1]; /* 相当于 return (int)(p + 1); */
3. 无论是你程序中的 &(*p[1]),还是上面的 &p[1],都需要 [color=red]计算[/color] p + 1 的值。由于 p 是一个指针变量,不是常量,所以这个计算只能在 [color=red]运行时[/color] 计算,不可能是一个 [color=red]编译时[/color] 概念。
mik 回复于:2006-10-06 16:09:13
引用:原帖由 whyglinux 于 2006-10-6 15:48 发表
1. 似乎不是实现的加 1 功能,而是加 2,因为 p 指向一个具有两个char元素的数组,所以 p 和 p+1 之间的距离是 2 字节。要实现加 1 应该使用长度为 1 的字符数组。
2. 按照你这个思路,_inc 函数的实现还可以更 ...
不要这么快下结论,你试过了吗?
1、
[color=Blue]p 是一个 char (*)[2] 型指针, 那么 *p 类型是: char [2][/color]
char c[2];
p = & c;
*p 就是 c,是一个 char[2];
(*p)[1] 相当于 c[1]
那么,c[1] 相对于 c[0],你说是不是 偏移了 1 byte 呀?
2、数组之间的偏移,不是编译时确定,难道是运行时确定 ??
[ 本帖最后由 mik 于 2006-10-7 22:28 编辑 ]
whyglinux 回复于:2006-10-06 16:19:59
>> (*p)[1] 相当于 c[1]
那你应该这样使用:(int)&((*p)[1]),而不是上面程序中的 (int)&(*p[1])。
mik 回复于:2006-10-06 16:34:19
引用:原帖由 whyglinux 于 2006-10-6 16:19 发表
>> (*p)[1] 相当于 c[1]
那你应该这样使用:(int)&((*p)[1]),而不是上面程序中的 (int)&(*p[1])。
那确实是手误, 宏定义 _INC(x) 里就正确了
lixuda 回复于:2006-10-06 16:36:36
自己测试下,whyglinux 是对啊。
也简单
没有必要用到(*p)[]吧。。
不过楼主厉害
awake 回复于:2006-10-06 16:52:36
引用:原帖由 mik 于 2006-10-6 16:09 发表
2、数组之间的偏移,不是编译时确定,难道是运行时确定 ??
可惜这个数组本身不是静态时确定的。
converse 回复于:2006-10-06 17:11:17
加精鼓励一下.
mik耍了一把编译器,把需要递增的值作为数组地址传给p,然后求以这个地址开始的下一个元素也就实现了加一的目的了,whyglinux那样可能更好看懂一些的,原来的做法麻烦了一点.
mik 回复于:2006-10-06 17:18:09
引用:原帖由 awake 于 2006-10-6 16:52 发表
可惜这个数组本身不是静态时确定的。
在 _inc(100);
这种情形下,是编译器计算的:
movl $101, 4(%esp)
movl $.LC0, (%esp)
call printf
在另一种情形下,还是要运行时确定:
int i = 0;
printf("%d\n", _inc(i));
movl -4(%ebp), %eax
incl %eax
movl %eax, 4(%esp)
movl $.LC0, (%esp)
call printf
awk就是awp加ak 回复于:2006-10-06 17:38:51
呵呵,地址加1 !
win_hate 回复于:2006-10-06 20:36:55
a c99 way
#include <stdio.h>
f(int n)
{
struct {
char a[n];
char b;
} *a;
printf ("%d\n", sizeof(*a));
}
main (int argc, char **argv)
{
f(atoi(argv[1]));
}
./a.out 1234
1235
[ 本帖最后由 win_hate 于 2006-10-6 20:38 编辑 ]
lovesaka 回复于:2006-10-06 23:17:34
引用:原帖由 win_hate 于 2006-10-6 20:36 发表
a c99 way
#include <stdio.h>
f(int n)
{
struct {
char a[n];
char b;
} *a;
printf ("%d\n", sizeof(*a));
}
main (int argc, char **argv)
{
f(atoi(argv[1 ...
这也给想出来了强!!!!
converse 回复于:2006-10-06 23:35:31
win_hate老大的代码如果考虑struct对齐的情况下呢?似乎还要加一个编译选项指明是严格的大小,而不是对齐之后的大小.
win_hate 回复于:2006-10-06 23:58:11
印象里会 gcc 按结构成员中最大的``成员对齐'' 要求来对齐的。这里全是 char,应该不会有问题。加个对齐指令会保险一些。
但我总是记不住对齐指令,要 google,所以就算了。
flw 回复于:2006-10-07 22:18:07
想不通为什么要 [2]
int _inc(int i)
{
char *p = (char *)i;
return (int)&(p[1]); // 我一般不写 &(p) 而直接写 p+i; 所以其实说白了还是 p+1
}
int main()
{
printf("%d\n", _inc(100));
return 0;
}
[ 本帖最后由 flw 于 2006-10-7 22:21 编辑 ]
mik 回复于:2006-10-07 22:27:32
脑子清醒了,是 OK
看,抛出了砖头,把玉给引来了 :)
yjh777 回复于:2006-10-08 10:45:06
都是吃饱了撑的,就一个加1操作需要不用+/++操作符,非要调个函数,还有竟然用数组指针的。
把你们的“钻研精神”用到其他地方行不?中国的软件精英们
[color=Yellow]printf("%d\n", _inc(100));
printf("%d\n", 100+1);
printf("%d\n", 101);[/color]
美丽人生 回复于:2006-10-08 11:11:58
如果是个常量
比如
const int p=100;
那么printf("%d",p+1);
好的编译器加一也是在编译时完成的
如果不是常量,那么,你再怎么耍巧,那都得运行时计算的
可以想象一下,被加数都还不知道,编译器又怎么呢做加法呢?
有句不是很雅的话,说的是脱了裤子打屁,多此一举
庄周蝴蝶 回复于:2006-10-08 11:41:52
template <int a>
int Inc()
{
return a + 1;
}
......
std::cout << Inc<100>() << endl;
飞灰橙 回复于:2006-10-08 13:50:35
的确多此一举,包括楼上用template的方式也是多此一举(template要求int a为常量)。
#define INC(x) ((x) + 1) 可以胜任楼上任何一种代码。
awake 回复于:2006-10-08 14:58:31
Hacker们喜欢这种方式啊。可以增加我们阅读代码的能力,而且有时候可以学到点东西。写这些代码当然不是为了优化+1。
ccjjhua 回复于:2006-10-08 17:02:20
不知道long long 型的+1用楼主的方式能不能做到。就是说被加数如果很大,超出了该进程允许访问的地址范围,这种方式+1 不会有问题吗?
ccjjhua 回复于:2006-10-08 17:05:25
我觉得铁定出问题!!!
awk就是awp加ak 回复于:2006-10-08 17:55:23
其实这不是为了效率,也不是考虑方便、易读性。主要是让思维开阔,看脑子会不会转弯 :em02:
awk就是awp加ak 回复于:2006-10-08 17:59:45
引用:原帖由 ccjjhua 于 2006-10-8 17:02 发表
不知道long long 型的+1用楼主的方式能不能做到。就是说被加数如果很大,超出了该进程允许访问的地址范围,这种方式+1 不会有问题吗?
会
langue 回复于:2006-10-08 18:11:03
引用:原帖由 yjh777 于 2006-10-8 10:45 发表
都是吃饱了撑的,就一个加1操作需要不用+/++操作符,非要调个函数,还有竟然用数组指针的。
把你们的“钻研精神”用到其他地方行不?中国的软件精英们
[_color=Yellow]printf("%d\n", _inc(100));
...
开发智力么,总要有思想的摩擦。
awk就是awp加ak 回复于:2006-10-08 20:58:17
对哑,我也不知道为什么要[2],正常思维应该是酱紫滴吧:
#define INCR(x) (int)&((char *)x)[1]
chenwandong 回复于:2006-10-09 11:07:20
让人一眼就看懂的代码才是好代码
chenwandong 回复于:2006-10-09 11:10:34
殊途同归,当时不应该提倡
LinuxServer 回复于:2006-10-10 02:04:44
没劲。
jruv 回复于:2006-11-20 22:05:38
真不知这样做有何意义,就像去研究“回”字有几种写法一样,其实这种方法比x+=1只是真加了复杂度,并且翻成最后的机器指令后,可能要去访问物理内存, 反而降低了运行效率,并且lz所谓的这样做是编译器计算的,不是用户代码计算的结论不知从何而来的。指针偏移也要进行计算的。
mik 回复于:2006-11-20 22:57:59
晕~~ 忍不住想说几句,
明眼的人一看就知道:这点小技俩,只不过是写了玩玩,仅供娱乐娱乐
不知道怎么会有这么多人认真批判这点简陋的写作。
chjcpu1 回复于:2006-11-21 09:00:44
lz不是在发帖时说明只供娱乐了吗,如果这也批评,恐怕将来没人敢发帖了。
fibbery 回复于:2006-11-21 11:34:20
批判前考虑一下你要批判的是什么!
zhirong_xu 回复于:2007-01-18 09:39:07
用来研究还是不错的啊,从小的地方着手分析是可以做大文章的,鼓励一下!
nickzou 回复于:2007-01-18 12:11:21
就算编译器被骗过了,CPU呢,CPU执行++的指令要比这一串快多了。
newzy 回复于:2007-01-18 13:59:32
把程序中添加一句, 用 gcc 生成汇编对比分析下:
int _inc(int i)
{
char (*p)[2] = (char (*)[2])i;
return (int)&((*p)[1]);
}
生成的汇编代码:
...
movl %eax, -4(%ebp)
movl $0, -8(%ebp) // 这两句, 初始化 i=0
leal -8(%ebp), %eax
incl (%eax) // 这两句, 实现 i++
movl -4(%ebp), %eax
incl %eax // 这两句, 实现 (int)&((*p)[1]);
leave
ret
[color=Red]对比不难发现, 它们被编译成相同的汇编语句, 即操作方法都是一样的.
(int)&((*p)[1]) 并未精简任何操作.[/color]
所谓, "编译器运算" 到头来还是运行时计算的.
不过, 还是很恭喜楼主能有这个创新的想法.
newzy 回复于:2007-01-19 17:34:19
在 arm 和 ppc 测试得到类似结果.
okyzx 回复于:2007-02-24 11:05:27
lz想法很有创意
ddvv 回复于:2007-04-16 20:05:06
LZ想法有意思~
顶一下
|