是sizeof(size_t)==8说,该平台是64位的相同呢?相反,sizeof(size_t)==4相当于说平台是32位?
更重要的是,在所有情况下,此测试是否安全可靠,并牢记操作系统和编译器的可移植性?是否有一些奇怪的角落案例,包括size_t可能丢失的潜在情况?
我有点担心size_t可能只能保证C99环境.
使用gcc或时clang,启用多个警告通常是一个好主意,第一批警告通常由 提供-Wall。该批次相当大,并且包含特定警告-Wunused-function。
现在,-Wunused-function可用于检测static不再调用的函数,这意味着它们无用,因此最好从源代码中删除。当实行“零警告”政策时,它不再是“可取的”,而是彻头彻尾的强制性。
出于性能原因,某些函数可能会直接定义到头文件中*.h,以便可以在编译时内联它们(忽略任何类型的 LTO 魔法)。此类函数通常声明和定义为static inline。static inline在过去,此类函数可能会被定义为宏,但只要适用,最好将它们改为函数(没有有趣的类型问题)。
好的,出于性能原因,现在我们将一堆函数直接定义到头文件中。包含此类头文件的单元没有义务使用其所有声明的符号。因此,static inline头文件中定义的函数可能不会被调用。
对于gcc,那很好。gcc会标记一个未使用的static函数,但不会标记一个inline static。但clang结果是不同的:如果单个单元不调用static inline标头中声明的函数,则会触发警告。-Wunused-function而且不需要很多标志就可以到达那里:-Wall就足够了。
解决方法是引入特定于编译器的扩展,例如__attribute__((unused)),它向编译器明确声明标头中定义的函数不一定会被其所有单元调用。好的,但是现在,曾经干净的代码C99包含某种形式的特定编译器扩展,增加了可移植性和维护的重量。
因此,问题更多地是关于这种选择的逻辑:为什么当未调用标头中定义的函数clang时 selects 会触发警告?static inline在什么情况下这是一个好主意?
什么clang建议涵盖头文件中定义的内联函数的相对常见情况,而不要求使用编译器扩展?
编辑:经过进一步调查,问题似乎不正确。使用应用选定列表编译标志(等)的 linter在编辑器(VSCode)中触发警告。但是,当实际使用完全相同的标志列表编译源代码时,不会出现“未使用的函数”警告。clang-Wallclang
到目前为止,编辑器中可见的结果与编译时找到的结果完全相同。这是我第一次目睹差异。
因此,问题似乎与 linter 用于clang生成警告列表的方式有关。这是一个更加复杂和具体的问题。
请注意 …
可能重复:
为什么切换/案例而不是If/Else如果?
我想了解switch() case:C语句如何被编译器转换为汇编程序操作码.
具体来说,我有兴趣了解一系列if then else分支的差异.
性能比较是主要议题.
关于词汇表的几句话:我熟悉汇编程序的主要概念,很久以前就用汇编程序编写了更简单的系统,但现在肯定没有关于x86汇编程序语义的东西.因此直接汇编器输出将没有用.伪代码是首选.
我有一个非常热的指令循环,需要在32字节边界上正确对齐,以最大化英特尔的指令提取器效率.
这个问题特定于英特尔不太老的CPU系列(从Sandy Bridge开始).如果未能正确对齐循环开始,则速度损失高达20%,这绝对太明显了.这个问题非常罕见,需要一组高度优化的指令才能使指令获取器成为瓶颈.但幸运的是,这不是一个独特的案例.这是一篇很好的文章,详细解释了如何检测到这样的问题.
问题是,gcc还是clang会关心这个指令循环.它使得编译这个代码变成了一个产生随机结果的噩梦,这取决于热循环如何"好"地偶然排列.这也意味着修改完全不相关的功能仍然会极大地影响热循环的性能.
已经尝试了几个编译器标志,它们都没有给出令人满意的结果.
[编辑]尝试编译标志的更详细说明:
-falign-functions=32 :没有影响或负面影响-falign-jumps=32 : 没有影响-falign-loops=32:当热循环被隔离成一小段测试代码时,工作正常.但是在正常构建中,编译标志应用于整个源代码,在这种情况下它是有害的:对齐32字节的所有循环对性能不利.只有非常热门的人才能从中受益.__attribute__((optimize("align-loops=32")))在函数声明中使用.不会产生任何影响(生成相同的二进制文件,就好像声明不存在一样).后来由gcc支持团队确认有效忽略.编辑:@Jester在评论中表明该语句适用于gcc 5+.不幸的是,我的开发站主要使用gcc 4.8.4,这更像是一个可移植性的问题,因为我不控制构建过程中使用的最终编译器.只有使用PGO构建才能可靠地产生预期的性能,但PGO不能被接受为解决方案,因为这段代码将使用自己的构建链集成到其他程序中.
所以,我正在考虑内联汇编.这将特定于x64指令集,因此不需要可移植性.
如果我的理解是正确的,像NASM这样的汇编允许使用如下语句:ALIGN 32这会强制下一条指令在32字节边界上对齐.
由于目标源代码在C中,因此必须包含此语句.例如,类似的东西asm("ALIGN 32");
(当然不起作用).
我希望这主要是知道正确的写作指令,而不是更深层次的东西,比如"这是不可能的".
我在MSVC中的C代码只有一个奇怪的编译错误.更确切地说:
错误C2143:语法错误:缺少';' 在'类型'之前
C2143是一个相当普遍的错误,围绕它有无数的问题,但到目前为止它们似乎都没有.可以在这里找到最接近的一个,并强调在块的开头声明变量的重要性,这似乎在这里得到了尊重.
这是一个示例代码:
#define NB_LL 6
typedef struct { long long ll[NB_LL ]; } stateSpace_t;
#define ALLOCATE_ONSTACK(stateName) stateSpace_t stateName##_s; void* stateName = (void*) &(stateName##_s);
Run Code Online (Sandbox Code Playgroud)
以下代码运行良好:
void f1()
{
ALLOCATE_ONSTACK(state1);
/* do something */
}
Run Code Online (Sandbox Code Playgroud)
这个没有:
void f2()
{
ALLOCATE_ONSTACK(state1);
ALLOCATE_ONSTACK(state2); // <--- error C2143: syntax error : missing ';' before 'type'
/* do something */
}
Run Code Online (Sandbox Code Playgroud)
第二个代码适用于GCC,所以问题似乎仅限于MSVC.我的理解是宏ALLOCATE_ONSTACK()只做变量声明和初始化,所以它似乎尊重C语法.
是吗?
我目前正在尝试构建一个代码,该代码可用于各种机器,从手持式口袋和传感器到数据中心的大型服务器.
这些体系结构之间的(许多)差异之一是对齐内存访问的要求.
"标准"x86 CPU上不需要对齐的内存访问,但是如果不遵守规则,许多其他CPU需要它并产生异常.
到目前为止,我一直在使用packed属性(或pragma)强制编译器对已知存在风险的特定数据访问保持谨慎.它工作正常.
问题是,编译器非常谨慎,以至于在此过程中会丢失大量性能.
由于性能很重要,我们最好重写代码的某些部分以专门处理严格对齐的cpus.这样的代码会,在另一方面,是对CPU的支持对齐的内存访问(如86)慢,所以我们想用它仅在需要严格对齐的内存访问的CPU.
现在的问题是:如何在编译时检测目标架构是否需要严格对齐的内存访问?(或反过来)
我最近被指向了我的一个C程序,如果内存块的起始地址足够低,我的一个测试会因为绕零而失败,导致崩溃.
起初我认为"这是一个讨厌的潜在错误",但是,我想知道:这种情况会发生吗?我从来没有见过这个.公平地说,这个程序已经在无数系统上运行了数百万次,到目前为止它从未发生过.
因此,我的问题是:调用malloc()可能返回的最低内存地址是多少?据我所知,我从未见过像0x00000032这样的地址.
我只对"现代"环境感兴趣,例如Linux,BSD和Windows.此代码不适用于C64或任何业余爱好/研究操作系统.
我正在考虑一个非常具体的(现在无用的)处理器,称为土星.
该CPU的一个奇怪特性是它的元素单位是半字节(4位),而不是字节(8位).它不只是化妆品,任何指针值都以半字节表示.
有一次尝试为土星创建一个C编译器:hp48xgcc
看一下它的文档,我注意到它的基本类型遵循通常的GCC约定,使用char 8位.此时,我想知道:CHAR_BIT是否有意提供元素单元的大小?GCC自己的文档似乎暗示了这一点:
You can compute the number of bits in any data type like this:
sizeof (type) * CHAR_BIT
Run Code Online (Sandbox Code Playgroud)
在这种情况下,考虑到Saturn架构,最好让CHAR_BIT = 4
或者我是否误解了CHAR_BIT的含义?
我正在尝试使用 NEON 向量指令做一些相对简单的事情:给定uint64x2_t,我想交换 64 位成员的位置。
又名,如果这是一个简单的普通代码:
typedef struct {
U64 u[2];
} u64x2;
u64x2 swap(u64x2 in)
{
u64x2 out;
out.u[0] = in.u[1];
out.u[1] = in.u[0];
return out;
}
Run Code Online (Sandbox Code Playgroud)
令人惊讶的是,我找不到内在的东西。显然有一个汇编指令 ( VSWP) 但没有相应的内在指令。
这很奇怪。这是一个尽可能微不足道的操作,所以它必须是可能的。问题是:如何?
编辑:作为参考,godbolt结果使用@Jake 回答:https ://godbolt.org/z/ueJ6nB
。不vswp,但vext效果很好。
我一直在阅读有关哪个头文件更适合访问英特尔内在函数的意见:x86intrin.h或immintrin.h。
两者似乎都达到了相同的结果,但我确信在代码可移植性方面一定存在一些细微的差异。也许其中一个比另一个更常见或更完整?
我找不到对其中任何一个的解释。如果有人知道为什么有两个文件,以及它们有什么区别,这将是一个受欢迎的答案。
说到可移植性,对于较旧的编译器(例如gcc< v4.4.0),事情当然会变得更加复杂,而且两者都不可用。必须考虑包含另一个内在标头(可能emmintrin.h用于 SSE 支持)。
我正在努力尝试用Visual生成的C++代码做一些琐碎的事情.这令人沮丧,超出预期.
我习惯于从命令行获取参数,这要归功于:
int main(int argc, char** argv)
Run Code Online (Sandbox Code Playgroud)
好的,这是有效的,至少只要我们留在ANSI世界.
现在,Visual生成了这个:
int main(array<System::String ^> ^args)
Run Code Online (Sandbox Code Playgroud)
关于如何使用这条线,我有点无能为力.我显然试图将未知语义与更熟悉的语义交换,但编译失败.我也一直在互联网上漫游大量的时间,为这种情况找到了很多例子,但是没有一个能够工作......
[编辑]关于我正在尝试做什么的小解释:这应该是一个微不足道的GUI程序.它从命令行参数获取文件路径,并根据用户在GUI上选择的选项写入.所以:它不是CLI,它是一个Windows窗体.
我读了C99标准,它stdint.h是C标准库的一部分.
我是否正确阅读,如果我测试符合C99,请使用:
defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
Run Code Online (Sandbox Code Playgroud)
这意味着stdint.h应该可用吗?
一个C99很好的例子:我可以考虑一个假装符合要求但不提供stdint.h与自己的合规声明不一致的环境,因此有错误吗?
编辑:对于好奇的系统,有问题的系统是带有HP C编译器的OpenVMS(不是gcc,在openVMS上提供stdint.h).因此,根据到目前为止收到的答案和评论,我必须将此实现(假装为C99)视为错误.有关详情,请访问:https://groups.google.com/forum/#!topic/comp.os.vms/Bnh3tIOc7bo%5B101-125%5D
我有一个性能关键的C代码需要在各种平台上工作.其中一些是小端,另一些是大端.
基于宏观检测,检测结束性目前是不完美的过程.但是很难确定宏检测是否适用于系统和编译器的所有组合.欢迎来到便携式代码世界.
检测endianess的一种相对安全的方法是使用运行时测试,并希望它将被编译器优化.这些方面的东西:
static const int one = 1;
#define IS_LITTLE_ENDIAN (*(char*)(&one))
Run Code Online (Sandbox Code Playgroud)
一般来说它有效.编译器应正确检测到该宏的结果对于给定的体系结构始终是相同的(1表示小端,0表示大端),只需完全删除内存访问和关联的分支.
我的问题是:总是这样吗?我们是否可以期望编译器始终正确理解此测试,并始终正确地优化它?(假设-O2/-O3或等效的优化级别,当然不适用于调试代码)
我特别担心双端CPU,比如ARM.由于这样的CPU可以是大端或小端,取决于OS参数,编译器可能难以"硬连线"这样的端序测试.另一方面,我不希望应用程序以"任何两种选择模式"工作:我想它应该编译为一个精确和明确的endianess.因此,IS_LITTLE_ENDIAN应始终保持相同.
无论如何,我要求遇到遇到这种情况的人的经历.由于我目前没有双端CPU和编译器,我无法测试和观察上述假设.
[ 编辑 ] @Brandin建议"保存宏的结果",使其成为一个变量.我猜他提出这样的事情:
static const int one = 1;
static const int isLittleEndian = *(char*)(&one);
Run Code Online (Sandbox Code Playgroud)
由于在编译时会计算静态const int,因此确实可以保证编译器必须知道isLittleEndian的值,因此可以正确地优化使用此变量的分支.
不幸的是,它不起作用.上述声明导致以下编译错误:
error: initializer element is not constant
Run Code Online (Sandbox Code Playgroud)
我想那是因为在编译时无法评估一个(指针地址).
@ HuStmpHrrr的变体,使用union,看起来更好:没有要评估的指针地址.不幸的是,它不能更好地工作,并导致相同的编译错误.
我想这是因为编译器认为联合不够简单,不能用作静态const初始化的值.
所以我们回到了开头,用宏来代替.
c ×12
gcc ×3
c99 ×2
intrinsics ×2
64-bit ×1
alignment ×1
arm ×1
c++-cli ×1
clang ×1
endianness ×1
header ×1
intel ×1
macros ×1
malloc ×1
memory ×1
neon ×1
openvms ×1
performance ×1
portability ×1
stdint ×1
syntax-error ×1
visual-c++ ×1
x86-64 ×1