小编Cya*_*yan的帖子

C中数组的const和typedef

在C中,typedef使用这种结构可以使用数组:

typedef int table_t[N];

这里,table_t现在定义为N的数组int.声明的任何变量table_t t;现在将表现为正常数组int.

这种构造的要点是在函数中用作参数类型,例如:

int doSomething(table_t t);

一个相对等效的函数原型可能是:

int doSomething(int* t);

第一个结构的优点是它强制执行N表的大小.在许多情况下,强制执行此属性更安全,而不是依赖程序员来正确地找出这种情况.

现在一切都很好,除了为了保证表的内容不会被修改,有必要使用const限定符.

以下陈述相对简单易懂:

int doSomething(const int* t);

现在,doSomething保证它不会修改作为指针传递的表的内容.现在,这几乎相同的建筑怎么样?:

int doSomething(const table_t t);

这是什么const?表的内容,或指向表的指针?如果它是指针const,是否有另一种方式(C90兼容)保留定义表大小的能力并告诉它的内容将是const?

请注意,有时还需要修改表的内容,因此该const属性不能嵌入到typedef定义中.

[ 编辑 ]感谢到目前为止收到的优秀答案.总结一下:

  • typedef强制执行大小N的初始假设是完全错误的.它基本上与普通指针的行为相同.
  • const属性的行为也与指针相同(与指针类型的typedef形成鲜明对比,如下面的@random所示)
  • 要强制执行一个大小(这不是最初的问题,但最终现在非常重要......),请参阅Jonathan的回答

c arrays typedef const

19
推荐指数
3
解决办法
4464
查看次数

检测字节序

我正在尝试创建一个C源代码,无论目标系统的字节顺序如何,它都能正确处理I/O.

我选择了"little endian"作为我的I/O约定,这意味着,对于大端CPU,我需要在写入或读取时转换数据.

转换不是问题.我面临的问题是检测字节序,最好是在编译时(因为CPU在执行过程中不会改变字节序...).

到目前为止,我一直在使用这个:

#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
...
#else
...
#endif
Run Code Online (Sandbox Code Playgroud)

它被记录为GCC预定义的宏,而Visual似乎也理解它.

但是,我收到报告说某些big_endian系统(PowerPC)的检查失败了.

所以,我正在寻找一个万无一失的解决方案,确保无论编译器和目标系统如何都能正确检测到字节顺序.好吧,他们中的大多数至少......

[编辑]:提出的大多数解决方案都依赖于"运行时测试".编译期间编译器有时可以正确评估这些测试,因此不会产生实际的运行时性能.

然而,用某种<< if (0) { ... } else { ... }>> 分支是不够的.在当前的代码实现中,变量和函数声明依赖于big_endian检测.使用if语句无法更改这些内容.

嗯,显然,有后备计划,即重写代码......

我宁愿避免这种情况,但是,它看起来像是一个越来越少的希望......

[编辑2]:我通过深度修改代码测试了"运行时测试".尽管他们正确地完成了工作,但这些测试也会影响性能.

我期待着,因为测试具有可预测的输出,编译器可以消除坏分支.但不幸的是,它并不是一直有效.MSVC是一个很好的编译器,并且成功地消除了坏分支,但是GCC的结果是混合的,这取决于版本,测试类型,以及对64位比对32位的影响更大.

真奇怪.而且这也意味着无法确保编译器处理运行时测试.

编辑3:这些天,我正在使用编译时常量联合,期望编译器将其解析为明确的是/否信号.它运作得很好:https: //godbolt.org/g/DAafKo

c endianness compile-time c-preprocessor

18
推荐指数
6
解决办法
2万
查看次数

在编写干净的C代码时,充分利用ARM未对齐的内存访问

过去,ARM处理器无法正确处理未对齐的内存访问(ARMv5及更低版本).喜欢的东西u32 var32 = *(u32*)ptr;也只是失败(引发异常),如果ptr没有正确的4字节对齐.

编写这样的语句对于x86/x64可以正常工作,因为这些CPU总是非常有效地处理这种情况.但根据C标准,这不是一种"正确"的写作方式.u32显然等同于4个字节的结构,必须在4个字节上对齐.

在保持正统正确性确保与任何cpu完全兼容的同时获得相同结果的正确方法是:

u32 read32(const void* ptr) 
{ 
    u32 result; 
    memcpy(&result, ptr, 4); 
    return result; 
}
Run Code Online (Sandbox Code Playgroud)

这个是正确的,将为任何能够或不在未对齐位置读取的CPU生成适当的代码.更好的是,在x86/x64上,它已针对单个读取操作进行了适当优化,因此具有与第一个语句相同的性能.它便携,安全,快速.谁能问更多?

好吧,问题是,在ARM上,我们没有那么幸运.

编写memcpy版本确实是安全的,但似乎导致系统谨慎的操作,这对于ARMv6和ARMv7(基本上是任何智能手机)来说都非常慢.

在一个严重依赖读取操作的性能导向应用程序中,可以测量第一版和第二版之间的差异:它在设置时 大于5倍gcc -O2.这太过不容忽视了.

试图找到一种使用ARMv6/v7功能的方法,我寻找了几个示例代码的指导.不幸的是,他们似乎选择了第一个声明(直接u32访问),这不应该是正确的.

这还不是全部:新的GCC版本现在正在尝试实现自动矢量化.在x64上,这意味着SSE/AVX,在ARMv7上意味着NEON.ARMv7还支持一些新的"加载多个"(LDM)和"存储多个"(STM)操作码,这些操作码需要指针对齐.

那是什么意思 ?好吧,编译器可以自由地使用这些高级指令,即使它们没有从C代码中特别调用(没有内在的).为了做出这样的决定,它使用a u32* pointer应该在4个字节上对齐的事实.如果不是,那么所有的赌注都是关闭的:未定义的行为,崩溃.

这意味着即使在支持未对齐内存访问的CPU上,使用直接u32访问也是危险的,因为它可能导致在高优化设置下生成错误的代码(-O3).

那么现在,这是一个困境:如何在未对齐内存访问的情况下访问ARMv6/v7的本机性能而无需编写错误的版本u32访问权限?

PS:我也试过__packed()说明,从性能的角度看,它们似乎与memcpy方法完全一样.

[编辑]:感谢迄今为止收到的优秀元素.

看看生成的程序集,我可以确认@Notlikethat发现该memcpy版本确实生成了正确的ldr操作码(未对齐的加载).但是,我还发现生成的程序集无用地调用str(命令).因此,完整的操作现在是一个未对齐的加载,一个对齐的存储,然后是一个最终对齐的加载.这比必要的工作要多得多.

回答@haneefmubarak,是的,代码正确内联.而且,不是,memcpy提供最佳速度是非常远的,因为强制代码接受直接u32 …

c arm memory-alignment

18
推荐指数
2
解决办法
6656
查看次数

全局变量性能影响(c,c ++)

我目前正在开发一种非常快速的算法,其中一部分是极快的扫描和统计功能.在这个任务中,我追求任何性能优势.因此,我也有兴趣保持代码"多线程"友好.

现在的问题是:我注意到将一些非常频繁访问的变量和数组放入"全局"或"静态本地"(它们都是相同的),有一个可衡量的性能优势(在+ 10%的范围内) .我试图理解为什么,并找到一个解决方案,因为我宁愿避免使用这些类型的分配.请注意,我不认为差异来自"分配",因为在堆栈上分配一些变量和小数组几乎是瞬间完成的.我相信差异来自"访问"和"修改"数据.

在这次搜索中,我发现了stackoverflow的这篇旧帖子: 全局变量的C++性能

但我对那里的答案感到非常失望.很少解释,大多是咆哮"你不应该这样做"(嘿,这不是问题!)和非常粗略的陈述,如'它不影响性能',这显然是不正确的,因为我用精确测量它基准工具.

如上所述,我正在寻找解释,并且,如果存在,则解决此问题.到目前为止,我已经感觉到计算本地(动态)变量的内存地址比全局(或本地静态)花费更多.也许类似于ADD操作差异.但这无助于找到解决方案......

c performance benchmarking static global

14
推荐指数
2
解决办法
9073
查看次数

CPU寄存器的大小

通常最好将CPU寄存器用于其全部容量.对于便携式代码,它意味着在64位CPU上使用64位算术和存储,在32位CPU上仅使用32位(否则,将在32位模式下模拟64位指令,从而导致毁灭性的表演).

这意味着有必要检测CPU寄存器的大小,通常是在编译时(因为运行时测试很昂贵).

多年来,我一直使用简单的启发式方法sizeof(nativeRegisters) == sizeof(size_t).

它在许多平台上运行良好,但它似乎是linux x32的错误启发式:在这种情况下,size_t只有32位,而寄存器仍然可以处理64位.它会导致一些性能损失(对我的用例来说很重要).

即使在这种情况下,我也想正确检测CPU寄存器的可用大小.

我怀疑我可以尝试在特殊情况x32模式下找到一些特定于编译器的宏.但我想知道是否存在更通用的东西,以涵盖更多情况.例如,另一个目标是OpenVMS 64位:那里,本机寄存器大小是64位,但size_t只是32位.

c cpu-registers

13
推荐指数
1
解决办法
3629
查看次数

在C中的堆栈上可分配的不透明类型

在设计C接口时,通常只允许.h用户程序需要知道的公共接口().

因此,例如,如果用户程序不需要知道它们,则结构的内部组件应该保持隐藏.这确实是一种很好的做法,因为结构的内容和行为将来可能会发生变化,而不会影响界面.

实现该目标的一个好方法是使用不完整的类型.

typedef struct foo opaqueType;

现在opaqueType可以构建仅使用指针的接口,而无需用户程序知道内部工作struct foo.

但有时,可能需要静态地(通常在堆栈上)分配此类结构,以解决性能和内存碎片问题.显然,上面的结构,opaqueType是不完整的,所以它的大小是未知的,所以它不能静态分配.

解决方法是分配"shell类型",例如:

typedef struct { int faketable[8]; } opaqueType;

上面的构造强制执行大小和对齐,但不会进一步描述结构真正包含的内容.因此它符合保持"不透明"类型的目标.

它主要起作用.但在一种情况下(GCC 4.4),编译器抱怨它打破了严格别名,并且它生成了错误的二进制文件.

现在,我已经阅读了大量关于严格混叠的内容,所以我想我现在明白这意味着什么.

问题是:有没有办法定义一个opaque类型,它仍然可以在堆栈上分配,而不会破坏严格的别名规则?

请注意,我已经尝试了这篇优秀文章中描述union方法,但它仍然会生成相同的警告.

另请注意,visual,clang和gcc 4.6及更高版本不会抱怨并且可以正常使用此构造.

[编辑]信息补充:

根据测试,问题只发生在以下情况:

  • 私人和公共类型不同.我正在将公共类型转换为.c文件中的私有类型.如果他们是同一个联盟的一部分,那显然无关紧要.公共类型是否包含无关紧要char.
  • 如果私有类型的所有操作都只是读取,则没有问题.只有写入会导致问题.
  • 我还怀疑只有自动内联的函数才会遇到麻烦.
  • 问题仅发生在gcc 4.4 at -O3设置上.-O2很好.

最后,我的目标是C90.也许C99如果真的没有选择.

c stack memory-management opaque-pointers

11
推荐指数
1
解决办法
773
查看次数

gcc的性能下降,可能与内联相关

我目前正在经历一些奇怪的效果gcc(测试版本:4.8.4).

我有一个性能导向的代码,运行速度非常快.它的速度在很大程度上取决于内联许多小功能.

由于内联多个.c文件很困难(-flto尚未广泛使用),因此我将许多小函数(通常每行1到5行)保存到一个公共C文件中,我正在开发一个编解码器,其相关解码器.根据我的标准,它"相对"大(约2000行,虽然其中很多只是注释和空白行),但将它分成更小的部分会带来新的问题,所以如果可能的话,我宁愿避免这样做.

编码器和解码器是相关的,因为它们是逆运算.但是从编程的角度来看,它们是完全分离的,除了少量的typedef和非常低级的函数(例如从未对齐的内存位置读取)之外,没有任何共同点.

奇怪的效果是这个:

我最近fnew在编码器方面添加了一个新功能.这是一个新的"切入点".它不会在.c文件中的任何位置使用或调用.

它存在的简单事实使得解码器功能的性能fdec大幅下降超过20%,这是太多不容忽视的.

现在,记住比编码和解码操作是完全分开的,并分享几乎没有,节省一些小的typedef(u32,u16关系等)以及相关的操作(读/写).

当定义新的编码功能fnewstatic,解码器的性能fdec提高了恢复正常.既然fnew没有从中调用.c,我想它就像它不存在一样(死代码消除).

如果static fnew现在从编码器端调用,则性能fdec仍然很强.

但是一旦fnew修改,fdec性能就会大幅下降.

假设fnew修改超过了一个阈值,我增加了以下gcc参数:( --param max-inline-insns-auto=60默认情况下,它的值应该是40.)并且它起作用:性能fdec现在恢复正常.

而且我猜这个游戏将永远持续每一个小修改fnew或其他类似的东西,需要进一步调整.

这简直太奇怪了.fnew对于完全不相关的函数具有连锁效应,函数中的一些小修改是没有逻辑上的原因fdec,只有关系是在同一个文件中.

到目前为止,我可以发明的唯一一个初步的解释是,简单的存在可能fnew足以跨越某种global file threshold会影响的东西fdec.fnew可以在以下情况下"不存在":1.不存在,2.static …

c performance gcc inlining

10
推荐指数
1
解决办法
666
查看次数

获取C中的大文件大小

在有人抱怨"重复"之前,我一直在彻底检查SO,但似乎还没有干净的答案,尽管问题看起来很简单.

我正在寻找一个可移植的C代码,它能够提供文件的大小,即使这样的文件大于4GB.

通常的方法(fseek,ftell)工作正常,只要文件仍然<2GB.它在各处得到了相当好的支持,所以我试图找到相同的东西.

不幸的是,所有编译器都不支持更新的方法(fseeko,ftello).例如,MinGW错过了(显然是MSVC).此外,一些评论让我相信新的返回类型(off_t)不一定支持> 2GB的大小,它可能依赖于一些外部参数来进行检查.

MSVC不支持明确的方法(fseeko64,ftello64).MS提供等效的_fseeki64和_ftelli64.这已经很糟糕,但它变得更糟:一些Linux配置似乎在运行时严重支持这些功能.例如,我在PowerPC上的Debian Squeeze,使用GCC 4.4,将使用fseeko64生成一个"filesize"方法,它始终返回0(虽然它适用于Ubuntu64).MinGW似乎回答了2GB以上的随机垃圾.

嗯,就可移植性而言,我有点无能为力.如果我需要制作一堆#if #else,那么为什么不首先直接使用OS和编译器细节方法,例如用于MSVC的GetFileSize().

c file filesize large-files

9
推荐指数
3
解决办法
8774
查看次数

C++ 2a合同编程和编译器

我有兴趣研究最近接受的C++ 20合同编程,用于学习和调查目的.

当我正在寻找编译器支持时,我很失望,没有找到任何.双方gccclang都相当明确表示,他们不其内支持此功能--std=c++2a模式.

由于批准是最近的,我不会惊讶于当前的编译器不支持所提出的确切语义.更令我惊讶的是,绝对没有任何东西,即使是特定于编译器的扩展,即使在有限的方式下也会模仿相同的功能.

我期待C++委员会只考虑在现场证明自己的功能,通常是通过编译器或目标特定扩展.批准C++ 20的此功能表明该功能应该可以在某处访问.但到目前为止,我一直无法找到它.

目前有没有办法试验C++合同编程?甚至使用一组特定于编译器的扩展?

c++ gcc clang contract c++20

9
推荐指数
1
解决办法
617
查看次数

PowerPC仿真:Qemu,PearPC或......?

我目前正在尝试构建一个配置来测试Big-Endian系统上的一些代码.

通过聊天和研究,我一直相信这些测试的一个好目标是PowerPC架构.由于我没有自己的,并且不希望很快就能直接访问,我正在寻找某种仿真软件来测试我的代码.

问题是,我发现在这方面没有"易于使用"的解决方案.

似乎至少有两种可能的解决方案,一种使用QEMU,另一种使用PearPC.它们都不容易部署.

我看到它的方式:

我想要像部署VMWare虚拟机一样简单,只需一个简单的ghost VM即可使用和下载.

附加信息:我认为PowerPC上的Linux可能是更好的操作系统选择,因为模拟MAC环境可能会破坏许可证.我猜QEMU也比PearPC更新,更受支持.主机系统可以是Windows或Linux.主机CPU必须是x86.

qemu powerpc emulation

8
推荐指数
2
解决办法
1万
查看次数