考虑在C中编写一些不那么明显的算法的实现.例如,让我们在KN King的"C编程:现代方法,第2版"一书中找到它的递归快速排序,它可以从这里获得.最有趣的部分包括以下两个定义:
void quicksort(int a[], int low, int high)
{
int middle;
if (low >= high)
return;
middle = split(a, low, high);
quicksort(a, low, middle - 1);
quicksort(a, middle + 1, high);
}
int split(int a[], int low, int high)
{
int part_element = a[low];
for (;;) {
while (low < high && part_element <= a[high])
high--;
if (low >= high)
break;
a[low++] = a[high];
while (low < high && a[low] <= part_element)
low++;
if (low >= high) …Run Code Online (Sandbox Code Playgroud) 对于以下程序:
int main(void)
{
int value = 2;
int result = value >> 1U;
return result;
}
Run Code Online (Sandbox Code Playgroud)
... Splint 3.1.2给出警告:
splint_test.c: (in function main)
splint_test.c:4:18: Variable result initialized to type unsigned int, expects
int: value >> 1U
To ignore signs in type comparisons use +ignoresigns
Run Code Online (Sandbox Code Playgroud)
Splint似乎声称有符号整数右移的表达式具有无符号整数的类型。但是,我可以在ANSI C90标准中找到的全部是:
结果
E1 >> E2是E1右移位E2位置。如果E1具有无符号类型或E1具有符号类型和非负值,则结果的值是商E1除以数量2的幂的整数部分E2。
此代码的主要目标是带有大多数C90编译器的嵌入式系统。但是,我对编写符合标准的代码感兴趣。我一直在以C99模式在GCC和Clang上进行测试,因此可以restrict正常工作。
我的问题是:
你知道我怎么能忽略我声明变量的地方?
我知道旧学校c告诉你在每个函数的开头都声明变量,但由于我是一个坏人,我喜欢声明接近我使用它们的地方.一个很好的例子就是把int i; 就在for(i = 0; ...)之前.
我们来看一个非常简单的例子
#include <stdio.h>
int main()
{
printf("Hello splint test\n");
int i;
for(i=5;i>0;i--)
{
printf("%2d...\n",i);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这里夹板和大多数旧的c编译器想移动int i; 向上一行,或者放置{}循环声明和for循环.
现在问题是,如何关闭此检查?但保持其他检查是好的吗?
谢谢约翰
注1:我已经使用gcc警告(见下文)作为第一道防线,而valgrind作为第二道防线.但是我正在考虑将夹板添加到可以控制我的愚蠢的事情列表中;-)但是这个检查只是烦人的,
我使用的gcc警告:-Wall -W -Wextra -Wconversion -Wshadow -Wcast-qual -Wwrite-strings -Werror
注2:我知道这种不良行为可能带来的潜在可移植性问题.但我觉得它增加了可读性,也就是说不需要跳起来搜索这种类型的声明更有价值(我们可以在另一个线程中讨论).
更新:更多信息,我把上面的代码放在一个名为main.c的文件中.使用的平台是Ubuntu 8.04和gvim作为编辑器,这是我运行时splint的输出:
splint +gnuextensions main.c
Splint 3.1.1 --- 03 Nov 2006
Command Line: Setting +gnuextensions redundant with current value
main.c:8:8: Parse Error. (For help on parse errors, see splint -help
parseerrors.)
Run Code Online (Sandbox Code Playgroud)
这开辟了另外两个我以前没想过的问题.
"冗余与当前价值",现在有什么价值?
为什么它是解析错误而不是警告?
更新::有补丁夹板支持这个问题的可能性,我还没有尝试过,但我认为这是解决方案.
这是我第一次使用splint(来自Ubuntu存储库),我立即被WTF击中.错误消息:
nightcracker@nightcracker-pc:~/c/brainfuck$ splint brainfuck.c
Splint 3.1.2 --- 03 May 2009
brainfuck.c:17:6: Parse Error. (For help on parse errors, see splint -help
parseerrors.)
*** Cannot continue.
Run Code Online (Sandbox Code Playgroud)
现在,显然它在第16行第6列看到了错误.让我们检查一下(发布完整代码):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum {
CELL_CHUNK_SIZE = 1024,
};
typedef unsigned char cell;
int main(int argc, char *argv[]) {
if (argc < 1) {
fprintf(stderr, "ERROR: Not enough arguments\n");
return 1;
}
FILE *srcfile; // source file << THIS LINE APPARENTLY IS WRONG
long srclen; // source file size
char *bf; …Run Code Online (Sandbox Code Playgroud) 任何夹板专家在那里?我试图用夹板静态分析大量的项目,我有C.我看到过多数量的边界检查显然不能界失误的错误.我写了一个小测试程序试图找出问题,并在我对代码运行splint时注意到一些非常奇怪的警告.我有3个不同的例子.这是第一个:
int arr[3];
int main(void)
{
int i;
int var;
arr[3] = 0; // (1) warning with +bounds, no warning with +likely-bounds
return 0;
}
Run Code Online (Sandbox Code Playgroud)
arr[3]当+bounds我按照预期使用时,分配会生成警告,但在使用时不会执行任何操作+likely-bounds.什么是+likely-bounds连做?它似乎行不通.第二个例子:
int arr[3];
int main(void)
{
int i;
int var;
for (i = 0; i < 3; i++)
var = arr[i]; // (2) warning, even though I'm within the bounds.
return 0;
}
Run Code Online (Sandbox Code Playgroud)
在这个示例中,splint抱怨我正在读取数组的边界("内存读取引用内存超出分配的存储空间.")var = arr[i],即使我显然不是.这应该是一个警告,因为数组中的值没有初始化,但这不是我得到的警告.初始化数组中的最后一个值将清除错误(但不会初始化第一个或第二个).难道我做错了什么?在第三个例子中:
int arr[3];
int main(void)
{
int i;
int var;
arr[3] …Run Code Online (Sandbox Code Playgroud) 我使用夹板作为 c99 代码的静态分析器。
夹板似乎不太符合 c99。因此我应用了这个补丁:http : //www.cs.virginia.edu/pipermail/splint-discuss/attachments/20080718/52cc25f6/attachment.obj
现在,由于声明不在顶部,我没有得到解析错误。
但是如果我在 for 语句中放入一个变量声明,我仍然会在 for 循环中遇到解析错误。例如:
for(int i = 0; i < 10; i++)
{
}
Run Code Online (Sandbox Code Playgroud)
一种解决方法是这样写:
int i;
for(i = 0; i < 10; i++){
}
Run Code Online (Sandbox Code Playgroud)
但是因为我不想适应我所有的 for 循环,我想知道是否有可用的补丁来解决这个问题。
我想splint在debian稳定的环境中运行我的一些源代码.
我需要给出预处理器指令-DUINT16_T='unsigned short',因为我经常需要它.我想将它放在我的.splintrc文件中.
从命令行运行时,splint -DUINT16_T='unsigned short' mysource.c它运行良好.如果将此行移动到我的.splintrc文件中
-DUINT16_T='unsigned short'
-I/usr/local/include/
Run Code Online (Sandbox Code Playgroud)
该splint调用导致
Cannot list files in .splintrc files:
short' (probable missing + or -)
A flag is not recognized or used in an incorrect way (Use -badflag to inhibit
warning)
Run Code Online (Sandbox Code Playgroud)
有人有解决方案吗?(请不要别名).
为了进行更深入的讨论,我将提供一个mnwe(最小的不工作示例)hello.c,这可能会有所帮助:
#include <stdio.h>
int main (void)
{
UINT16_T returnvalue=0;
printf ("Hello, world!\n");
return returnvalue;
}
Run Code Online (Sandbox Code Playgroud)
命令gcc -DUINT16_T='unsigned short' hello.c运行良好 - splint -DUINT16_T='unsigned short' hello.c …
debian whitespace splint secure-coding preprocessor-directive
我正在开发一个相当复杂的项目,作为一个额外的复杂功能,它是一个内核Linux模块.在这样的代码库上运行像splint这样的linting工具当然不是不可能的[1],但是琐碎的努力还不足以让事情顺利进行.
在我投入更多时间之前,我希望能在这个问题上获得一些反馈和意见.有足够的标志,gcc做了很多检查.在实践中使用单独的静态代码分析工具仍然值得吗?
[1] http://www.cs.virginia.edu/pipermail/splint-discuss/2005-January/000531.html
有没有办法停止splint分析包括 POSIX、libc 等在内的系统头文件?我运行-warnposix -preproc:
% splint -warnposix -preproc my.c
/usr/include/unistd.h:220:8: Parse Error: Non-function declaration:
__BEGIN_DECLS : int. (For help on parse errors, see splint -help
parseerrors.)
*** Cannot continue.
Run Code Online (Sandbox Code Playgroud)
更新:
% splint +posixlib +unixlib -I/path/to/myheaders -I/usr/include/x86_64-linux-gnu my.c
/usr/include/asm-generic/int-ll64.h:19:24: Parse Error:
Suspect missing struct or union keyword: __signed__ :
int. (For help on parse errors, see splint -help parseerrors.)
Run Code Online (Sandbox Code Playgroud)
阅读夹板 FAQ14 后,我试图消除非标准关键字:
% splint +posixlib +unixlib -D__signed__= -I/path/to/myheaders -I/usr/include/x86_64-linux-gnu my.c
/usr/include/x86_64-linux-gnu/sys/syslog.h:200:66: Parse Error:
Inconsistent function …Run Code Online (Sandbox Code Playgroud) 我知道局部变量在未设置时可以具有"随机"值,但是用指针设置局部变量的第一个值是不是很糟糕?例如:
void setValue(int* p_val)
{
*p_val = …; /* Assignment does not use *p_val */
}
int main(void)
{
int val;
setValue(&val);
printf("%d", val);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
其中setValue仅设置并且从不读取引用变量的值.Splint警告我val是"在定义之前使用",我对这个警告有点惊讶,因为我相信在执行printf之前设置val的值并使用val.夹板是否不够先进,无法识别用于设置初始值的参考?