C语言 语义陷阱超详细梳理总结

 更新时间:2022年03月25日 15:38:38   作者:鹿九丸  
这篇文章主要介绍了C语言常见的一些语义陷阱,梳理的比较全面,对我们做开发的过程中有一定帮助,感兴趣的朋友快来看看吧

1 指针与数组

  • C语言中只有一维数组。数组中的元素可以是任意类型的对象,这也是多维数组构建的理论基础所在
  • 对于一个数组,我们只能做两件事:确定该数组的大小以及获得该数组下标为0的元素的指针。任何一个数组下标运算都等同于一个对应的指针运算。
  • 数组名代表首元素的地址,无法对其进行++或者–操作,换句话说,我们无法改变数组名(表示的值),因为数组名是个常量,无法进行修改。

2 非数组的指针

下面有一段程序,指出它的错误:

char *r;
r = malloc(strlen(s)+strlen(t));
strcpy(r,s);
strcat(r,t);
  • malloc有可能无法提供请求的内存,这种情况下malloc函数会通过返回一个空指针来作为“内存分配失败”事件的信号。
  • 给r分配的内存在使用完毕后应该及时释放。
  • 前面的例程在调用malloc函数时并未分配足够的内存,因为字符串还包含结束标志'\0'。

3 作为参数的数组声明

1.下面列举的两种写法是等价的:

char hello[] = "hello";
printf("%s\n",hello);//写法1
printf("%s\n",&hello);//写法2

原因:数组名hello代表数组hello首元素的地址。

2.下面的两种写法是等价的:

int strlen(char s[])
{
	/*具体内容*/
}
int strlen(char *s)
{
	/*具体内容*/
}

注意下面的两种写法:

extern char *hello;
extern char hello[];

这两种写法虽然是都是正确的,但是不同的形式传递给我们的意思却是完全不一致的,我们要根据具体情况进行使用。

4 空指针并非空字符串

注意:空指针不能对其进行解引用。

同时注意不能出现下述写法:

if(strcmp(p,(char*)0)==0)
	···

这种写法是非法的,原因在于库函数strcmp的实现中会包括一个操作,用于查看它的指针参数所指向的内容,即对空指针进行了解引用。

也不能出现下述写法:

假设p是空指针

printf(p);
printf("%s",p);
//当然,这两种写法是等价的

这种行为是未定义的。

5 边界计算与不对称边界

在我们写循环是最好这样来写:

int i = 0;
for(i = 0;i < 10; i++)
	···

这样写能够更好的看出循环的次数,即10次。

当数组中有10个元素时,下标的取值范围为0到9,但是当我们不需要引用这个元素时只需要引用这个元素的地址时,我们可以这样写

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(int i = 0;&arr[i]<&(arr[10]);i++)
	···

这样可以顺利打印出数组元素从1到10的数字,

ANSI C标准明确允许这种用法:数组中实际不存在的"溢界"元素的地址位于数组之外所占内存之后,这个地址可以用于进行赋值和比较。当然,如果要引用该元素,那就是非法的了。对于实际去读取这个元素的值,这种做法的结果是未定义的,而且极少有编译器能偶检测出这个错误。当然,如果试图去修改这个元素,必然会导致程序崩溃,属于非法访问了!

6 求值顺序

C语言中只有四个运算符(&&、||、?:和,)存在规定的求值顺序。==运算符&&和运算符||首先对左侧操作数求值,只有在需要时才对右侧操作数求值。==运算符?:有三个操作数:在a?b:c中。操作数a首先被求值,根据a的值再求操作数b或c的值(此时b或c两个表达式根据前面a表达式的结果只会执行一个)。逗号运算符则首先对左侧操作数求值,然后"丢弃该值",再对右侧操作数求值。

注意:分割函数的参数并非逗号运算符。例如,x和y在函数f(x,y)中的求值顺序是未定义的,而在函数g((x,y))中却是确定的先x后y的循序。在后一个例子中,函数g只有一个参数。这个参数的值是这样求得的:先对x求值,然后“丢弃”x的值,接着求y的值。

这种求值顺序的存在使得某些“错误”的程序变为了正确,且在执行后得出正确的结果:

if(count!=0 && sum/count < smallaverage)
	···

注意:C语言中其它所有的运算符对其操作数求值的顺序是未定义的。特别是,赋值运算符并不保证任何求值循序。

例如:下面的这中从数组x中复制前n个元素到数组y中的做法是不正确的,因为它对求值顺序做了太多的假设:

i = 0;
while(i < n)
	y[i] = x[i++];

上面的代码假设y[i]的地址将在i的自增操作指向之前被求值,但这是不一定的,这依赖于编译器的具体实现。同样,下面的这种写法也是不正确的:

i = 0;
while(i<n)
	y[i++] = x[i];

修改成下面这种写法即可正常工作:

i = 0;
while(i<n)
{
	y[i] = x[i];
	i++;
}

当然,这种写法也可以简写为:

for(i = 0;i < n;i++)
	y[i] = x[i];

7 整数溢出

无符号整数不会发生溢出,这是C语言所规定的,如果结果大于所能表示的最大值M,则模(M+1),也就是发生了截断现象。

两个有符号整数进行相加时会发生溢出,而且溢出的结果是未定义的。

下面是一种错误的检查方式:

if(a + b < 0)
	complain();

因为当a+b却是发生溢出时,所有关于结果如何假设都不再可靠。

下面是两种正确的方式:

//方法一:
if((unsigned)a + (unsigned) > INT_MAX)
	complain();
//方法二:
if(a > INT_MAX - b)
	complain()

8 为函数提供返回值

C语言种常常通过return 返回一个值来告知操作系统的执行是成功还是失败,典型的处理方案是。返回值为0表示程序执行成功,返回值为非0则表示程序执行失败。我们常常会在程序的末尾加上return 0操作。

到此这篇关于C语言 语义陷阱超详细梳理总结的文章就介绍到这了,更多相关C语言 语义陷阱内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言表达式求值中类型转换和优先级等问题详解

    C语言表达式求值中类型转换和优先级等问题详解

    表达式求值是一个常见的问题,可以用C语言实现,下面这篇文章主要给大家介绍了关于C语言表达式求值中类型转换和优先级等问题的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-05-05
  • 使用ShellClass获取文件属性详细信息的实现方法

    使用ShellClass获取文件属性详细信息的实现方法

    本篇文章是对ShellClass获取文件属性详细信息的实现方法进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++ 花括号{}初始化小结

    C++ 花括号{}初始化小结

    在C++11及以后的版本中,花括号{}语法在不同语境下有不同的用法,本文就详细的介绍C++ 花括号{}初始化,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • C++ 中boost::share_ptr智能指针的使用方法

    C++ 中boost::share_ptr智能指针的使用方法

    这篇文章主要介绍了C++ 中boost::share_ptr智能指针的使用方法的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下
    2017-10-10
  • C++简单又轻松的讲解类和对象中友元函数

    C++简单又轻松的讲解类和对象中友元函数

    采用类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,成员函数一般定义为公有的,依此提供类与外界间的通信接口。但是,有时需要定义一些函数,这些函数不是类的一部分,但又需要频繁地访问类的数据成员,这时可以将这些函数定义为该类的友元函数
    2022-06-06
  • 使用C++ Matlab中的lp2lp函数教程详解

    使用C++ Matlab中的lp2lp函数教程详解

    本文介绍如何使用C++编写数字滤波器设计算法,实现Matlab中的lp2lp函数,将低通滤波器转换为参数化的低通滤波器,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-04-04
  • C语言字符函数与字符串函数详解

    C语言字符函数与字符串函数详解

    这篇文章主要给大家介绍了关于C语言字符/字符串的相关函数,文中通过示例代码总结的非常详细,对大家学习或者使用C语言具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2021-09-09
  • Visual Studio2022的完全卸载及安装到D盘的操作方法

    Visual Studio2022的完全卸载及安装到D盘的操作方法

    这篇文章主要介绍了Visual Studio2022的完全卸载以及完全安装到D盘,因为VS如果随便写在会有很多很多的乱七八糟的东西掉出来,所以我们选择制式一点的卸载方式,需要的朋友可以参考下
    2022-09-09
  • C语言实现遍历文件夹中的文件

    C语言实现遍历文件夹中的文件

    这篇文章主要为大家详细介绍了如何使用C语言实现遍历文件夹中的文件,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下
    2024-02-02
  • Pthread并发编程之线程基本元素和状态的剖析

    Pthread并发编程之线程基本元素和状态的剖析

    本篇文章主要给大家介绍pthread并发编程当中关于线程的基础概念,并且深入剖析进程的相关属性和设置,以及线程在内存当中的布局形式,帮助大家深刻理解线程
    2022-11-11

最新评论