C语言不匹配的整形转换详解

本文通过分析《Cprimerplus》中不匹配的整形转换代码,探讨了不同整型之间的转换原理,涉及原码、反码、补码的概念,并从机器码层面解释了转换结果。代码示例中展示了short、int、unsigned等类型在正负数值转换时的细节,揭示了底层数据表示对转换的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景:近期翻看《C primer plus》第6版,书中在第四章有写不匹配的整形转换的代码和分析。虽然书中的分析思路可以解释的通,但是我认为还有一种更偏向底层的分析思路会更直观,所以写这篇文章记录下,如有错误敬请大家批评指正。

本文布局:第一部分是不匹配的整形转换代码(基于书中的代码的基础上做了些修改),第二部分是该代码对应的输出结果的截图,第三部分尝试从机器码这样偏底层的角度来解释这样的结果,第四部分谈下我对本文中的这种偏向底层的分析思路的想法。

一、不匹配的整形转换代码

#include <stdio.h>
#define PAGES 336
#define WORDS 65618

int main(void)
{
	short num = PAGES;
	short mnum = -PAGES;
	int num2 = WORDS;
	int mnum2 = -WORDS;
	
	printf("num as short and unsigned short:%hd, %hu\n",num,num);
	printf("-num as short and unsigned short:%hd, %hu\n", mnum, mnum);
	printf("-WORDS as int and unsigned int:%d, %u\n", mnum2, mnum2);
	printf("-WORDS as short and unsigned short:%hd, %hu\n", mnum2, mnum2);
	printf("-WORDS as signed char and unsigned char:%hhd, %hhu\n", mnum2, mnum2);

	return 0;
}

二、该代码对应的输出结果的截图

三、基于机器码这样更偏底层的角度来分析

稍微接触过计算机的同学应该都知道,计算机中的数据都是用0、1这样的二进制码表示的,关于数字的表示大家也都有学过原码、反码、补码,在此都不再赘述。但是有两点需要强调:第一,原码的存在是为了让我们人脑计算该二进制码所代表的十进制数;第二,反码的存在是为了机器内部来存储、运算数据。这2点非常重要,接下来我们就基于这2点从更底层的角度来分析上述代码的结果。

PAGES对应的原码  00000000 00000000 00000001 01010000
-PAGES对应的原码 10000000 00000000 00000001 01010000
-WORDS对应的原码 10000000 00000001 00000000 01010010

PAGES对应的补码  00000000 00000000 00000001 01010000
-PAGES对应的补码 11111111 11111111 11111110 10110000
-WORDS对应的补码 11111111 11111110 11111111 10101110

如上由于PAGES \ WORDS 都是signed int类型的(-PAGES \ -WORDS同理),都占有4个字节(对C语言来说不同的系统int类型占用的字节数略有差异,此处默认与我的系统一样均为4个字节)。我们可以根据这两个常量对应的值,用程序员模式的计算器算出其原码,然后根据正数、负数中原码与补码的关系求出其对应的补码(如上已列出)。

由于short类型在我的系统中是2个字节,所以num在计算机内部的补码是PAGES对应补码的后两个字节,即为00000001 01010000。由于num是signed short类型的正数,所以num as short and unsigned short对应的原码都与补码一致,即 00000001 01010000,对应的十进制数即为336

mnum(即-num)在计算机内部的补码是-PAGES对应补码的后两个字节,即11111110 10110000。signed short是带符号的,第一位是符号位,该符号位上是1代表负号,所以-num as short对应的原码是 10000001 01010000,对应的十进制数即为-336;而unsigned short是不带符号的,都是正数,而正数的原码与机器内存储的补码相同,所以as unsigned short对应的原码是  11111110 10110000,对应的十进制数即为65200

与-PAGES类似,-WORDS as int对应的原码是 10000000 00000001 00000000 01010010,对应的十进制数即为-65618,as unsigned int对应的原码是 11111111 11111110 11111111 10101110,对应的十进制数即为4294901678

与-num类似,-WORDS as short对应的补码是11111111 10101110,所以原码是 10000000 01010010,对应的十进制数即为-82,as unsigned short对应的原码是 11111111 10101110,对应的十进制数即为65454

同理,-WORDS as signed char对应的补码是10101110,所以原码是 11010010,对应的十进制数即为-82, as unsigned char对应的原码是 10101110,对应的十进制数即为174

四、关于此种分析思路的一点想法

此分析思路的优点非常明显,无需死记不匹配的整形转换的规律,只需知道原码和反码的本质即可根据这一思路分析出结果。不过值得注意的是,C语言在不同的系统中,各个整数类型所占的字节数可能会略有差异。

### C语言 `strtol` 函数详解 #### 头文件 为了使用 `strtol` 函数,程序中需包含 `<stdlib.h>` 头文件[^3]。 #### 函数原型 函数声明如下: ```c long int strtol(const char *str, char **endptr, int base); ``` 此函数用于将字符串形式的数字转换成长整型数值。参数解释如下: - `str`: 指向待解析的空终止字符数组(即字符串)。该字符串应遵循特定格式:可选的初始空白字符忽略计;随后是一个可选的加号或减号作为符号位;接着是由指定基数决定的一系列有效数字组成的序列[^2]。 - `endptr`: 如果是NULL,则会更新指向第一个未能成功解析的有效字符的位置。这可以用来检测输入字符串中有多少部分被实际处理过。 - `base`: 表示所期望的基础数系,范围是从2到34之间的一个整数值。如果设置为0,则允许自动识别十进制、八进制或者十六进制表示法(依据前缀 '0' 或者 '0x'/‘0X’ 来判断)。 #### 返回值 返回由给定字符串代表的新分配的对象地址。对于有效的字符串,它将是对应于所提供的文本表达式的相应类型的对象。若遇到非法字符停止转换之前已经读取到了至少一个合法字符,则返回已转换的结果并置*endptr指向下一个未匹配位置;否则返回零并将errno设为ERANGE来指示溢出错误。 #### 示例代码 下面给出一段简单的例子展示如何利用 `strtol` 进行基本操作: ```c #include <stdio.h> #include <stdlib.h> int main(void){ const char *num_str = " -123abc"; char *p; long num; errno = 0; // Reset error indicator before calling function. num = strtol(num_str, &p, 10); if (*p != '\0') { printf("Conversion stopped at '%s'\n", p); } if (errno == ERANGE && (num == LONG_MAX || num == LONG_MIN)) { fprintf(stderr,"Error: number out of range\n"); } printf("Converted value is %ld.\n", num); return 0; } ``` 上述代码尝试把带有负号和非数字结尾的字符串 `-123abc` 转换成长整形,并通过检查 `endptr` 的内容确认哪些部分得到了正确解析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值