fstrict-aliasing
承如“optimization blocks”文中所述,由于相同的指针可能指向相关的内存区,因此编译器将不做过分的优化……
特意搜了下编译器在不同的优化等级下都有哪些默认优化,因此有了此记录(比较长,尽管只讲述了fstrict-aliasing ……)
下述内容引述自:http://dbp-consulting.com/tutorials/StrictAliasing.html
http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html
http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule
gcc -v
gcc version 4.4. (Ubuntu/Linaro 4.4.-14ubuntu5)
arm-none-arm-none-linux-gnueabi-gcc -v
gcc version 4.3. (Sourcery G++ Lite 2008q3-)
首先看下在不同优化等级下gcc的默认优化参数(此处重点关注fstrict-aliasing 在O0(默认优化等级),O2(一般使用时的优化等级)
下的开启情况)
gcc -Q -O0 --help=optimizers
-fstrict-aliasing [disabled]
arm-gcc -Q -O0 --help=optimizers
-fstrict-aliasing [disabled] gcc -Q -O2 --help=optimzers
-fstrict-aliasing [enabled] arm-gcc -Q -O0 --help=optimizers
-fstrict-aliasing [enabled] 在O0等级下,无论是否显式的添加fstrict-aliasing,fstrict-aliasing都被禁用
在O2等级下,默认开启fstrict-aliasing,可以显式的添加fno-strict-aliasing以禁用fastrict-aliasing
先看下程序和运行结果(分arm与x86)
示例file1.c、
unsigned int swap_words(unsigned int arg)
{
unsigned short int* const sp = (unsigned short int*)&arg;
unsigned short int lo = sp[];
unsigned short int hi = sp[]; sp[] = hi;
sp[] = lo;
return arg;
} int main(void)
{
int x = 0x12345678;
x = swap_words(x);
printf("%08x\n", x);
return ;
}
gcc -m32 file1.c -o mytest :
swap_words:
pushl %ebp
movl %esp, %ebp
subl $, %esp
leal (%ebp), %eax
movl %eax, -(%ebp) ;sp
movl -(%ebp), %eax
movzwl (%eax), %eax ;arg
movw %ax, -(%ebp) ;arg(l)
movl -(%ebp), %eax
addl $, %eax
movzwl (%eax), %eax
movw %ax, -(%ebp) ;arg(h)
movl -(%ebp), %eax
movzwl -(%ebp), %edx
movw %dx, (%eax) ;arg: arg(h)arg(h)
movl -(%ebp), %eax
leal (%eax), %edx
movzwl -(%ebp), %eax
movw %ax, (%edx) ;arg: arg(l)arg(h)
movl (%ebp), %eax
leave
ret gcc -m32 -O2 file1.c -o mytest:
swap_words:
pushl %ebp
movl %esp, %ebp
movzwl (%ebp), %eax ;arg
movzwl (%ebp), %edx ;arg(h)
movw %ax, (%ebp) ;arg(l)arg(l)
movl (%ebp), %eax ;arg(l)arg(l)
movw %dx, (%ebp) ;arg(l)arg(h)
popl %ebp
ret
尽管在swap_words中正确修改了调用者的栈上的值,但是在main中更本就不调用swap_words,
eax的值与我们的原意也不同 gcc -m32 -fno-strict-aliasing -O2 file1.c -o mytest :
swap_words:
pushl %ebp
movl %esp, %ebp
movzwl (%ebp), %eax
movzwl (%ebp), %edx
movw %ax, (%ebp)
movw %dx, (%ebp)
movl (%ebp), %eax
popl %ebp
ret
正确计算了返回值 arm-gcc file1.c -o mytest:
swap_words:
str fp, [sp, #-]!
add fp, sp, #
sub sp, sp, #
str r0, [fp, #-] ;arg
sub r3, fp, #
str r3, [fp, #-] ;sp
ldr r3, [fp, #-]
ldrh r3, [r3, #]
strh r3, [fp, #-] ;arg[l]
ldr r3, [fp, #-]
add r3, r3, #
ldrh r3, [r3, #]
strh r3, [fp, #-] ;arg[h]
ldr r2, [fp, #-]
ldrh r3, [fp, #-]
strh r3, [r2, #] ;arg:arg(l)arg(l)
ldr r3, [fp, #-]
add r2, r3, #
ldrh r3, [fp, #-]
strh r3, [r2, #] ;arg:arg(l)arg(h)
ldr r3, [fp, #-]
mov r0, r3
add sp, fp, #
ldmfd sp!, {fp}
bx lr arm-gcc file1.c -O2 -o mytest:
swap_words:
sub sp, sp, #
str r0, [sp, #]
add sp, sp, #
bx lr
因为实施了fstrict-aliasing优化,因此,swap_words中做了非常大的优化,以至于不做任何计算,直接返回r0 arm-gcc file1.c -O2 -fno-strict-aliasing -o mytest:
swap_words:
sub sp, sp, #
str r0, [sp, #]
ldrh r3, [sp, #]
ldrh r2, [sp, #]
strh r3, [sp, #] @ movhi
ldr r0, [sp, #]
add sp, sp, #
bx lr
当我们加上fno-strict-aliasing参数时,swap_words正确计算了返回值,尽管file1.c违背了strict-aliasing规则
先以如上实例阐述一些概念
通过以上的对比分析,我们看到当加入有效的fstrict-aliasing时,编译器做了大量的优化,由于我们的程序违背了strict-aliasing规则,但是这是由于我们程序员自身造成的,与编译器本身的优化无关。那么,如果我们既想编译器做出大量的优化,同时对于违背strict-aliasing规则的程序发出警告,该怎么办了????加入-Wstrict-aliasing编译参.
例如:
arm-none-linux-gnueabi-gcc -O2 -Wstrict-aliasing file1.c -o mytest,会有如下输出:
warning: likely type-punning may break strict-aliasing rules
当我们遇到这类输出时一定要注意(要么我们就使用fno-strict-aliasing参数,例如内核,使用fno-strict-aliasing后只是在某些部分没有做优化,但不会导致编译出的程序与我们的原意相反)
关于c/c++中strict aliasing记录(重点是c)
aliasing is when more than one lvalue refers to the same memory location.
strict aliasing is an assumption, made by the C (or C++) compiler, that dereferencing pointers to objects of different types will never refer to the same memory location. Enable this option in GCC with -fstrict-aliasing flag. Be sure that all code can safely run with this rule enabled. Enable strict aliasing related warnings with -Wstrict-aliasing, but do not expect to be warned in all cases. In order to discover aliasing problems as quickly as possible, -fstrict-aliasing should always be included in the compilation flags for GCC. Otherwise problems may only be visible at the highest optimization levels where it is the most difficult to debug(如果我们使用参数-fno-strict-warning,有些优化将不被实施,而且对于一些潜在的转换错误,编译器不会报出)
the compiler writers know what the strict aliasing rules are for. They are written to let compiler writers known when they can safely assume that a change made through one variable won't affect the value of another variable.
Conversely, the compiler must act very conservatively to access the memory when the compiler cannot assume that two object are not aliased.
在编译选项中加入-fstrict-aliasing的优势在于向编译器说明不同类型的lvalue将指向不相关的内存区域,编译器可以做大量的优化。在编译内核的编译选项CFLAGS中,加入了-fno-strict-aliasing,向编译器表明不同类型的lvalue可能指向相关的内存区域,因此编译器不会做出一些极端的优化而造成不安全(内核编译中优化选项为-O2, -O2优化时默认是-fstrict-aliasing,因此需要显
式的指出编译参数是-fno-strict-aliasing)
1、编译器设计者知道一些左值量会指向相关的内存区域,因此不会做出一些不安全的优化(即使编译选项显式的指出为-fstrict-aliasing,也不会做极端的优化,因为这些类型的左值量有可能指向相关的内存的区域);
2、当我们给编译器加入-fstrict-aliasing选项时,就意味着使用编译器的程序员清晰地向编译器表明:不同类型的左值量不会指向相关的内存区域,编译器可以做出大量的优化,由此产生的不良后果由程序员负责,而非编译器设计者负责;
当使用-fstrict-aliasing时,有如下基本示例表明不同类型的左值将不会指向相关的内存区域:
(1)pointers to different built in types do not alias
(2)pointers to aggregate or union types with differing tags do not alias
(3)pointers to aggregate or union types which differ only by name may alias
在 1 中提到:一些左值量可能指向相关的内存区域,那么都有哪些可能呢?,如下:
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
(1)— a type compatible with the effective type of the object,
(2)— a qualified version of a type compatible with the effective type of the object,
(3)— a type that is the signed or unsigned type corresponding to the effective type of the object,
(4)— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
(5)— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
(6)— a character type.
即:
(1)Things that are compatible types or differ only by the addition of any combination of signed, unsigned, or volatile. For most purposes compatible type just means the same type. If you want more details you can read the specs. (Example: If you get a pointer to long, and a pointer to const unsigned long they could point to the same thing.)
(2)An aggregate (struct or class) or union type can alias types contained inside them. (Example: If a function gets passed a pointer to an int, and a pointer to a struct or union containing an int, or possibly containing another struct or union containing an int, or containing...ad infinitum, it's possible that the int* points to an int contained inside the struct or union pointed at by the other pointer.)
(3)A character type. A char*, signed char*, or unsigned char* is specifically allowed by the specs to point to anything. That means it can alias anything in memory.
(4)For C++ only, a possibly CV (const and/or volatile) qualified base class type of a dynamic type can alias the child type. (Example: if class dog has class animal for a base class, pointers or references to class dog and class animal can alias.)
(5)Any lvalue has to be assumed to possibly alias to another lvalue if these rules say that they can alias. An aliasing issue is just as likely to come up with values passed by reference as it is with values passed as pointer to values. Additionally any combination of pointers and references have a possibility of aliasing.
看完上面的内容,我们来测试下:
示例file2.c, 此实例验证了上述的第二点() typedef struct
{
unsigned int a;
unsigned int b;
unsigned int c;
}Sample; void test(unsigned int* values,
Sample* uniform,
unsigned int count)
{
unsigned int i = ;
for(i = ; i < count; i++)
{
values[i] = (unsigned int)uniform->b;
}
} gcc file2.c -o mytest test:
pushl %ebp
movl %esp, %ebp
subl $, %esp
movl $, -(%ebp) ;i
movl $, -(%ebp)
jmp .L2
.L3:
movl -(%ebp), %eax ;i
sall $, %eax
addl (%ebp), %eax ;values + * i
movl -(%ebp), %edx ;i
sall $, %edx
addl (%ebp), %edx ;values + * i
movl (%edx), %ecx
movl (%ebp), %edx
movl (%edx), %edx ;每次重新取得uniform->b
leal (%ecx,%edx), %edx
movl %edx, (%eax)
addl $, -(%ebp)
.L2:
movl -(%ebp), %eax ;i
cmpl (%ebp), %eax ;i-count
jb .L3
leave
ret gcc file2.c -O2 -o mytest
test:
pushl %ebp
movl %esp, %ebp
pushl %esi
movl (%ebp), %edx
pushl %ebx
movl (%ebp), %ebx
movl (%ebp), %esi
testl %ebx, %ebx
je .L4
xorl %eax, %eax
.L3:
movl (%esi), %ecx ;每次重新取得uniform->b
addl %ecx, (%edx,%eax,)
addl $, %eax
cmpl %eax, %ebx
ja .L3
.L4:
popl %ebx
popl %esi
popl %ebp
ret arm-gcc file2.c -O2 -o mytest:
test:
stmfd sp!, {r4, r5}
subs r5, r2, #
mov r4, r1
beq .L4
mov r1, #
mov ip, r1
.L3:
ldr r3, [r0, ip]
ldr r2, [r4, #] ;每次重新取得uniform->b
add r1, r1, #
add r3, r3, r2
cmp r5, r1
str r3, [r0, ip]
add ip, ip, #
bhi .L3
.L4:
ldmfd sp!, {r4, r5}
bx lr
=========================================================================
The most commonly accepted method of converting one type of object to another is by
using a union type;
实例file3.c,
typedef union
{
unsigned int u32;
unsigned short int u16[];
}U32; unsigned int swap_words(unsigned int arg)
{
U32 in;
unsigned short int lo;
unsigned short int hi; in.u32 = arg;
lo = in.u16[];
hi = in.u16[]; in.u16[] = hi;
in.u16[] = lo; return in.u32;
} gcc file3.c -S -o file3.s
swap_words:
pushl %ebp
movl %esp, %ebp
subl $, %esp
movl (%ebp), %eax ;arg
movl %eax, -(%ebp)
movzwl -(%ebp), %eax ;arg
movw %ax, -(%ebp) ;arg(l)
movzwl -(%ebp), %eax ;arg(h)
movw %ax, -(%ebp) ;arg(h)
movzwl -(%ebp), %eax
movw %ax, -(%ebp) ;arg(h)arg(h)
movzwl -(%ebp), %eax
movw %ax, -(%ebp) ;arg(l)arg(h)
movl -(%ebp), %eax
leave
ret gcc file3.c -O2 -S -o file3.s
既做了优化,有没有错误,验证上述第二点
swap_words:
pushl %ebp
movl %esp, %ebp
movl (%ebp), %eax
popl %ebp
roll $, %eax
ret arm-gcc file3.c -S -o file3.s
swap_words:
str fp, [sp, #-]!
add fp, sp, #
sub sp, sp, #
str r0, [fp, #-] ;arg
ldr r3, [fp, #-] ;arg
str r3, [fp, #-] ;arg
ldrh r3, [fp, #-] ;arg(l)
strh r3, [fp, #-] ;arg(l)
ldrh r3, [fp, #-] ;arg(h)
strh r3, [fp, #-] ;arg(h)
ldrh r3, [fp, #-] ;arg(h)
strh r3, [fp, #-] ;arg(h)arg(h)
ldrh r3, [fp, #-] ;arg(l)
strh r3, [fp, #-] ;arg(l)arg(h)
ldr r3, [fp, #-] ;arg(l)arg(h)
mov r0, r3
add sp, fp, #
ldmfd sp!, {fp}
bx lr arm-gcc file3.c -O2 -S -o file3.s
//直接用了循环右移来实现,优化啊!!!
swap_words:
mov r0, r0, ror #
bx lr
=============================================================================
Casting proper may be done between a pointer to a type and a pointer to an aggregate or union type which contains a member of a compatible type; 实例file4.c
unsigned int swap_words(unsigned int arg)
{
U32* in = (U32*)&arg;
unsigned short int lo = in->u16[];
unsigned short int hi = in->u16[]; in->u16[] = hi;
in->u16[] = lo;
return (in->u32);
} gcc -S -o file4.s
swap_words:
pushl %ebp
movl %esp, %ebp
subl $, %esp
leal (%ebp), %eax
movl %eax, -(%ebp) ;in
movl -(%ebp), %eax
movzwl (%eax), %eax ;arg
movw %ax, -(%ebp) ;arg(l)
movl -(%ebp), %eax
movzwl (%eax), %eax ;arg(h)
movw %ax, -(%ebp) ;arg(h)
movl -(%ebp), %eax ;in
movzwl -(%ebp), %edx ;arg(h)
movw %dx, (%eax) ;arg(h)arg(h)
movl -(%ebp), %eax ;in
movzwl -(%ebp), %edx ;arg(l)
movw %dx, (%eax) ;arg(l)arg(h)
movl -(%ebp), %eax
movl (%eax), %eax ;arg(l)arg(h)
leave
ret gcc -S -O2 -o file4.s
swap_words:
pushl %ebp
movl %esp, %ebp
movzwl (%ebp), %eax
movzwl (%ebp), %edx
movw %ax, (%ebp)
movw %dx, (%ebp)
movl (%ebp), %eax
popl %ebp
ret arm-gcc file4.c -S -o file4.s
swap_words:
str fp, [sp, #-]!
add fp, sp, #
sub sp, sp, #
str r0, [fp, #-] ;arg
sub r3, fp, #
str r3, [fp, #-] ;in
ldr r3, [fp, #-]
ldrh r3, [r3, #] ;arg(l)
strh r3, [fp, #-] ;arg(l)
ldr r3, [fp, #-]
ldrh r3, [r3, #] ;arg(h)
strh r3, [fp, #-] ;arg(h)
ldr r2, [fp, #-]
ldrh r3, [fp, #-] ;arg(h)
strh r3, [r2, #] ;arg(h)arg(h)
ldr r2, [fp, #-]
ldrh r3, [fp, #-] ;arg(l)
strh r3, [r2, #] ;arg(l)arg(h)
ldr r3, [fp, #-]
ldr r3, [r3, #] ;arg(l)arg(h)
mov r0, r3
add sp, fp, #
ldmfd sp!, {fp}
bx lr arm-gcc file4.c -O2 -S -o file4.s
swap_words:
sub sp, sp, #
add r3, sp, #
str r0, [r3, #-]! ;arg
ldrh r2, [sp, #] ;arg(l)
ldrh r1, [r3, #] ;arg(h)
strh r1, [sp, #] ;arg(h)arg(h)
strh r2, [r3, #] ;arg(l)arg(h)
ldr r0, [sp, #] ;arg(l)arg(h)
add sp, sp, #
bx lr
============================================================================
实例file5.c(因为对于部分结构来说空间较大,用指针传递)
void swap_words(unsigned int *arg)
{
U32* combined = (U32*)arg;
unsigned int start = combined->u32;
unsigned int lo = start >> ;
unsigned int hi = start << ; unsigned int final = lo | hi; combined->u32 = final;
} gcc file5.c -S -o file5.s
swap_words:
pushl %ebp
movl %esp, %ebp
subl $, %esp
movl (%ebp), %eax
movl %eax, -(%ebp) ;combined
movl -(%ebp), %eax
movl (%eax), %eax
movl %eax, -(%ebp) ;start
movl -(%ebp), %eax
shrl $, %eax
movl %eax, -(%ebp) ;lo
movl -(%ebp), %eax
sall $, %eax
movl %eax, -(%ebp) ;hi
movl -(%ebp), %eax ;hi
movl -(%ebp), %edx ;lo
orl %edx, %eax ;hi | lo
movl %eax, -(%ebp) ;final
movl -(%ebp), %eax
movl -(%ebp), %edx
movl %edx, (%eax) ;combined->u32 = final
leave
ret gcc file5.c -O2 -S -o file5.s
swap_words:
pushl %ebp
movl %esp, %ebp
movl (%ebp), %eax
roll $, (%eax)
popl %ebp
ret arm-gcc file5.c -S -o file5.s
swap_words:
str fp, [sp, #-]!
add fp, sp, #
sub sp, sp, #
str r0, [fp, #-]
ldr r3, [fp, #-]
str r3, [fp, #-] ;combined
ldr r3, [fp, #-]
ldr r3, [r3, #]
str r3, [fp, #-] ;start
ldr r3, [fp, #-]
mov r3, r3, lsr #
str r3, [fp, #-] ;lo
ldr r3, [fp, #-]
mov r3, r3, asl #
str r3, [fp, #-] ;hi
ldr r2, [fp, #-]
ldr r3, [fp, #-]
orr r3, r2, r3
str r3, [fp, #-] ;final
ldr r2, [fp, #-]
ldr r3, [fp, #-]
str r3, [r2, #] ;combined->u32 = final
add sp, fp, #
ldmfd sp!, {fp}
bx lr arm-gcc file5.c -O2 -S -o file5.s
//注意与file4.c中使用同样编译选项的编译结果的区别(file5.c比较高效,
对于小结构体而言,file3.c同样的编译选项更高效)
swap_words:
ldr r3, [r0, #]
mov r3, r3, ror #
str r3, [r0, #]
bx lr
==============================================================================
实例file6.c
Occasionally a programmer may encounter the following INVALID method for creating an alias with
a pointer of a different type: (注意上述()的适用范围不包括如下情形)
typedef union
{
unsigned short int* sp;
unsigned int* wp;
}U32P; unsigned int swap_words(unsigned int arg)
{
U32P in = {.wp = &arg};
const unsigned int hi = in.sp[];
const unsigned int lo = in.sp[]; in.sp[] = hi;
in.sp[] = lo; return arg;
} int main(void)
{
int x = 0x12345678;
int y = swap_words(x); printf("x:%08x y:%08x\n", x, y);
return ;
} gcc file6.c -o mytest:
swap_words:
pushl %ebp
movl %esp, %ebp
subl $, %esp
movl $, -(%ebp)
leal (%ebp), %eax
movl %eax, -(%ebp) ;wp
movl -(%ebp), %eax ;sp
addl $, %eax ;sp +
movzwl (%eax), %eax
movzwl %ax, %eax
movl %eax, -(%ebp) ;hi
movl -(%ebp), %eax
movzwl (%eax), %eax
movzwl %ax, %eax
movl %eax, -(%ebp) ;lo
movl -(%ebp), %eax
movl -(%ebp), %edx ;hi
movw %dx, (%eax) ;sp[] = hi
movl -(%ebp), %eax
leal (%eax), %edx
movl -(%ebp), %eax
movw %ax, (%edx) ;sp[] = lo
movl (%ebp), %eax ;lo:hi
leave
ret gcc file6.c -O2 -o mytest:
swap_words:
pushl %ebp
movl %esp, %ebp
movzwl (%ebp), %eax ;arg(l)
movzwl (%ebp), %edx ;arg(h)
movw %ax, (%ebp) ;arg(l)arg(l)
movl (%ebp), %eax ;arg(l)arg(l)
movw %dx, (%ebp) ;arg(l)arg(h)
popl %ebp
ret
尽管在swap_words中正确修改了调用者的栈上的值,但是在main中更本就不调用用swap_words
eax的值与我们的原意也不同 arm-gcc -o mytest :
swap_words:
str fp, [sp, #-]!
add fp, sp, #
sub sp, sp, #
str r0, [fp, #-] ;arg
mov r3, #
str r3, [fp, #-]
sub r3, fp, #
str r3, [fp, #-] ;wp
ldr r3, [fp, #-] ;sp
add r3, r3, #
ldrh r3, [r3, #]
str r3, [fp, #-] ;hi
ldr r3, [fp, #-] ;sp
ldrh r3, [r3, #]
str r3, [fp, #-] ;lo
ldr r2, [fp, #-] ;sp
ldr r3, [fp, #-] ;hi
mov r3, r3, asl #
mov r3, r3, lsr #
strh r3, [r2, #] ;arg(h)arg(h)
ldr r3, [fp, #-]
add r2, r3, #
ldr r3, [fp, #-]
mov r3, r3, asl #
mov r3, r3, lsr #
strh r3, [r2, #] ;arg(l)arg(h)
ldr r3, [fp, #-] ;arg(l)arg(h)
mov r0, r3
add sp, fp, #
ldmfd sp!, {fp}
bx lr arm-gcc -O2 -o mytest :
swap_words:
sub sp, sp, #
str r0, [sp, #]
add sp, sp, #
bx lr
因为实施了fstrict-aliasing优化,因此,swap_words中做了非常大的优化,以至于不做任何计算,直接返回r0 The problem with this method is although U32P does in fact say that sp is an alias for wp,
it does not say anything about the relationship between the values pointed to by sp and wp.
This differs in a critical way from the experiment in "file3.c & file4.c" which both define
aliases for the values being pointed to, not the pointers themselves.
=========================================================================
对于上述情形(3)的说明:
It is always presumed that a char* may refer to an alias of any object. It is therefore quite safe, if perhaps a bit unoptimal (for architecture with wide loads and stores) to cast any pointer of any type to a char* type. (即由于char*可能与任何类型的左值相同,因此编译器不会做过分的优化,无论是否指定fstrict-aliasing参数选项都在安全范围内做优化,使用char*带来的问题是可能存在多次load/store,可能会降低性能)
如下代码,使用-O0, 与-O2编译出的程序执行效果相同,都可以交换字内的字节序(注意情形())
unsigned int swap_words(unsigned int arg)
{
char* const cp = (char*)arg;
const char c0 = cp[];
const char c1 = cp[];
const char c2 = cp[];
const char c3 = cp[]; cp[] = c2;
cp[] = c3;
cp[] = c0;
cp[] = c1; return arg;
}
对于情形(3),相反的情况则不一定成立,即:
The converse is not true. Casting a char* to a pointer of any type other than a char* and dereferencing it is usually in volation of the strict aliasing rule.
大叔来了,回去洗澡,有点乱,再看看
fstrict-aliasing的更多相关文章
- Java是如何处理别名(aliasing)的
什么是Java别名(aliasing) 别名意味着有多个别名指向同一个位置,且这些别名有不同的类型. 在下面的代码例子中,a和b是两个不同的名字,有不同的类型A和B,B继承A B[] b = new ...
- Aliasing 走样
Computer Science An Overview _J. Glenn Brookshear _11th Edition Have you ever noticed the weird &quo ...
- Java 别名(Aliasing)
别名 (Aliasing) 别名,顾名思义,是有别于现在名字的另一个名字,但指的是不是同一个人或事物呢?比如,你上学的时候同学有没有给你起什么外号?如果有的话,你的名字和同学给你起的外号是不是都指的是 ...
- 从花式swap引出的pointer aliasing问题
上次,一个同学问我,你知不知道可以不用引入中间变量就可以实现swap? 我说,我知道,可以用加减法或者异或实现,像是这样 void mySwap(int &x,int &y) { x= ...
- Specular Aliasing与Specular Leaking
最近做高质量实时HDR PBR渲染中碰到了2个关键问题,若干思考如下: 问题1: 极高的动态范围HDR+高级BRDF+相对较低的采样率(比方说不考虑子像素的原始分辨率),在这3项因素的综合作用下,Sp ...
- PHP使用命名空间:别名/导入(Aliasing/Importing)
1.导入,就是使用use操作符 2.在一个类中导入了另一个类之后,当前的命名空间仍然是当前类的命名空间 3.注意对命名空间中的名称(包含命名空间分隔符的完全限定名称如 Foo\Bar以及相对的不包含命 ...
- Aliasing input/output properties
angular @input alias别名的使用. https://angular.io/guide/template-syntax#aliasing-io https://stackoverflo ...
- (转) [it-ebooks]电子书列表
[it-ebooks]电子书列表 [2014]: Learning Objective-C by Developing iPhone Games || Leverage Xcode and Obj ...
- ASP.NET图形验证码的生成
效果: 调用方法: int[] r = QAPI.VerifImage.RandomList();//取得随机数种子列 );//产生验证码字符 pictureBox1.Image = QAPI.Ver ...
- Unity3D游戏开发初探—1.跨平台的游戏引擎让.NET程序员新生
一.Unity3D平台简介 Unity是由Unity Technologies开发的一个让轻松创建诸如三维视频游戏.建筑可视化.实时三维动画等类型互动内容的多平台的综合型游戏开发工具,是一个全面整合的 ...
随机推荐
- SyntaxError: missing ] after element list 火狐问题
关于火狐运行var obj = eval('(' + data + ')');时 报SyntaxError: missing ] after element list错误,Chrome和IE正常 情形 ...
- RhinoMock学习-Stub方法
// Arrange var stub = MockRepository.GenerateStub<IDemo>(); stub.Stub(x => x.StringArgStrin ...
- CentOS7添加logstash5启动脚本
默认情况使用rpm包安装完logstash之后没有启动脚本,这一点我觉得算是开发不够彻底.官网给了一个脚本,需要根据不同的系统版本生成对应的启动脚本,而且官网没有给明使用方法,对于新用户来说算是个坑, ...
- 【DSP】CCS中.dat文件的格式
转自“http://e2e.ti.com/support/development_tools/code_composer_studio/f/81/t/168292” Here is the infor ...
- 每日英语:Skull Shakes Up Human Family Tree
A newly discovered 1.8 million-year-old skull offers evidence that humanity's early ancestors emerge ...
- [转]Understanding Weak References
原文地址:https://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html 推荐另一篇文章:http://www ...
- LeetCode: Count and Say 解题报告
Count and Say The count-and-say sequence is the sequence of integers beginning as follows:1, 11, 21, ...
- python 读取文件、并以十六进制的方式写入到新文件
#!/usr/bin/env python infile = file("in.mp3","rb") outfile = file("out.txt& ...
- 在java web中获取该项目的根路径
在jsp页面中: <% String path = application.getRealPath("").replace("\\","\\\\ ...
- 【WPF】ScrollViewer无法滚动的问题
还需要给ScrollViewer注册一个鼠标滚轮事件! XAML: <ScrollViewer x:Name="scrollViewer" Width="950&q ...