广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言语义陷阱超详细梳理总结
  • 205
分享到

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

2024-04-02 19:04:59 205人浏览 薄情痞子
摘要

目录1 指针与数组2 非数组的指针3 作为参数的数组声明4 空指针并非空字符串5 边界计算与不对称边界6 求值顺序7 整数溢出8 为函数提供返回值1 指针与数组 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语言 语义陷阱内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言语义陷阱超详细梳理总结

本文链接: https://www.lsjlt.com/news/143948.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C语言语义陷阱超详细梳理总结
    目录1 指针与数组2 非数组的指针3 作为参数的数组声明4 空指针并非空字符串5 边界计算与不对称边界6 求值顺序7 整数溢出8 为函数提供返回值1 指针与数组 C语言中只...
    99+
    2022-11-13
  • C语言 超详细梳理总结动态内存管理
    目录一.为什么存在动态内存分配二.动态内存函数的介绍1.malloc和free2.calloc3.realloc三.常见的动态内存错误1.对NULL指针的解引用操作2.对动态开辟空间...
    99+
    2022-11-13
  • C语言自定义类型超详细梳理之结构体枚举联合体
    目录一、什么是结构体1.结构体实现2.匿名结构体类型3.结构体自引用4.结构体的内存对齐5.结构体位段 二、什么是枚举1.枚举类型的定义2.枚举的优点三、联合(共用体)1.什么是联合...
    99+
    2022-11-13
  • C语言数组全面总结梳理
    目录一,一维数组1.创建和初始化2.使用下标访问3.在内存中的存储二,二维数组 1.创建和初始化2.使用下标访问3.在内存中的存储三,越界问题数组(array)是由一系列类...
    99+
    2022-11-13
  • C语言超详细梳理排序算法的使用
    目录排序的概念及其运用排序的概念排序运用插入排序直接插入排序希尔排序选择排序直接选择排序堆排序交换排序之冒泡排序总结排序的概念及其运用 排序的概念 排序:所谓排序,就是使一串记录,按...
    99+
    2022-11-13
  • C语言结构体超详细讲解
    目录前言1、结构体的声明1.1 结构的基础知识1.2 结构的声明1.3 结构成员的类型1.4 结构体变量的定义和初始化2、结构体成员的访问2.1 点操作符访问2.2 ->操作符...
    99+
    2022-11-13
  • C语言 struct结构体超详细讲解
    目录一、本章重点二、创建结构体三、typedef与结构体的渊源四、匿名结构体五、结构体大小六、结构体指针七、其他一、本章重点 创建结构体typedef与结构体的渊源匿名结构体结构体大...
    99+
    2022-11-13
  • C语言 超详细总结讲解二叉树的概念与使用
    目录1.二叉树的概念及结构 2.二叉树链式结构的实现1.二叉树的概念及结构  ①概念:一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别...
    99+
    2022-11-13
  • C语言超详细讲解指针与结构体
    目录本节目标初识指针1、内存与地址2、变量的地址3、指针变量4、指针的使用5、指针变量的大小初识结构体1、什么是结构体2、结构体的定义3、结构体的使用本节目标 理解内存与地址的相关概...
    99+
    2022-11-13
  • C语言与C++内存管理超详细分析
    目录一、内存1.1 内存四区1.2 使用代码证实内存四区的底层结构二、malloc 和 free2.1 malloc 和 free 的使用2.2 内存泄漏与安全使用实例与讲解三、ne...
    99+
    2022-11-13
  • C语言数据结构超详细讲解单向链表
    目录1.链表概况1.1 链表的概念及结构1.2 链表的分类2. 单向链表的实现2.1 SList.h(头文件的汇总,函数的声明)2.2 SList.c(函数的具体实现逻辑)2.2.1...
    99+
    2022-11-13
  • C语言结构体嵌套与对齐超详细讲解
    目录嵌套结构体结构体内存对齐32位的操作系统64位的操作系统嵌套结构体 格式:typedef struct 结构体名 {struct 结构体名1 结构体变量名1;struct 结构体...
    99+
    2022-12-26
    C语言结构体嵌套 C语言结构体对齐 C语言结构体嵌套与对齐
  • C语言超详细讲解数据结构中的线性表
    目录前言一、分文件编写1、分文件编写概念2、代码展示二、动态分布内存malloc1、初识malloc2、使用方法三、创建链表并进行增删操作1、初始化链表2、在链表中增加数据3、删除链...
    99+
    2022-11-13
  • C语言线性表中顺序表超详细理解
    目录一、本章重点二、线性表三、顺序表四、静态顺序表接口实现4.1顺序表初始化4.2顺序表打印4.3顺序表尾插4.4顺序表尾删4.5顺序表头插4.6顺序表头删4.7顺序表任意位置插入4...
    99+
    2022-11-13
  • C语言超详细讲解结构体与联合体的使用
    目录结构体offsetof-宏位段枚举联合体(共用体)结构体 结构体内存对齐问题: 当我们在计算结构体的大小时,我们便需要清楚的知道结构体内存对齐是什么。 存在内存对齐的原因可细分为...
    99+
    2022-11-13
  • 超详细分析C语言动态内存管理问题
    目录一、为什么存在动态内存的分配二、动态内存函数的介绍2.1 malloc和free2.2 calloc2.3 realloc三、常见的动态内存错误3.1 对NULL指针的解引用操作...
    99+
    2022-11-13
  • C语言可变参数与内存管理超详细讲解
    目录概述动态分配内存重新调整内存的大小和释放内存概述 有时,您可能会碰到这样的情况,您希望函数带有可变数量的参数,而不是预定义数量的参数。C 语言为这种情况提供了一个解决方案,它允许...
    99+
    2023-01-02
    C语言可变参数 C语言内存管理
  • C语言超详细讲解数据结构中双向带头循环链表
    目录一、概念二、必备工作2.1、创建双向链表结构2.2、初始化链表2.3、动态申请节点2.4、打印链表2.5、销毁链表三、主要功能3.1、在pos节点前插入数据尾插头插3.2、删除p...
    99+
    2022-11-13
  • C语言详细分析宏定义与预处理命令的应用
    目录宏定义与预处理命令预处理命令 - 宏定义定义符号常量定义傻瓜表达式定义代码段预定义的宏函数 VS 宏定义预处理命令 - 条件式编译示例宏定义与预处理命令 预处理阶段:处理宏定义与...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作